Windows Management Instrumentation (WMI) has been around for a long time in Windows. Lots of IT pros have used WMI either directly or have used tools that read information from WMI.

WMI is the de facto place to gather information about a Windows machine and to manipulate various services inside of Windows. Because of WMI's vast array of information, you can tap into using WMI in PowerShell. By using PowerShell to interrogate WMI allows you to automate thousands of tasks on Windows computers.

Luckily, one of the first systems in Windows that PowerShell had commands for was WMI. Since PowerShell had native support for WMI, this meant that an IT administrator could leverage the easy-to-understand verb/noun syntax of PowerShell to interact with WMI and, as you'll see, power users can also invoke WMI events and classes directly by just using standard PowerShell commands.

WMI vs. CIM PowerShell Cmdlets

There are two "sets" of commands when it comes to using WMI in PowerShell. Some people refer these "sets" as the "old" way to interact with WMI and the CIM cmdlets. When querying information from WMI, both return the same information but approach it a little differently. The "old" way of reading information from WMI is by using the Get-WmiObject command. ´┐ŻOther WMI cmdlets include Invoke-WmiMethod, Register-WMIEvent and so on but we will not be covering these in our article.

The Get-WmiObject works but has a serious drawback. When querying remote computers, it relies on DCOM and DCOM has historically been an attack vector for bad guys. DCOM is usually blocked by various firewalls. Because this is the "old" way, we're not going to go into this method. Instead, we'll focus on the CIM cmdlets: primary, Get-CimInstance.

The big difference between Get-WmiObject and Get-CimInstance is that instead of using DCOM to access remote computers, Get-CimInstance uses the WSMAN protocol which is the transport for the familiar PowerShell remoting feature.

Demo of Querying CIM

Let's say you'd like to return some information about the version of Windows on a remote computer. In this instance, I'd like to see some information from a computer called DC. You can do this by specifying the WMI class that contains the information and the computer name I'd like to query.

You can see below that since Get-CimInstance does not, by default, display all attributes inside of the class, I've had to pipe the output to the Select-Object command to return all of the properties.

PS> Get-CimInstance -ClassName Win32_OperatingSystem -ComputerName DC | Select *
Status                                    : OK
Name                                      : Microsoft Windows 10 Pro|C:\WINDOWS|\Device\Harddisk0\Partition2
FreePhysicalMemory                        : 1802336
FreeSpaceInPagingFiles                    : 800308
FreeVirtualMemory                         : 1472916
Caption                                   : Microsoft Windows 2016
Description                               :
InstallDate                               : 7/5/2018 6:04:09 PM
CreationClassName                         : Win32_OperatingSystem
CSCreationClassName                       : Win32_ComputerSystem
--snip-

Not only can you gather information via properties, but you can also invoke WMI/CIM methods on classes as well. A good example of this is stopping a process on a remote computer. Natively, there is no way to do this via the Stop-Process command in PowerShell. However, there is a way via WMI query. To kill remote processes, we first need to figure out which process we'd like to kill. We can enumerate all running processes on a remote computer by looking at the Win32_Process class.

PS> Get-CimInstance -ClassName CIM_Process -ComputerName DC

ProcessId Name                                      HandleCount WorkingSetSize VirtualSize   PSComputerName
--------- ----                                      ----------- -------------- -----------   --------------
0         System Idle Process                       0           4096           65536         DC
4         System                                    631         212992         3465216       DC
232       smss.exe                                  52          823296         4325376       DC
328       csrss.exe                                 258   om      3346432        49483776      DC
392       csrss.exe                                 87          2789376        44507136      DC
400       wininit.exe                               80          3072000        2199065096192 DC
428       winlogon.exe                              119         4431872        2199079510016 DC

Once you've figured out with process to kill, you can then pass the name to the Terminate() method on the Win32_Process class. To do that, you need to capture an instance of the process you'd like to kill.

Notice below that I'm using the Filter parameter to ensure I only receive the ccmexec.exe process. This is important!

Once you've captured the instance, you can then invoke the Terminate() method to kill the process using the Invoke-CimMethod command as shown below.

PS> $instance = Get-CimInstance -ClassName CIM_Process -ComputerName DC -Filter 'Name = "ccmexec.exe"'
PS> $instance

ProcessId Name        HandleCount WorkingSetSize VirtualSize PSComputerName
--------- ----        ----------- -------------- ----------- --------------
3528      CcmExec.exe 878         34246656       158261248   DC


PS> Invoke-CimMethod -InputObject $instance -MethodName Terminate

ReturnValue PSComputerName
----------- --------------
          0 DC

Summary

If you'd like to learn more about using WMI in PowerShell and especially using the Get-WmiObject cmdlet that wasn't covered here, I encourage you to check out Get-WmiObject: Querying WMI on Local and Remote Computers where I go deeper into WMI and using the Get-WmiObject cmdlet.

For a full breakdown of all of the CIM cmdlets and how they work in PowerShell, refer to the Introduction to CIM Cmdlets article on Technet. There you'll see all of the various CIM cmdlets and how they might be used in your scripts.

Join the Jar Tippers on Patreon

It takes a lot of time to write detailed blog posts like this one. In a single-income family, this blog is one way I depend on to keep the lights on. I'd be eternally grateful if you could become a Patreon patron today!

Become a Patron!