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
Register-WMIEvent and so on but we will not be covering these in our article.
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,
The big difference between
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
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
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
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!