Safely Using WMI in PowerShell (Part 4)

by Nov 22, 2019

In this mini-series, we are looking at the differences between Get-WmiObject and Get-CimInstance. Future PowerShell versions no longer support Get-WmiObject, so it is time to switch to Get-CimInstance if you haven’t already.

In the previous part you learned that there are considerable differences when you query information across a network, and that Get-CimInstance can use fully configurable and reusable session objects that help make network access faster and less resource intense.

Since Get-CimInstance works like a web service and unlike Get-WmiObject is not based on DCOM, this has important implications on the returned data. Get-CimInstance always goes through serialization, so you always and inevitably receive copies, never original objects. This is why Get-CimInstance never returns methods. You always just get properties.

Here is a practical example: the WMI class Win32_Process has a method called GetOwner() which returns the owner of a process. If you wanted to find out who is logged on to your machine, you could ask for explorer.exe processes and list their owners:

# find all explorer.exe instances
Get-WmiObject -Class Win32_Process -Filter 'Name="explorer.exe"' |
 ForEach-Object {
    # call the WMI method GetOwner()
    $owner = $_.GetOwner()
    if ($owner.ReturnValue -eq 0)
    {
        # return either the process owner...
        '{0}\{1}' -f $owner.Domain, $owner.User
    }
    else
    {
        # ...or the error code
        'N/A (Error Code {0})' -f $owner.ReturnValue
    }
 } | 
 # remove duplicates
 Sort-Object -Unique 

If you wanted to transition this code to Get-CimInstance, you cannot directly access the method GetOwner() because Get-CimInstance only returns a set of properties. Instead, you need to call the method via Invoke-CimMethod:

# find all explorer.exe instances
Get-CimInstance -ClassName Win32_Process -Filter 'Name="explorer.exe"' |
 ForEach-Object {
    # call the WMI method GetOwner()
    $owner = $_ | Invoke-CimMethod -MethodName GetOwner 
    if ($owner.ReturnValue -eq 0)
    {
        # return either the process owner...
        '{0}\{1}' -f $owner.Domain, $owner.User
    }
    else
    {
        # ...or the error code
        'N/A (Error Code {0})' -f $owner.ReturnValue
    }
 } | 
 # remove duplicates
 Sort-Object -Unique

Invoke-CimMethod either expects CimInstances to work with, or you can combine it with the original query, which simplifies the code further:

# find all explorer.exe instances
Invoke-CimMethod -Query 'Select * From Win32_Process Where Name="explorer.exe"' -MethodName GetOwner |
 ForEach-Object {
    if ($_.ReturnValue -eq 0)
    {
        # return either the process owner...
        '{0}\{1}' -f $_.Domain, $_.User
    }
    else
    {
        # ...or the error code
        'N/A (Error Code {0})' -f $_.ReturnValue
    }
 } | 
 # remove duplicates
 Sort-Object -Unique

Invoke-CimMethod can also call static WMI methods which are methods that reside in the WMI class itself rather than a specific instance. If you wanted to create a new process (launch a new program) locally or remotely, you can use a line like this:

 
PS> Invoke-CimMethod -ClassName Win32_Process -MethodName "Create" -Arguments @{ CommandLine = 'notepad.exe'; CurrentDirectory = "C:\windows\system32" }

ProcessId ReturnValue PSComputerName
--------- ----------- --------------
     3308           0  
 

Important: if you launch a program on a remote computer, it will run in your own hidden logon session and not be visible on the screen.


Twitter This Tip! ReTweet this Tip!