Is wrapping Get-WMIObject a bad idea?

I made a function to wrap Get-WMIObject so that I can use it in a pipleine and so that it is adding an extra attribute

function Get-Services (){ 
    [cmdletbinding()]
    param(
        [parameter(ValueFromPipelineByPropertyName,ValueFromPipeline)]
        [string]$Name
    )
    $services = Get-WMiObject -Class Win32_Service -ComputerName:$Name 
    $services  | Add-Member IsSystemAccount (($_.StartName -ne "LocalSystem") -and ($_.StartName -ne "Local System") -and ($_.StartName -notlike "NT AUTHORITY\*") -and ($_.StartName -notlike "NT Service\*")) -PassThru
}

I then use this:

Get-ADComputer -SearchBase "OU=Server Farm,DC=test,DC=org" -Filter * | Get-Services | Where {-not $_.IsSystemAccount}

Before I had a giant function that performed the search, and the WMI request and the filter. But as I added options that are simple in the pipeline, the function became huge. I had flags for if services started by system accounts should be excluded, how to search for machine. It became a mess. So I did the above, and now I'm worried there is some reason that Get-WmiObject doesn't work in a pipeline by default.

Am I just being paranoid?

Posted Apr 5, 2017, 7:20 PM UTC

33
markekraus•
function Get-Services { 
    [cmdletbinding()]
    param(
        [parameter(
            ValueFromPipelineByPropertyName=$true,
            ValueFromPipeline=$true)]
        [string[]]$Name
    )
    process{
        foreach($ComputerName in $Name){
            $WMIParams = @{
                 Class = 'Win32_Service'
                 ComputerName = $ComputerName
            }
            Get-WMiObject @WMIParams | ForEach-Object {
                $IsSystemAccount = (
                    ($_.StartName -ne "LocalSystem") -and 
                    ($_.StartName -ne "Local System") -and 
                    ($_.StartName -notlike "NT AUTHORITY\*") -and 
                    ($_.StartName -notlike "NT Service\*")
                )
                $Params = @{
                    MemberType = 'NoteProperty'
                    Name = 'IsSystemAccount'
                    Value = $IsSystemAccount 
                    PassThru = $true
                }
                $_  | Add-Member @Params
            } #End ForEach-Object
        } #End Foreach
    } #End Process
} #End Function

"localhost" |  Get-Services | Where { -not $_.IsSystemAccount}

if you are going to use the pipeline, you must use the Process{} block in the function. You also need to run your Add-member on every service returned so that needs to run through a foreach-object.

Another change was to make $Name a [string[]] and to wrap the entire process in a foreach. That is so it will support both a pipeline and something like this:

 Get-Services -Name 'server1','server2'
3
eric256OP•

Yea after posting i realized I'd only tried it with a single computer name. Thanks! So with the process command and the name as n array it works great. I managed to invert my system account check somewhere so I changed that.

Your's looks prettier...but I got mine to work too :)

function Get-Services (){ 
    [cmdletbinding()]
    param(
        [parameter(ValueFromPipelineByPropertyName,ValueFromPipeline)]
        [string[]]$Name
    )
    process {
        $services = Get-WMiObject -Class Win32_Service -ComputerName:$Name 
        $services  | ForEach-Object { $_ | Add-Member IsSystemAccount (-not (($_.StartName -ne "LocalSystem") -and ($_.StartName -ne "Local System") -and ($_.StartName -notlike "NT AUTHORITY\*") -and ($_.StartName -notlike "NT Service\*"))) -PassThru}
    }
}
1
Dogoodwork•

You should maybe be using Get-CimInstance

$services = Get-CimInstance -CimSession computername -ClassName "Win32_Service"

1