Should I use CIM or WMI with Windows PowerShell?

Summary: Richard Siddaway explains the differences between the CIM cmdlets and the WMI cmdlets, and details use cases.

Hey, Scripting Guy! Question Hey, Scripting Guy! Should I use the WMI cmdlets or the newer CIM cmdlets?


Hey, Scripting Guy! Answer Hello NR,

Honorary Scripting Guy, Richard Siddaway, here today filling in for my good friend, The Scripting Guy. The simple answer is that you can use either Windows Management Instrumentation (WMI) or Common Information Model (CIM) cmdlets, but there are (to my mind) some significant advantages to using the newer CIM cmdlets.

Before I show you why I think you should use the CIM cmdlets, let’s do a little recap.

First, you have to remember that CIM = WMI = CIM. CIM is an open standard from the Distributed Management Task Force (DMTF), with the latest version introduced in January 2016. CIM provides a common definition of management information for systems, networks, applications, and services, and it allows for vendor extensions. WMI is the Microsoft implementation of CIM for the Windows platform.

Let’s start by runnig this:

 Get-CimClass *Process | select CimClassName










Notice the CIM_Process and Win32_Process classes? CIM_Process is the original standard class. Win32_Process is the Microsoft-specific class, which may (or may not) be modified from the original. You’ll see many pairings like this in the default CIM namespace – root\cimv2.

WMI was first introduced in the days of NT 4.0, and usual way to access it by IT pros was to use VBScript. Moving swiftly on…

These WMI cmdlets were introduced in Windows PowerShell 1.0 and 2.0:

  • Get-WmiObject
  • Invoke-WmiMethod
  • Register-WmiEvent
  • Remove-WmiObject
  • Set-WmiInstance

Get-WmiObject is one of the original PowerShell cmdlets. (As a quick quiz, how many of the 137 original cmdlets can you name?). It was enhanced in PowerShell 2.0 when the other WMI cmdlets were introduced. In PowerShell 1.0, Get-WmiObject was the only cmdlet with the option to access another system.

The big drawback to the WMI cmdlets is that they use DCOM to access remote machines. DCOM isn’t firewall friendly, can be blocked by networking equipment, and gives some arcane errors when things go wrong.

The CIM cmdlets appeared in PowerShell 3.0 as part of the new API for working with CIM classes, which is more standards based. The CIM cmdlets were overshadowed by PowerShell workflows, but they are (to my mind) the most important thing to come out of that release.

The other major CIM-related advance was the introduction of CDXML, which enables a CIM class to be wrapped in some simple XML and published as a PowerShell module. This is how over 60% of the cmdlets in Windows 8 and later are produced.

Note   I explained how to use CDXML in my Hey, Scripting Guy! Blog series about creating Registry cmdlets.

There are CIM cmdlets that are directly equivalent to the WMI cmdlets:

  • Get-CimInstance
  • Invoke-CimMethod
  • Register-CimIndicationEvent
  • Remove-CimInstance
  • Set-CimInstance

You also get extra cmdlets for working with CIM classes:

  • Get-CimAssociatedInstance
  • Get-CimClass
  • New-CimInstance

The big difference between the WMI cmdlets and the CIM cmdlets is that the CIM cmdlets use WSMAN (WinRM) to connect to remote machines. In the same way that you can create PowerShell remoting sessions, you can create and manage CIM sessions by using these cmdlets:

  • Get-CimSession
  • New-CimSession
  • New-CimSessionOption
  • Remove-CimSession

If you look in the PowerShell Help files, you’ll see messages like this:

“Starting in Windows PowerShell 3.0, this cmdlet has been superseded by Get-CimInstance.”

I take this to mean that the CIM cmdlets are the supported option and that in time the WMI cmdlets could be removed.

Time to look at the individual cmdlets…

Get-WmiObject does a great job (if you’ve ever had to work with WMI through VBScript you’ll appreciate how great), but it has a few awkward edges. A particular pain point is working with dates, for example:

Get-WmiObject -Class Win32_OperatingSystem | select LastBootUpTime




The objects produced by Get-WmiObject have a couple of methods added by the PowerShell team to make life easier:

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



