Safely Using WMI in PowerShell (Part 2)

by Nov 18, 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 both cmdlets return the same basic information for WMI classes, but the metadata properties added by both cmdlets differ considerably. Now let’s take a closer look at the basic information returned by them.

We refine the test script to also take into account the datatype, so we are not just looking at property names but also the type of data returned by these properties. For this, we are looking at the property “TypeNameOfValue”.

Since this is a string, type names are not always consistent. They can be displayed as “Bool” and “Boolean”, or “String” and “System.String”. To make results comparable, the code uses a calculated property in $typeName to omit the type namespace, and to apply manual adjustments in a switch statement. If you happen to find other type names that are displayed differently, simply extend the switch statement.

# 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 

# create a calculated property that returns only the basic type name
# and omits the namespace
$typeName = @{
    Name = 'Type'
    Expression = { 
        $type = $_.TypeNameOfValue.Split('.')[-1].ToLower() 
        switch ($type)
            'boolean' { 'bool' }
            default   { $type }

# ignore the metadata properties which we already know are different
$meta = '__CLASS','__DERIVATION','__DYNASTY','__GENUS','__NAMESPACE','__PATH','__PROPERTY_COUNT','__RELPATH','__SERVER','__SUPERCLASS','CimClass','CimInstanceProperties','CimSystemProperties','ClassPath','Container','Options','Properties','Qualifiers','Scope','Site','SystemProperties'

# return the properties and their data type. Add the origin so we later know 
# which cmdlet emitted them
$aDetail = $a.PSObject.Properties | 
    # exclude the metadata we already know is different
    Where-Object { $_.Name -notin $meta } |
    # add the origin command as new property "Origin"
    Select-Object -Property Name, $typeName, @{N='Origin'={'Get-WmiObject'}}
$bDetail = $b.PSObject.Properties | 
    # exclude the metadata we already know is different
    Where-Object { $_.Name -notin $meta } |
    # add the origin command as new property "Origin"
    Select-Object -Property Name, $typeName, @{N='Origin'={'Get-CimInstance'}}

# compare differences
Compare-Object -ReferenceObject $aDetail -DifferenceObject $bDetail -Property Name, Type -PassThru |
  Select-Object -Property Name, Origin, Type |
  Sort-Object -Property Name

Here is the result:

Name           Origin          Type                
----           ------          ----                
InstallDate    Get-CimInstance ciminstance#datetime
InstallDate    Get-WmiObject   string              
LastBootUpTime Get-CimInstance ciminstance#datetime
LastBootUpTime Get-WmiObject   string              
LocalDateTime  Get-CimInstance ciminstance#datetime
LocalDateTime  Get-WmiObject   string              
Path           Get-WmiObject   managementpath 
  • Get-CimInstance returns date and time as DateTime objects whereas Get-WmiObject returns them as string, using its internal WMI format for it
  • Get-WmiObject adds another metadata property called “Path”

Get-CimInstance makes dealing with dates and times much easier than Get-WmiObject:

PS> $a.InstallDate

PS> $b.InstallDate

Tuesday, September 3, 2019 12:42:41   

With the code above, you now have an easy-to-use tool to test the WMI classes your scripts use, and identify properties that may return different data types when you move from Get-WmiObject to Get-CimInstance.

Twitter This Tip! ReTweet this Tip!