Safely Using WMI in PowerShell (Part 1)

by Nov 14, 2019

WMI (Windows Management Instrumentation) is part of any Windows operating system and a common and widely used way of getting information about a computer system. PowerShell originally introduced the Get-WmiObject cmdlet. In PowerShell 3, the more modern Get-CimInstance was added.

Since Windows PowerShell always kept the old Get-WmiObject cmdlet for backwards compatibility, many scripters kept using it and ignored Get-CimInstance. Now is the time to break this old habit because PowerShell Core (PowerShell 6, 7) stopped support for Get-WmiObject. To make sure your scripts are compatible with future versions of PowerShell, you should start using Get-CimInstance instead of Get-WmiObject.

This seems trivial at first, and often it is. For simple data queries, you might get away with replacing Get-WmiObject by Get-CimInstance:

PS> Get-WmiObject -Class Win32_BIOS

SMBIOSBIOSVersion : 1.0.9
Manufacturer      : Dell Inc.
Name              : 1.0.9
SerialNumber      : 4ZKM0Z2
Version           : DELL   - 20170001

PS> Get-CIMInstance -Class Win32_BIOS

SMBIOSBIOSVersion : 1.0.9
Manufacturer      : Dell Inc.
Name              : 1.0.9
SerialNumber      : 4ZKM0Z2
Version           : DELL   - 20170001

Note that the -Class parameter actually was renamed to -ClassName in Get-CimInstance, but since PowerShell allows shortened parameter names as long as they are unique, you do not necessarily have to adjust the parameter name.

The truth is, however, that Get-WmiObject and Get-CimInstance are not 100% compatible, and there are important differences you should know, especially when you plan to make changes to existing scripts. In this mini-series, we’ll look at the most important practical differences.

Let’s look at the information returned by both commands. Here is an approach that looks at the returned properties:

# we are comparing this WMI class (feel free to adjust)
$wmiClass = 'Win32_OperatingSystem'

# get information about the WMI class Win32_OperatingSystem with both cmdlets
$a = Get-WmiObject -Class $wmiClass | Select-Object -First 1
$b = Get-CimInstance -ClassName $wmiClass | Select-Object -First 1 

# dump the property names and add the property "Origin" so you know
# which property was returned by which command:
$aDetail = $a.PSObject.Properties | Select-Object -Property Name, @{N='Origin'={'Get-WmiObject'}}
$bDetail = $b.PSObject.Properties | Select-Object -Property Name, @{N='Origin'={'Get-CimInstance'}}

# compare the results:
Compare-Object -ReferenceObject $aDetail -DifferenceObject $bDetail -Property Name -PassThru |
  Sort-Object -Property Origin

And here is the result:

Name                  Origin          SideIndicator
----                  ------          -------------
CimClass              Get-CimInstance =>           
CimInstanceProperties Get-CimInstance =>           
CimSystemProperties   Get-CimInstance =>           
Qualifiers            Get-WmiObject   <=           
SystemProperties      Get-WmiObject   <=           
Properties            Get-WmiObject   <=           
ClassPath             Get-WmiObject   <=           
Options               Get-WmiObject   <=           
Scope                 Get-WmiObject   <=           
__PATH                Get-WmiObject   <=           
__NAMESPACE           Get-WmiObject   <=           
__SERVER              Get-WmiObject   <=           
__DERIVATION          Get-WmiObject   <=           
__PROPERTY_COUNT      Get-WmiObject   <=           
__RELPATH             Get-WmiObject   <=           
__DYNASTY             Get-WmiObject   <=           
__SUPERCLASS          Get-WmiObject   <=           
__CLASS               Get-WmiObject   <=           
__GENUS               Get-WmiObject   <=           
Site                  Get-WmiObject   <=           
Container             Get-WmiObject   <=   

The result shows that there are considerable differences when it comes to the metadata. While Get-WmiObject always returns the name of the computer where the query was conducted in its property “__Server” (two underscores), Get-CimInstance buries this information in CimSystemProperties:

PS> $b.CimSystemProperties

Namespace  ServerName      ClassName     Path
---------  ----------      ---------     ----
root/cimv2 DESKTOP-8DVNI43 Win32_Process     
PS> $b.CimSystemProperties.ServerName

The good news is, though, that the specific properties for a class do not differ, so both commands return the same basic information about the operating system, the BIOS, or whatever else you queried. This line returns the properties that are the same:

Compare-Object -ReferenceObject $aDetail -DifferenceObject $bDetail -Property Name -IncludeEqual -ExcludeDifferent -PassThru |
  Sort-Object -Property Origin | Select-Object -Property Name, SideIndicator
Name                       SideIndicator
----                       -------------
ProcessName                ==           
ParentProcessId            ==           
PeakPageFileUsage          ==           
PeakVirtualSize            ==           
PeakWorkingSetSize         ==           
Priority                   ==           
PrivatePageCount           ==           
ProcessId                  ==           
QuotaNonPagedPoolUsage     ==           
QuotaPagedPoolUsage        ==           
QuotaPeakNonPagedPoolUsage ==           
PageFileUsage              ==           
QuotaPeakPagedPoolUsage    ==           
PSComputerName             ==           
ReadTransferCount          ==           
SessionId                  ==           
Status                     ==           
TerminationDate            ==           
ThreadCount                ==           
UserModeTime               ==           
VirtualSize                ==           
WindowsVersion             ==           
WorkingSetSize             ==           
ReadOperationCount         ==           
WriteOperationCount        ==           
PageFaults                 ==           
OtherOperationCount        ==           
Handles                    ==           
VM                         ==           
WS                         ==           
Path                       ==           
Caption                    ==           
CreationClassName          ==           
CreationDate               ==           
CSCreationClassName        ==           
CSName                     ==           
Description                ==           
OtherTransferCount         ==           
CommandLine                ==           
ExecutionState             ==           
Handle                     ==           
HandleCount                ==           
InstallDate                ==           
KernelModeTime             ==           
MaximumWorkingSetSize      ==           
MinimumWorkingSetSize      ==           
Name                       ==           
OSCreationClassName        ==           
OSName                     ==           
ExecutablePath             ==           
WriteTransferCount         == 

Twitter This Tip! ReTweet this Tip!