31/01/2016 09:44:54

But the CIM cmdlets do it all for you:

Get-CimInstance -ClassName Win32_OperatingSystem | select LastBootUpTime



31/01/2016 09:44:54

Invoke-WmiMethod has a quirk in that if you supply the method parameters in the order specified by the WMI documentation, you will sometimes get an error message. The way to check the correct order of the parameters is to use Get-CimClass:

 PS> $class = Get-CimClass -ClassName Win32_Process

PS> $class.CimClassMethods[“Create”].Parameters

Name      CimType Qualifiers

—-      ——- ———-

CommandLine     String {ID, In, MappingStrings}

CurrentDirectory   String {ID, In, MappingStrings}

ProcessStartupInformation Instance {EmbeddedInstance, ID, In, MappingStrings}

ProcessId     UInt32 {ID, MappingStrings, Out}

Invoke-CimMethod requires a hash table of key value pairs where Invoke-WmiMethod only takes the parameter values. Invoke-CimMethod requires a bit more typing but you don’t have worry about the parameter order:

Invoke-CimMethod -ClassName Win32_Process -MethodName Create -Arguments @{CommandLine=’notepad.exe’; CurrentDirectory=’C:\test’}

ProcessId ReturnValue PSComputerName

——— ———– ————–

  8144   0

You get the ProcessId of the new process and the return value. Remember that a return value of 0 is always good when using CIM. If it is any other number, something has gone wrong somewhere.

This next trick isn’t possible with the WMI cmdlets. A frequent task is to view the resources used by a process—in this case, the Kernel mode and user mode times:

Get-CimInstance -ClassName Win32_Process -Filter “Name=’PowerShell.exe'” | select *time

KernelModeTime UserModeTime

————– ————

  28906250  39062500

You can keep running the code, or try this:

$x = Get-CimInstance -ClassName Win32_Process -Filter “Name=’PowerShell.exe'”

$x | select *time

KernelModeTime UserModeTime

————– ————

  32187500  42187500

$x | Get-CimInstance | select *time

KernelModeTime UserModeTime

————– ————

  33125000  43281250

$x | Get-CimInstance | select *time

KernelModeTime UserModeTime

————– ————

  33281250  43281250

You can pass the object back through Get-CimInstance and the values will be refreshed. This could be a good place to use the -Properties parameter on Get-CimInstance to restrict the amount of data, but you need to give the property names because wildcard characters aren’t allowed.

I mentioned earlier that the CIM cmdlets use WSMAN to access remote computers. As a consequence, they return inert objects. The WMI class methods aren’t available on the object. Compare the output of Get-Member:

PS> Get-WmiObject -Class Win32_Process | Get-Member -MemberType Method

 TypeName: System.Management.ManagementObject#root\cimv2\Win32_Process

Name     MemberType Definition

—-     ———- ———-

AttachDebugger   Method  System.Management.ManagementBaseObject AttachDebugger()

GetAvailableVirtualSize Method  System.Management.ManagementBaseObject GetAvailableVirtualSize()

GetOwner    Method  System.Management.ManagementBaseObject GetOwner()

GetOwnerSid    Method  System.Management.ManagementBaseObject GetOwnerSid()

