Home > Articles > Programming > Windows Programming

Efficient Windows PowerShell Administration with WMI and CIM

  • Print
  • + Share This
Timothy Warner, author of Sams Teach Yourself Windows PowerShell in 24 Hours, differentiates between the often-confused terms WMI and CIM, and explains how best to use these technologies with Windows PowerShell.
From the author of

Let's imagine that you and I started a business manufacturing and selling network interface cards (NICs). Industry standards would be pretty important to us, right? How could we make it easier for our Ethernet NICs to work natively with systems based on, say, Windows, Linux, and OS X? What about compatibility with different network architectures, protocols, and client/server applications? (Whoa—I'm glad we don't really need to worry about that particular set of problems!)

Windows systems administrators rely on several Distributed Management Task Force (DMTF) industry standards to make our lives easier. The DMTF is an industry consortium whose membership includes major computer hardware and software manufacturers. Their goal is to agree on standards so their products work together as seamlessly as possible.

In this article, we'll look at how to apply a couple of key DMTF standards to help us be more effective with Windows PowerShell–based systems administration.

Understanding the Relationship Between CIM and WMI

The Common Information Model (CIM, pronounced sim) is a DMTF specification that describes computer hardware and software components. CIM is part of a larger systems-management framework called Web-Based Enterprise Management (WBEM).

Every Windows server or client computer has a local CIM repository. As systems administrators, we can tap into that CIM repository to fetch and set properties and take action on the repository data.

Although it's a long-time DMTF member, a while back Microsoft made the ill-advised decision to write its own abstraction layer on top of CIM, called Windows Management Instrumentation (WMI).

What's confusing to many admins is that in Windows PowerShell v3 and later we can access the CIM repository by using either WMI or CIM calls. One of my goals in this article is to show you the pros and cons of each approach.

Let's begin by running through a simple example to help us visualize the CIM repository. At the moment I'm running a Windows 8.1 computer on which I've installed the free and open-source WMI Explorer desktop application. Figure 1 shows an annotated user interface.

Figure 1 WMI Explorer.

We start using WMI Explorer by clicking Connect to load the current computer's CIM repository (annotation A in Figure 1). The namespace is the highest level in the CIM hierarchy. In my experience, we use ROOT\CIMV2 almost exclusively for Windows systems management. When we double-click ROOT\CIMV2, after a moment the Classes pane populates (annotation B). Whereas a namespace defines a group of related classes, the class itself is a blueprint (definition) for a particular hardware or software component.

Type service in the Quick Filter list and double-click Win32_Service to load all service instances on the local computer (annotation C). If we think of a class as a generic object blueprint, an instance is an individual copy of that blueprint.

Any Windows computer has many services running, so WMI Explorer displays a mighty big list of service instances. Type spooler in the Quick Filter list and double-click Win32_Service.Name="Spooler" to load the properties of that instance (annotation D).

At the bottom of the WMI Explorer window (annotation E) is the following query:

SELECT * FROM Win32_Service WHERE Name='Spooler'

Earlier I explained that WMI is Microsoft's implementation of CIM. Microsoft also created the WMI Query Language (WQL) to give admins a method that works like Structured Query Language (SQL) for accessing CIM object data. If you don't yet know SQL, I'd encourage you to learn it, because you can apply that syntax in WQL to query system configuration data.

Finally, spend some time clicking across the six tabs marked at annotation F:

  • Instances: Defines the object and shows selected attributes (properties) that describe the object).
  • Properties: Full list of properties, along with their descriptions. The window that shows the MSDN documentation is especially helpful here.
  • Methods: Actions that an object can perform. For example, we can call StartService() and StopService(), respectively, to start and stop the given service.
  • Query: Use WQL syntax to run ad hoc queries against the current object.
  • Script: Generate a PowerShell script from the current query.
  • Logging: Status messages reported from the CIM repository itself.

WMI in Action

