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. Did you know you can easily use PowerShell to manage 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 Powershell WMI. 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.