Monthly Archive


PowerShell and CIM

File searches with WMI

I saw a question about file searches with WMI.

If you just know the file name it’s a very slow process. Painfully slow.

If you have an idea about the folder its much quicker.


function get-wmifile {
param (
[Parameter(Mandatory = $true)]



if ($path.IndexOf('\\') -le 0 ){
$path = $path.replace('\', '\\')

if ($path.IndexOf('*') -ge 0 ){
$path = $path.replace('*', '%')

Write-Verbose -Message "Path to search: $path"

$folders = Get-CimInstance -ClassName Win32_Directory -Filter "Name LIKE '$path'" 
foreach ($folder in $folders){

if ($file) {
Get-CimAssociatedInstance -InputObject $folder -ResultClassName CIM_DataFile |
where Name -Like "*$file" |
Select Name
else {
Get-CimAssociatedInstance -InputObject $folder -ResultClassName CIM_DataFile |
Select Name



Take the path and replace any single \ with \\. WMI expects c:\\test NOT c:\test.

Likewise replace the wildcard * with the WMI wildcard equivalent %

Get the folders that are like the path. Foreach folder use the WMI association between Win32_Directory and CIM_datafile to get the files. if you’ve specified a file then just return that otherwise all files in the folders matching path.


The WMI approach works and if you add in the computer name you can use remoting to get the answer from a remote machine.


As a comparison using Get-ChildItem would be

PS> Get-ChildItem -Path c:\test* -Recurse -Filter 'p1.txt' | select Fullname

which is much quicker.


Using WMI to do this is a fun exercise but the fastest approach is to use Get-ChildItem

user logon time

Saw an interesting question about user logon time. How can you tell the logged on user and when they logged on


$logon = Get-CimInstance -ClassName Win32_LogonSession |
sort StartTime -Descending |
select -First 1

$user = Get-CimAssociatedInstance -InputObject $logon -ResultClassName Win32_Account

$props = [ordered]@{
Name = $user.Fullname
UserId = $user.Name
Domain = $user.Domain
LocalAccount = $user.LocalAccount
LogonTime = $logon.StartTime

New-Object -TypeName PSobject -Property $props


Start by getting the last logon session using Win32_LogonSession. Use that to find the associated account using Get-CimAssociatedInstance and the Win32_Account class.


Pull together the output you want and create an object to display.


This deliberately only takes the latest logon and deals with that

WMI and CIM accelerators

In PowerShell an accelerator is a shortcut to a .NET type. The WMI accelerators have been around since PowerShell v1. The WMI accelerators were heavily used in v1 fill some of the gaps in cmdlet coverage. The CIM accelerators appeared in PowerShell v3 (I think – only discovered them recently!). This is how you use the WMI and CIM accelerators.


There are three WMI accelerators

wmiclass is shortcut for System.Management.ManagementClass
wmi is shortcut for System.Management.ManagementObject
wmisearcher is shortcut for System.Management.ManagementObjectSearcher


And four CIM accelerators are

ciminstance is shortcut for Microsoft.Management.Infrastructure.CimInstance
cimclass is shortcut for Microsoft.Management.Infrastructure.CimClass
cimtype is shortcut for Microsoft.Management.Infrastructure.CimType
cimconverter is shortcut for Microsoft.Management.Infrastructure.CimConverter


CimSession which is a shortcut for Microsoft.Management.Infrastructure.CimSession. Use this to set a parameter type.


Notice that there isn’t a direct correspondence between the WMI and CIM accelerators.

PowerShell v6 only has the CIM accelerators


The WMI accelerators are used like this:


This can be used for creating new instances of CIM classes

PS> $p = [wmiclass]'Win32_Process'
PS> $p.Create("notepad.exe")

This is easily replicated using the CIM cmdlets

PS> Invoke-CimMethod -ClassName Win32_Process -MethodName create -Arguments @{CommandLine='notepad.exe'}



The [wmi] accelerator is used to find an instance BUT you have to use the class key!

PS> [wmi]"root\cimv2:Win32_Process.Handle='7264'"

NOTE the handle has to be the one reported by CIM NOT the one reported by Get-Process!

Its much easier to use Get-CimInstance and filter on the name

Get-CimInstance -ClassName Win32_Process -Filter "Name='pwsh.exe'"



This is used to find CIM instances:

PS> $query = [wmisearcher]"SELECT * FROM Win32_Process WHERE Name='pwsh.exe'"
PS> $query.Get()

Its easier to use Get-CIMinstance these days

PS> Get-CimInstance -ClassName Win32_Process -Filter "Name='pwsh.exe'"


Looking at the CIM accelerators


This one doesn’t seem to be usable for anything but a type decorator on a parameter.



Using Get-CimClass

PS> Get-CimClass -ClassName Win32_process | fl

CimSuperClassName : CIM_Process
CimSuperClass : ROOT/cimv2:CIM_Process
CimClassProperties : {Caption, Description, InstallDate, Name...}
CimClassQualifiers : {Locale, UUID, CreateBy, DeleteBy...}
CimClassMethods : {Create, Terminate, GetOwner, GetOwnerSid...}
CimSystemProperties : Microsoft.Management.Infrastructure.CimSystemProperties
CimClassName : Win32_Process

The output is of type Microsoft.Management.Infrastructure.CimClass for which cimclass is an accelerator BUT there doesn’t seem to be a way to use the accelerator to access a class. I think this one is only usable as a type on a parameter for a function where you want to pass in a CIM class object.



Microsoft.Management.Infrastructure.CimType is an enum that contains the CIM (and WMI) datatypes:

PS> [cimtype]::Boolean
PS> [cimtype]::UInt32

The full set of CIM data types is

PS> [enum]::GetNames([cimtype])



Some of the CIM data types shown above don’t directly correspond to .NET types you’re used to from PowerShell. You can use [cimconvertor] which is shortcut for Microsoft.Management.Infrastructure.CimConverter to discover the corresponding .NET or CIM data type


PS> [cimconverter]::GetCimType([int32])
PS> [cimconverter]::GetCimType([double])


PS> [cimconverter]::GetDotNetType([cimtype]::SInt32)

IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Int32 System.ValueType

PS> [cimconverter]::GetDotNetType([cimtype]::Instance)

IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True CimInstance System.Object


For the most part I think the WMI and CIM accelerators are best ignored. Use the CIM cmdlets instead. The cimtype and cimconverter accelerators are useful when developing code to check on types between CIM and .NET

CIM references and associations

Way back in 2011, when I were just a young lad, I wrote about WMI or CIM references and associations -

ASSOCIATORS show the end point of the link between CIM classes and REFERENCES shows the linking class.

I used the Win32_NetworkAdapter class in the example because it has a known association with Win32_NetworkAdapterConfiguration.


One thing I appear to have not made clear is that you have to use the key to the CIM class in the query for the reference or association. The key for a Win32_networkadapter class is DeviceID.

This became clear when a reader pointed out that this won’t work:

PS> Get-WmiObject -Query "REFERENCES OF { = 'fax'}"
Get-WmiObject : Invalid object path
At line:1 char:1
+ Get-WmiObject -Query "REFERENCES OF { = 'fax'}"
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [Get-WmiObject], ManagementException
+ FullyQualifiedErrorId : GetWMIManagementException,Microsoft.PowerShell.Commands.GetWmiObjectCommand


But that

Get-WmiObject -Query "REFERENCES OF {Win32_Printer.DeviceID='Fax'}"

works. Again DeviceId is the key to the class.


Usually its better to go straight to the association and bypass the reference.

A better approach than writing WQL queries is to use the CIM cmdlets

$p = Get-CimInstance -ClassName Win32_Printer -Filter "Name='Fax'"

Get-CimAssociatedInstance -InputObject $p

will show you the data from the associated classes.


If you just want to see the associated class names

PS> Get-CimAssociatedInstance -InputObject $p | select CIMclass



And if you just want the data for a particular associated class

PS> Get-CimAssociatedInstance -InputObject $p -ResultClassName Win32_PrinterDriver

Caption :
Description :
InstallDate :
Name : Microsoft Shared Fax Driver,3,Windows x64
Status :
CreationClassName : Win32_PrinterDriver
Started :
StartMode :
SystemCreationClassName : Win32_ComputerSystem
SystemName :
ConfigFile : C:\WINDOWS\system32\spool\DRIVERS\x64\3\FXSUI.DLL
DataFile : C:\WINDOWS\system32\spool\DRIVERS\x64\3\FXSUI.DLL
DefaultDataType :
DependentFiles : {C:\WINDOWS\system32\spool\DRIVERS\x64\3\FXSWZRD.DLL,
DriverPath : C:\WINDOWS\system32\spool\DRIVERS\x64\3\FXSDRV.DLL
FilePath :
HelpFile :
InfName :
MonitorName :
OEMUrl :
SupportedPlatform : Windows x64
Version : 3
PSComputerName :

CIM_ or Win32_

If you dig into the classes available on a Windows machine you’ll see a mixture of prefixes – namely CIM_ and Win32_ used for the classes. So which should you use CIM_ or Win32_


Lets start by seeing whats available:

PS> Get-CimClass -ClassName *Volume*

    NameSpace: ROOT/CIMV2



The CIM_ classes follow the standard definition from the DMTF -

The Win32_ classes are Microsoft’s versions of the equivalent CIM_ class often with additional properties and methods.


I’ve always recommended using the Win32_ classes because they are “optimised” for Windows. The one exception that I’ve found is CIM_Datafile that doesn’t have a Win32_ equivalent.


A question on the forum asked why this was failing

PS> Get-CimInstance -ClassName CIM_StorageVolume -Filter "DriveLetter='C:'"
 Get-CimInstance : Invalid query
 At line:1 char:1
 + Get-CimInstance -ClassName CIM_StorageVolume -Filter "DriveLetter='C: ...
 + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     + CategoryInfo          : InvalidArgument: (:) [Get-CimInstance], CimException
     + FullyQualifiedErrorId : HRESULT 0x80041017,Microsoft.Management.Infrastructure.CimCmdlets.GetCimInstanceCommand


When this worked

PS> Get-CimInstance -ClassName CIM_StorageVolume | select Name, DriveLetter, Capacity

Name                                              DriveLetter     Capacity
 ----                                              -----------     --------
 \\?\Volume{c1c4c5bb-0000-0000-0000-100000000000}\                366997504
 C:\                                               C:          511210610688
 \\?\Volume{c1c4c5bb-0000-0000-0000-801c77000000}\                529526784
 D:\                                               D:


The reason is that you’re not getting back CIM_StorageVolume:

PS> Get-CimInstance -ClassName CIM_StorageVolume | Get-Member

    TypeName: Microsoft.Management.Infrastructure.CimInstance#root/cimv2/Win32_Volume


You’re getting Win32_Volume which DOES have a DriveLetter property. Get-WmiObject works in the same way. Looking at the properties:

PS> (Get-CimClass -ClassName CIM_StorageVolume ).CimClassProperties | Where Name -like "D*"

Name               : Description
Value              :
CimType            : String
Flags              : Property, ReadOnly, NullValue
Qualifiers         : {read}
ReferenceClassName :

Name               : DeviceID
Value              :
CimType            : String
Flags              : Property, ReadOnly, NullValue
Qualifiers         : {CIM_Key, read}
ReferenceClassName :


No DriveLetter. Now try Win32_Volume

PS> (Get-CimClass -ClassName Win32_Volume ).CimClassProperties | Where Name -like "D*"

Name               : Description
Value              :
CimType            : String
Flags              : Property, ReadOnly, NullValue
Qualifiers         : {read}
ReferenceClassName :

Name               : DeviceID
Value              :
CimType            : String
Flags              : Property, Key, ReadOnly, NullValue
Qualifiers         : {CIM_Key, read, key, MappingStrings...}
ReferenceClassName :

Name               : DirtyBitSet
Value              :
CimType            : Boolean
Flags              : Property, ReadOnly, NullValue
Qualifiers         : {read}
ReferenceClassName :

Name               : DriveLetter
Value              :
CimType            : String
Flags              : Property, NullValue
Qualifiers         : {read, write}
ReferenceClassName :

Name               : DriveType
Value              :
CimType            : UInt32
Flags              : Property, ReadOnly, NullValue
Qualifiers         : {MappingStrings, read}
ReferenceClassName :


Win32_Volume has 2 extra properties that start with the letter D, including DriveLetter. Win32_Volume is derived from, and builds on, CIM_StorageVolume

PS> Get-WmiObject -Class Win32_Volume | select -First 1 | select -ExpandProperty __Derivation


If you want to get volume information on a Windows box use Win32_Volume or better still on Windows 8 and later use Get-Volume.

The only possible reason for using CIM_StorageVolume is that you’re learning more about CIM or you’re trying some cross-platform task. As I can’t think of a platform other than Windows that has implemented CIM_StorageVolume not sure how far you’ll get on the latter. If you do try CIM_StorageVolume you can filter using the Name property:

Get-CimInstance -ClassName CIM_StorageVolume -Filter "Name='C:\\'"


Note that you have to use C:\\ not C:\ because \ is an escape character in WQL.

Alternatively, use where-object to filter:

Get-CimInstance -ClassName CIM_StorageVolume | where DriveLetter -eq 'C:'


If you’re working remotely the first option is best as it reduces the amount of data being returned across the network.


If in doubt use the Win32_ class rather than the CIM class.


Windows Updates CIM classes

When Windows 10 and Server 2016 were released they contained a new CIM namespace - ROOT/Microsoft/Windows/WindowsUpdate


This contained a CIM class MSFT_WUOperationsSession that had 2 very useful methods – ScanForUpdates and  ApplyApplicableUpdates.


These methods enabled you to find and install updates from the Windows Update site or a WSUS server if you’d configured the machine to use WSUS.


Best of all the CIM class could be accessed and used remotely which was a huge step forward over the old COM object that couldn’t be used remotely.


Things changed with Windows 10 Fall Creators Update (1709) and Windows Server 1709. MSFT_WUOperationsSession still exists but the methods you need to scan for updates and apply updates are now on the MSFT_WUOperations class. The great thing is that they’re static methods so using them is easier. The bad – no really, really bad – thing is that this class CAN’T BE ACCESSED REMOTELY through a Windows remoting session,  a CIM session or a SSH remoting session.


This takes us back to the bad old days of using COM objects. There doesn’t seem to be any reason or explanation for this decision.


I have managed to use the class remotely through a PowerShell direct remoting session though which means that I can force updates in my lab. Its not really an answer for production though

Monitor resolution

A question on the forum about getting monitor resolution led to this code

Get-CimInstance -Namespace root\wmi -ClassName WmiMonitorId | 
foreach { 
  $filter = ("InstanceName = '$($psitem.InstanceName)'").Replace("`\", "`\`\") 
  $maxres = Get-CimInstance -Namespace root\wmi -ClassName WmiMonitorListedSupportedSourceModes -Filter $filter | 
  Select-Object -ExpandProperty MonitorSourceModes | 
  Sort-Object -Property {$_.HorizontalActivePixels * $_.VerticalActivePixels} -Descending | 
  Select-Object -First 1 
  if ($psitem.UserFriendlyNameLength -gt 0) { 
    $name = ($psitem.UserFriendlyName -notmatch '^0$' | foreach {[char]$_}) -join "" 
  else { 
    $name = 'Unknown' 

  $props = [ordered]@{ 
     Manufacturer = ($psitem.ManufacturerName -notmatch '^0$' | foreach {[char]$_}) -join "" 
     Name = $name 
     Serial = ($_.SerialNumberID -notmatch '^0$' | foreach {[char]$_}) -join "" 
     MaximumResolution = "$($maxres.HorizontalActivePixels) x $($maxres.VerticalActivePixels)" 
  New-Object -TypeName PSObject -Property $props 


NOTE – we’re working in the root\wmi namespace not PowerShell’s default of root\cimv2

Use WmiMonitorId to get the attached monitors and for each of them create a filter using the instance name. You have to replace \ with \\ when dealing with WMI.

Use WmiMonitorListedSupportedSourceModes and expand MonitorSourceModes. use sort-object to find the maximum resolution (multiply horizontal by vertical)

Create an output object. I had to deal with the name of the monitor separately because one of my monitors didn’t have a user friendly name.

Results look like this

Manufacturer Name    Serial       MaximumResolution 
 ------------ ----    ------       ----------------- 
 GSM          22EA63  304NDJX51788 1920 x 1080      
 LEN          Unknown 0            1600 x 900

Use CIM cmdlets not WMI cmdlets

WMI and CIM seem to cause a LOT of confusion. Its really simple. CIM is an industry standard from WMI was Microsoft’s implementation of CIM way back in Windows NT days. The complication is that Microsoft had a set of WMI cmdlets in PowerShell v2. In PowerShell v3 they introduced a set of CIM cmdlets that work differently. The bottom line is you should use CIM cmdlets not WMI cmdlets.

Here’s three reasons  why you should use CIM cmdlets not WMI cmdlets.


Reason 1: the WMI cmdlets are deprecated and won’t have much if any further work done on them. The WMI help files tell you to use the CIM cmdlets! PowerShell v6 doesn’t have the WMI cmdlets – just the CIM cmdlets.


Reason 2: the CIM cmdlets use WS-MAN for remote access. The WMI cmdlets use DCOM which is blocked by default on Windows firewall. Using the CIM cmdlets makes your remoting easier.


Reason 3: The CIM cmdlets do some extra work for you. For instance finding the time a machine was started:

PS> Get-WmiObject -Class Win32_OperatingSystem | select LastBootUpTime



Read it left to right = Year = 2017; Month = 10; day = 01; hour = 11; minute = 35; second = 46.966894 with a +60 minute offset for daylight saving time.

That date format is painful to deal with so you convert it. the nice PowerShell Team added a conversion method for you

PS> Get-WmiObject -Class Win32_OperatingSystem | select @{N='LastBootUpTime'; E={$_.ConvertToDateTime($_.LastBootUpTime) }}

01/10/2017 11:35:46


It works great but means you have to do more work.

By contrast the CIM cmdlets do the work for you

PS> Get-CimInstance -ClassName Win32_OperatingSystem | select LastBootUpTime

01/10/2017 11:35:46


On a slightly off topic point anyone thinking of attending the PowerShell Summit next April will be aware that we have an Iron Scripter event planned for the last day. IF access to WMI classes is required anyone using the WMI cmdlets instead of the CIM cmdlets will be marked down. You have been warned Smile

Examples of replacing WMI cmdlet with CIM cmdlet

Following my last post I was asked about these Examples of replacing WMI cmdlet with CIM cmdlet.

Example 1

gwmi win32_operatingsystem -computername $Computer -credential $creds,

$cs = New-CimSession -Credential $creds -ComputerName $computer
Get-CimInstance -ClassName Win32_operatingsystem -CimSession $cs

Example 2

get-wmiobject -query “SELECT * FROM Meta_Class WHERE __Class = ‘Win32_Process'” -namespace “root\cimv2” -computername $computer -credential $creds

$cs = New-CimSession -Credential $creds -ComputerName $computer
Get-CimInstance -Query “SELECT * FROM Meta_Class WHERE __Class = ‘Win32_Process'” -CimSession $cs

Example 3

Get-WmiObject -Namespace $namespace -Class SMS_fullcollectionmembership -ComputerName $SCCMServer -filter “Name = ‘$computer'” -credential $creds

$cs = New-CimSession -Credential $creds -ComputerName $SCCMServer
Get-CimInstance -Namespace $namespace -ClassName SMS_fullcollectionmembership ” -filter “Name = ‘$computer'” -CimSession $cs


I still see a lot of people using the WMI cmdlets – Get-WmiObject etc. You really should be using CIM nit WMI. In other words use Get-CimInstance rather than get-WmiObject etc etc.

Why do I say that?

Two main reasons.

Firstly, the WMI cmdlets are effectively deprecated. Any further development effort will be for the CIM cmdlets.

Secondly, and to my mind more important, is that the CIM cmdlets use WS-MAN for access to remote machines. If you have PowerShell remoting enabled you have access to the machine via a CIM session – either ephemeral using the cmdlet name or persistent using a CIM session.

The WMI cmdlets use DCOM for remoting which is blocked by default on the Windows firewall and most other firewalls and routers giving the RPC server is unavailable error.

The only time there is justification for using the WMI cmdlets is if you’re on a machine that has Powershell v2 installed and if that’s the case why haven’t you upgraded? If you can’t does that mean you’re running an application (usually Exchange or System Center) that doesn’t allow you to upgrade PowerShell.

Maybe  its time to perform that upgrade.

As another thought PowerShell v6 includes the CIM cmdlets but not the WMI cmdlets!