I don't want to spend too much time on the WMI cmdlets because, frankly, Microsoft is deprecating them in favor of its own CIM cmdlets. However, you'll still need the legacy WMI commands if you're supporting computers running Windows PowerShell v1 or v2 or if legacy scripts are used in your environment.

Let's enumerate the WMI cmdlets:

Get-Command -Noun wmi* | Select-Object -Property Name

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

For accessing the local computer's CIM repository, Get-WmiObject works pretty well. The command defaults to the ROOT\CIMv2 namespace, so all we need to do is to supply the appropriate class name:

Get-WmiObject -Class Win32_OperatingSystem

SystemDirectory : C:\Windows\system32
Organization    :
BuildNumber     : 9600
RegisteredUser  : Windows User
SerialNumber    : 00261-80246-78149-AA747
Version         : 6.3.9600

The WMI situation begins to show its age when we use the -ComputerName parameter to retrieve WMI information from remote computers:

Get-WmiObject -Query "SELECT * FROM win32_service WHERE name='Spooler'" -ComputerName
 localhost,mem1,mem2 | Format-List -Property PSComputerName,Name,State,Status

PSComputerName : DC1
Name           : Spooler
State          : Running
Status         : OK

PSComputerName : MEM1
Name           : Spooler
State          : Running
Status         : OK

PSComputerName : MEM2
Name           : Spooler
State          : Running
Status         : OK

Sure, it works, but at what cost?

Notice the handy -Query parameter in the previous example. If you've worked with relational databases and the SQL data access language, you'll feel right at home with using WQL to query CIM repository data. Here's the deal: The Get-WmiObject cmdlet uses old-school PowerShell remoting, which involves the following issues:

  • The underlying network transport involves DCOM and RPC, which are older, "heavier" protocols with corresponding reduced network performance.
  • DCOM and RPC use dynamic port allocation, which means that you'll have difficulty on most networks unless the appropriate firewall rules are configured.
  • Remote computers are queried serially rather than in parallel.

These three issues (especially the firewall port issue) can plague your work with annoying problems. For instance, frustrating "No RPC server available" error messages might crop up when you use Get-WmiObject, because the cmdlet is trying to use random TCP ports.

If the WMI cmdlets have any advantage, it's that you can take action on CIM repository data from remote computers, using methods available to you because DCOM and RPCs build up and tear down persistent connections to the remote CIM repositories for each connection request. Take a look:

$mem2spool = Get-WmiObject -Query "SELECT * FROM win32_service WHERE name='Spooler'"
$mem2spool | Get-Member -MemberType Method | Select-Object -Property Name

Name
----
Change
ChangeStartMode
Delete
GetSecurityDescriptor
InterrogateService
PauseService
ResumeService
SetSecurityDescriptor
StartService
StopService
UserControlService

That capability is actually pretty cool, because we can control that remote service by using dot notation:

$mem2spool.StopService()
$mem2spool.StartService()

As we'll see next, the "new school" CIM commands don't have methods—or at least not initially.

CIM in Action

Whereas the old WMI cmdlets use stateful connections for remote access and trip over existing firewall rules, the new CIM cmdlets run in a much leaner, meaner fashion, thanks to their use of the DMTF Web Services-Management (WS-Man) protocols.

Current Windows OS versions rely on the Windows Remote Management (WinRM) service to make standards-based PowerShell remoting possible. PowerShell WS-Man remoting brings these possibilities:

  • HTTP/HTTPS transport and XML serialized data streams make the remote access extremely firewall-friendly.
  • WS-Man remoting is stateless, which means faster performance than with the older DCOM/RPC methods.
  • Remote computers are queried in parallel, in what Microsoft calls a "fan out" remote management scenario.

Pretty exciting stuff!

Let's enumerate the CIM cmdlets:

Get-Command -Module CimCmdlets | Select-Object -Property Name

Name
----
Export-BinaryMiLog
Get-CimAssociatedInstance
Get-CimClass
Get-CimInstance
Get-CimSession
Import-BinaryMiLog
Invoke-CimMethod
New-CimInstance
New-CimSession
New-CimSessionOption
Register-CimIndicationEvent
Remove-CimInstance
Remove-CimSession
Set-CimInstance

