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 that PowerShell WMI cmdlets come built-in?
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. 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 you have native PowerShell WMI cmdlets, 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.
PowerShell WMI Cmdlets vs CIM
There are two “sets” of commands when it comes to using WMI in PowerShell. Some people refer to 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 PowerShell WMI cmdlet 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.
PowerShell CIM Cmdlets
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 PowerShell WMI cmdlet Get-WmiObject
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 use 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.