Summary: Richard Siddaway explains the differences between the CIM cmdlets and the WMI cmdlets, and details use cases.
Hey, Scripting Guy! Should I use the WMI cmdlets or the newer CIM cmdlets?
—NR
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
CimClassName
————
CIM_Process
Win32_Process
Win32_NamedJobObjectProcess
CIM_OSProcess
Win32_SessionProcess
Win32_PerfFormattedData_PerfProc_Process
Win32_PerfRawData_PerfProc_Process
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
LastBootUpTime
————–
20160131094454.487954+000
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)}}
LastBootTime
————
31/01/2016 09:44:54
But the CIM cmdlets do it all for you:
Get-CimInstance -ClassName Win32_OperatingSystem | select LastBootUpTime
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'”
$x.Terminate()
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
$cs
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
$csd
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
PSComputerName : RSSURFACEPRO2
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
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 www.manning.com. Richard has been a director of PowerShell.org 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 scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.
Ed Wilson, Microsoft Scripting Guy
0 comments