The Get-CimInstance command is the direct analog to Get-WmiObject, so I suggest that you learn how to use this command post-haste. Let's get help:

Get-Help Get-CimInstance -ShowWindow

One great thing about the CIM cmdlets (unlike the WMI cmdlets) is that they support tab completion! This feature is tremendously useful when you aren't exactly sure which class name you need:

PS C:\> Get-CimInstance -ClassName Win32_BIOS


SMBIOSBIOSVersion : 6.00
Manufacturer      : Phoenix Technologies LTD
Name              : PhoenixBIOS 4.0 Release 6.0
SerialNumber      : VMware-56 4d f5 d0 c3 4b d8 9e-3b bf 2e fa 04 4d 67 d7
Version           : INTEL  - 6040000

Following are two examples in which we look for processes whose names start with the letter n. Notice that we can use -Query with a WQL expression or -Filter with a more PowerShell-native filter expression. Both examples return the same results:

Get-CimInstance -Query "SELECT * FROM Win32_Process WHERE name LIKE 'n%'"

ProcessId Name        HandleCount WorkingSetSize VirtualSize
--------- ----        ----------- -------------- -----------
664       notepad.exe 76          7524352        2199118839808
1664      notepad.exe 76          7491584        2199122526208


Get-CimInstance -ClassName win32_process -Filter "name like 'n%'"

ProcessId Name        HandleCount WorkingSetSize VirtualSize
--------- ----        ----------- -------------- -----------
664       notepad.exe 76          7524352        2199118839808
1664      notepad.exe 76          7491584        2199122526208

But wait—what about remote access, and what I said about methods earlier in this article? Take a look:

$mem1spooler = Get-CimInstance -ComputerName mem1 -query "SELECT * FROM win32_service WHERE name='Spooler'"
$mem1spooler | gm -MemberType Method

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

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.S...
GetType                   Method     type GetType()
ToString                  Method     string ToString()

Remember those useful methods that become available when we use Get-WmiObject to create a variable to hold a remote server's spooler service? Not so with Get-CimInstance. The reason is simple: WS-Man remoting is stateless, so there's no persistent link to the remote computer's CIM repository.

Also, WS-Man remoting uses SOAP and XML to serialize the data stream from the remote host to your local server, so you're not dealing with live objects as you are with Get-WmiObject. Long story short—no methods. However, we can absolutely leverage the PowerShell pipeline and the Invoke-CimMethod cmdlet to call a method. Suppose I stop the Spooler service on my mem1 server, like this:

Get-CimInstance -ComputerName mem1 -query "SELECT * FROM win32_service WHERE name='Spooler'" | Invoke-CimMethod -MethodName StopService

In case you wondered how I knew to call StopService as my method, let me draw your attention to the Get-CimClass cmdlet:

Get-CimClass -ClassName Win32_Service  | Select-Object -ExpandProperty CimClassMethods

Name                  ReturnType Parameters
----                  ---------- ----------
StartService              UInt32 {}
StopService               UInt32 {}
PauseService              UInt32 {}
ResumeService             UInt32 {}
InterrogateService        UInt32 {}
UserControlService        UInt32 {ControlCode}
Create                    UInt32 {DesktopInteract, DisplayName, ErrorControl, LoadOrde...
Change                    UInt32 {DesktopInteract, DisplayName, ErrorControl, LoadOrde...
ChangeStartMode           UInt32 {StartMode}
Delete                    UInt32 {}
GetSecurityDescriptor     UInt32 {Descriptor}
SetSecurityDescriptor     UInt32 {Descriptor}

Next Steps

If you understood what we covered in this article, you've come a great distance in mastering PowerShell-based management with CIM. Your next step is to learn how to use CIM sessions to make even more efficient use of network bandwidth when managing remote computers. To that point, I'll leave you with a few references to check out:

Happy PowerShelling!

  • + Share This
  • 🔖 Save To Your Account