WMI is considered a valuable tool for system administrators, allowing management of Windows workstations, interaction with Microsoft products like Configuration Manager, monitoring of server resources, and more. Microsoft will explore various WMI usage options with PowerShell. By the end, you will understand when to use each method and may not have a clear favorite.
The Ways
There are three tools for managing WMI I want to share with you.
- The System.Management namespace.
- The WMI Scripting API.
- The CIM cmdlets.
Regarding WMI cmdlets such as Get-WmiObject, they won’t be discussing them today for two reasons: they’re only available in Windows PowerShell and the System.Management namespace offers similar functionality. If you haven’t tried PowerShell 7 yet, they highly recommend giving it a try.
The Procedure
Microsoft aims to address common tasks encountered in administering Windows devices, including:
- Querying.
- Calling a WMI Class method.
- Creating, Updating, and Deleting a WMI Class Instance.
- Bonus: Creating, Populating, and Deleting a custom WMI Class.
Microsoft also aims to demonstrate the advantages and disadvantages of each method and highlight where one method excels over the others.
The System.Management Namespace
If Microsoft had to choose, their favorite would be this method. It brings an object-oriented approach to WMI and makes WMI management easier to understand. Additionally, if you’re a C# developer, you’ll feel right at home.
Querying
To execute a query, you need an instance of the ManagementObjectSearcher class. There are three constructors worth examining.
ManagementObjectSearcher(String)
- The simplest one. Creates a searcher object specifying the query string.
ManagementObjectSearcher(String, String)
- Creates the object with the query and the scope.
ManagementObjectSearcher(ManagementScope, ObjectQuery)
- The same as the previous one, but with instances of the objects instead of strings. This gives you more options.
After obtaining the searcher, the Get method is called to retrieve the ManagementObjects.
$query = "Select * From Win32_Process Where Name = 'powershell.exe'"
$searcher = [wmisearcher]($query)
$result = $searcher.Get()
The variable $result holds an instance of the ManagementObjectCollection class, which contains all the Win32_Process instances as ManagementObjects.
$result = $searcher.Get()
$result | Format-Table -Property ProcessId, Name, ExecutablePath -AutoSize
```Output
ProcessId Name ExecutablePath
--------- ---- --------------
4116 powershell.exe C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
This is how it looks like using the second and third constructors.
$query = "Select * From Win32_Process Where Name = 'powershell.exe'"
$scope = 'root\cimv2'
$searcher = [wmisearcher]::new($scope, $query)
$result = $searcher.Get()
# Or
$query = [System.Management.ObjectQuery]::new("Select * From Win32_Process Where Name = 'powershell.exe'")
$scope = [System.Management.ManagementScope]::new('root\cimv2')
$scope.Connect()
$searcher = [System.Management.ManagementObjectSearcher]::new($scope, $query)
$result = $searcher.Get()
Calling a WMI Method
Microsoft can either call a method on the resulting ManagementObject from the query operation, such as Terminate or call a method on the WMI Class object. Let’s create a new process using the Create method.
$commandLine = 'powershell.exe -ExecutionPolicy Bypass -Command "Write-Output ''Howdy! From WMI!''; Read-Host"'
$processClass = [wmiclass]'Win32_Process'
# The parameters are: CommandLine, CurrentDirectory and ProcessStartupInformation.
$processClass.Create($commandLine, $null, $null)
If the method is successful, you should see a PowerShell console and the Output Parameters displayed.
__GENUS : 2
__CLASS : __PARAMETERS
__SUPERCLASS :
__DYNASTY : __PARAMETERS
__RELPATH :
__PROPERTY_COUNT : 2
__PROPERTY_COUNT : 2
__DERIVATION : {}
__SERVER :
__NAMESPACE :
__PATH :
ProcessId : 11896
ReturnValue : 0
PSComputerName :
Creating, Updating, and Deleting a WMI Class Instance
Microsoft will use ManagementClass.CreateInstance()
method to create a new instance of the SMS_Collection class and then use Put to save it to the namespace.
$collection = ([wmiclass]'root\SMS\site_PS1:SMS_Collection').CreateInstance()
$collection.Name = 'AwesomeDeviceCollection'
$collection.LimitingCollectionID = 'PS1000042'
$collection.Put()
# The Get() method updates the $collection object with the new
# property values populated by the Config Manager.
$collection.Get()
Updating and deleting.
$collection = [wmiclass]"root\SMS\site_PS1:SMS_Collection.CollectionID='PS1000043'"
$collection.Name = 'AwesomeDeviceCollection_NewName'
$collection.Put()
# Deleting
$collection.Delete()
The WMI Scripting API
The WMI Scripting API is simply the WMI COM interfaces exposed through a Runtime Callable Wrapper, also known as the WMI COM Object. Although not as straightforward as the System.Management namespace, this method of managing WMI offers greater flexibility in its implementation.
Querying
To begin, Microsoft needs to create an instance of the SWbemLocator object, which serves as the interface to other objects and obtain a SWbemServices object by connecting to the server.
$locator = New-Object -ComObject 'WbemScripting.SWbemLocator'
$services = $locator.ConnectServer()
Next, Microsoft uses the ExecQuery method from the SWbemServices object to execute their query. This method returns a SWbemObjectSet, which is a collection of SWbemObjects, and its properties can be found under the Properties_ property.
$result = $services.ExecQuery("Select * From Win32_Process Where Name = 'powershell.exe'")
$object = $result | Select-Object -First 1
$value = $object.Properties_['ProcessId'].Value
Calling a WMI Method
First, they create an instance of the __Properties class, which holds the input parameters for the Create method. Then, they use the SWbemServices.ExecMethod() method to call Create.
$commandLine = 'powershell.exe -ExecutionPolicy Bypass -Command "Write-Output ''Howdy! From WMI!''; Read-Host"'
$parameters = $object.Methods_['Create'].InParameters.SpawnInstance_()
$parameters.Properties_['CommandLine'].Value = $commandLine
$output = $services.ExecMethod('Win32_Process', 'Create', $parameters)
The $output
variable contains a SWbemObject, which is an instance of the Output Parameters property class.
$services.ExecMethod('Win32_Process', 'Create', $parameters)
Value : 16172
Name : ProcessId
IsLocal : True
Origin : __PARAMETERS
CIMType : 19
Qualifiers_ : System.__ComObject
IsArray : False
Value : 0
Name : ReturnValue
IsLocal : True
Origin : __PARAMETERS
CIMType : 19
Qualifiers_ : System.__ComObject
IsArray : False
Creating, Updating, and Deleting a WMI Class Instance
Let’s replicate our last example using the Scripting API.
$collection = $services.Get('\\.\root\SMS\site_PS1:SMS_Collection').SpawnInstance_()
$collection.Properties_['Name'].Value = 'AwesomeDeviceCollection'
$collection.Properties_['LimitingCollectionID'].Value = 'PS1000042'
$collection.Put_()
Updating and deleting.
$collection = $services.Get("\\.\root\SMS\site_PS1:SMS_Collection.CollectionID='PS1000043'")
$collection.Properties_['Name'].Value = 'AwesomeDeviceCollection_NewName'
$collection.Put_()
# Deleting
$collection.Delete_()
The CIM Cmdlets
For quick and efficient WMI data analysis without the need for interaction, the CIM Cmdlets are unbeatable. They are fast and provide convenient features such as auto-complete for class and namespace names and easy class retrieval with Get-CimClass.
Querying
Performing queries with the CIM Cmdlets is very pleasant. One line does it all.
$result = Get-CimInstance -Query "Select * From Win32_Process Where Name = 'powershell.exe'"
The parameters are similar to those of Get-WmiObject and can be used in a similar manner.
$result = Get-CimInstance -ClassName 'Win32_Process' -Filter "Name = 'powershell.exe'"
The auto-complete feature in Visual Studio Code.
Calling a WMI Method
The CIM Cmdlets offers a distinctive approach to executing WMI methods. The outcome of a CIM query is referred to as CimInstances, and instance methods cannot be invoked in the same way as with the other two options. Instead, a separate Cmdlet named Invoke–CimMethod is used.
$commandLine = 'powershell.exe -ExecutionPolicy Bypass -Command "Write-Output ''Howdy! From WMI!''; Read-Host"'
$result = Get-CimClass -ClassName 'Win32_Process'
$params = @{
MethodName = 'Create'
Arguments = @{
CommandLine = $commandLine
}
}
$output = $result | Invoke-CimMethod @params
# Or
$params = @{
ClassName = 'Win32_Process'
MethodName = 'Create'
Arguments = @{
CommandLine = $commandLine
}
}
$output = Invoke-CimMethod @params
And the result:
Invoke-CimMethod -ClassName 'Win32_Process' -MethodName 'Create' -Arguments @{ CommandLine = $commandLine }
ProcessId ReturnValue PSComputerName
--------- ----------- --------------
14932 0
Struggling to recall parameters? Same here! Fortunately, auto-complete also works with them.
Creating, Updating, and Deleting a WMI Class Instance
If you used the old WMI Cmdlets before, this will look familiar.
$params = @{
Namespace = 'root\SMS\site_PS1'
ClassName = 'SMS_Collection'
Property = @{
Name = 'AwesomeDeviceCollection'
LimitingCollectionID = 'PS1000042'
}
}
$collection = New-CimInstance @params
Updating and deleting.
$params = @{
Namespace = 'root\SMS\site_PS1'
Query = "Select * From SMS_Collection Where Name = 'AwesomeDeviceCollection'"
Property = @{
Name = 'AwesomeDeviceCollection_NewName'
}
}
Set-CimInstance @params
#Or
$params = @{
Namespace = 'root\SMS\site_PS1'
Query = "Select * From SMS_Collection Where Name = 'AwesomeDeviceCollection'"
}
$collection = Get-CimInstance @params
$collection | Set-CimInstance -Property @{
Name = 'AwesomeDeviceCollection_NewName'
}
#Deleting
$params = @{
Namespace = 'root\SMS\site_PS1'
Query = "Select * From SMS_Collection Where Name = 'AwesomeDeviceCollection'"
}
$collection = Get-CimInstance @params
$collection | Remove-CimInstance
Pros and Cons
The System.Management namespace is efficient for obtaining objects or class instances, and their aliases such as [wmi]
or [wmiclass]
simplify their usage if their paths are known. Calling methods is straightforward, but performing queries and complex operations may be slower and require managing more objects.
The WMI Scripting API is ideal when constructing comprehensive scripts for WMI management. With the SWbemServices object, you have access to most WMI features. Its direct access to RCW interfaces also delivers improved performance compared to the System.Management namespace, which wraps these interfaces for abstraction. However, retrieving individual objects or performing data analysis queries can be cumbersome with this method, as it requires more effort compared to the System.Management namespace.
The CIM cmdlets excel in performance when it comes to querying large datasets, and the CimInstance object is easy to work with when combined with other common objects like PSCustomObject. However, calling methods is not as straightforward as with other methods, and accessing methods like Put, Get, or Delete can be challenging.
Conclusion
With the knowledge gained from this discussion, you should now be able to choose the appropriate tool for working with WMI depending on your needs. No single method is the best in all situations, but each has its own strengths. By utilizing all of them, you will become a more effective System Administrator.
Here at CourseMonster, we know how hard it may be to find the right time and funds for training. We provide effective training programs that enable you to select the training option that best meets the demands of your company.
For more information, please get in touch with one of our course advisers today or contact us at training@coursemonster.com