SetPriority    Method  System.Management.ManagementBaseObject SetPriority(System.Int3…

Terminate    Method  System.Management.ManagementBaseObject Terminate(System.UInt32…

PS> Get-CimInstance -ClassName Win32_Process | Get-Member -MemberType Method

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

Name      MemberType Definition

—-      ———- ———-

Clone      Method  System.Object ICloneable.Clone()

Dispose     Method  void Dispose(), void IDisposable.Dispose()

Equals     Method  bool Equals(System.Object obj)

GetCimSessionComputerName Method  string GetCimSessionComputerName()

GetCimSessionInstanceId Method  guid GetCimSessionInstanceId()

GetHashCode    Method  int GetHashCode()

GetObjectData    Method  void GetObjectData(System.Runtime.Serialization.Serializatio…

GetType     Method  type GetType()

ToString     Method  string ToString()

Notice that the class methods, such as terminate, aren’t present. You can still access them, but you need to modify your technique. Instead of using this line:

$x = Get-WmiObject -Class Win32_Process -Filter “Name=’calculator.exe'”


You can use this:

Get-CimInstance -Class Win32_Process -Filter “Name=’calculator.exe'” | Invoke-CimMethod -MethodName Terminate

Without any doubt, the best feature of the CIM cmdlets is CIM sessions. If you think back to PowerShell remoting for a second, you can send individual commands to a remote machine by using Invoke-Command, but you have to create and tear down the connection each time, which can add appreciable overhead. If you create a PowerShell remoting session, you can keep the connection open and send multiple commands to the same machines.

CIM sessions work in exactly the same way. You create a session and use it as many times as you need it before pulling it down.

$cs = New-CimSession -ComputerName RSSURFACEPRO2


Id   : 1

Name   : CimSession1

InstanceId : b4099322-f548-4e3c-a9c2-65da311627df

ComputerName : RSSURFACEPRO2

Protocol  : WSMAN

If you have machines running Windows PowerShell 2.0, you can’t create a WSMAN-based CIM session because the version of the WSMAN stack was changed in Windows PowerShell 3.0, and you can’t use the PowerShell 2.0 version. In this case, you can drop back to DCOM:

$opt = New-CimSessionOption -Protocol DCOM

$csd = New-CimSession -ComputerName RSSURFACEPRO2 -SessionOption $opt


Id   : 2

Name   : CimSession2

InstanceId : 4aa11e35-33c8-462a-9788-47f5520f7038

ComputerName : RSSURFACEPRO2

Protocol  : DCOM

Note   If you use a DCOM-based CIM session, you automatically get packet privacy DCOM authentication. This means that you can access classes that need that level of encryption, such as the IIS classes or the cluster classes.

Using a CIM session couldn’t be simpler:

 Get-CimInstance -ClassName Win32_Bios -CimSession $cs

SMBIOSBIOSVersion : 2.05.0250

Manufacturer  : American Megatrends Inc.

Name    : 2.05.0250

SerialNumber  : 036685734653

Version   : OEMA – 1072009


Also take a look at the CDXML-based cmdlets that were introduced in Windows 8. They all have a CIMSession parameter.

Get-Command Get-NetAdapter -Syntax

Get-NetAdapter [[-Name] <string[]>] [-IncludeHidden] [-Physical] [-CimSession <CimSession[]>] [-ThrottleLimit <int>] [-AsJob] [<CommonParameters>]

Get-NetAdapter -InterfaceDescription <string[]> [-IncludeHidden] [-Physical] [-CimSession <CimSession[]>] [-ThrottleLimit <int>] [-AsJob] [<CommonParameters>]

Get-NetAdapter -InterfaceIndex <uint32[]> [-IncludeHidden] [-Physical] [-CimSession <CimSession[]>] [-ThrottleLimit <int>] [-AsJob] [<CommonParameters>]

The CDXML process automatically generates the CIMSession parameter so you don’t have to do any work to get that functionality. You can also create CIM sessions to work against systems running OMI (the open source CIM server to which Microsoft contributes), meaning you can manage Linux machines and network switches through the CIM cmdlets.

And that’s why I prefer the CIM cmdlets and would recommend that you use them instead of the WMI cmdlets.

Bye for now.


Richard Siddaway is based out of the UK and spends his time writing about PowerShell and automating anything and everything. A multi-year year PowerShell MVP, Richard is a prolific blogger, mainly about PowerShell (see Richard Siddaway’s Blog: A PowerShell MVP’s site), and he is a frequent speaker at user groups and PowerShell conferences. He has written a number of PowerShell books: PowerShell in Practice; PowerShell and WMI, PowerShell in Depth (co-author); PowerShell Dive (co-editor), and Learn Active Directory Management in a Month of Lunches, which features lots of PowerShell. He is currently working on the third edition of PowerShell in Action with Bruce Payette of the PowerShell team. All of the books are available from Richard has been a director of since the inception of that organization.

Thanks, Richard!

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy

Comments (0)

Skip to main content