A Method to the Madness: Exploring WMI Methods
Windows Management Instrumentation (WMI) and Common Information Model (CIM) provide powerful interfaces for managing Windows systems programmatically. While many administrators are familiar with querying WMI classes for information, the ability to invoke methods on these classes opens up a world of system management possibilities. I thought I would take some time to explores the purpose of WMI methods, how to discover them, and the various approaches to invoking them using both traditional WMI cmdlets and modern CIM cmdlets in PowerShell. This is not something you will use every day, but it is handy information to have in your back pocket, especially because working with WMI methods is not as intuitive.
Why WMI Methods?
WMI methods serve as the action-oriented components of the Windows Management Instrumentation framework. While WMI properties provide read-only or read-write access to system information, methods enable you to perform operations, execute functions, and trigger system changes. Think of WMI properties as the "nouns" of system management and WMI methods as the "verbs." We use the "noun" metaphor all the time in PowerShell, including cmdlets built on WMI such as Get-Volume
.
The primary purposes of WMI methods include:
System Configuration and Management
WMI methods allow administrators to modify system configurations programmatically. For example, you can change network adapter settings, modify registry values, or update system policies without directly manipulating the underlying components. Ideally, you will be able to find a PowerShell command that abstracts this functionality, but sometimes you need to get down and dirty with WMI methods.
Process and Service Control
Methods provide fine-grained control over processes and services. You can start, stop, pause, or restart services, terminate processes, or create new processes with specific parameters and security contexts.
Commands like
Start-Service
,Stop-Service
, andStop-Process
are built using the .NET Framework to access the Windows APIs for managing services and processes. These are not using WMI methods in the way I am discussing here, but they are similar in that they provide a programmatic interface to manage system components.
Hardware Management
WMI methods enable interaction with hardware components. You can perform operations such as ejecting removable media, resetting network adapters, or configuring BIOS settings on supported systems.
Security Operations
Many WMI classes expose methods for security-related operations, including user account management, permission modifications, and security policy enforcement.
Event Management
WMI methods can trigger events, create event subscriptions, or respond to system events programmatically. I've written about this in the first year of this newsletter. It might be time to revisit that topic.
Remote Management
One of the most powerful aspects of WMI methods is their ability to operate on remote systems, enabling centralized management of distributed environments.
Consider this fundamental example of using a WMI method to restart a service. This code requires Windows PowerShell.
# Using WMI to restart the Windows Time service
$service = Get-WmiObject -Class Win32_Service -Filter "Name='W32Time'"
$result = $service.StopService()
if ($result.ReturnValue -eq 0) {
Start-Sleep -Seconds 2
$result = $service.StartService()
Write-Host "Service restart completed with return value: $($result.ReturnValue)"
}
The WMI service object contains methods like StopService
and StartService
, which you can invoke to control the service's state.
PS C:\> $service | Get-Member -MemberType method
TypeName: System.Management.ManagementObject#root\cimv2\Win32_Service
Name MemberType Definition
---- ---------- ----------
Change Method System.Management.ManagementBaseObject Chang...
ChangeStartMode Method System.Management.ManagementBaseObject Chang...
Delete Method System.Management.ManagementBaseObject Delete()
GetSecurityDescriptor Method System.Management.ManagementBaseObject GetSe...
InterrogateService Method System.Management.ManagementBaseObject Inter...
PauseService Method System.Management.ManagementBaseObject Pause...
ResumeService Method System.Management.ManagementBaseObject Resum...
SetSecurityDescriptor Method System.Management.ManagementBaseObject SetSe...
StartService Method System.Management.ManagementBaseObject Start...
StopService Method System.Management.ManagementBaseObject StopS...
UserControlService Method System.Management.ManagementBaseObject UserC...
When you invoke a method, you will get a result object.
PS C:\> $result
__GENUS : 2
__CLASS : __PARAMETERS
__SUPERCLASS :
__DYNASTY : __PARAMETERS
__RELPATH :
__PROPERTY_COUNT : 1
__DERIVATION : {}
__SERVER :
__NAMESPACE :
__PATH :
ReturnValue : 0
PSComputerName :
The ReturnValue
property indicates the success or failure of the operation, with 0
typically indicating success. For other values, you would need to refer to the WMI documentation for the specific class to understand what the return codes mean.
This simple example demonstrates how WMI methods bridge the gap between information gathering and system modification, providing a programmatic interface for system administration tasks.
Discovery
Before you can effectively use WMI methods, you need to understand how to discover them and their parameters. PowerShell provides several tools for method discovery, with Get-CimClass
being the most powerful and modern approach.
Method Discovery with Get-CimClass
The Get-CimClass
cmdlet is your primary tool for exploring WMI classes and their associated methods. This cmdlet provides comprehensive information about class structures, including properties, methods, and their parameters.
Basic Class Discovery
To begin exploring WMI methods, start by identifying the classes that contain the functionality you need:
# Find all classes related to processes
PS C:\> Get-CimClass -ClassName "*Process*" | Where CimClassName -notMatch "Perf" | Select-Object CimClassName
CimClassName
------------
Win32_ProcessTrace
Win32_ProcessStartTrace
Win32_ProcessStopTrace
CIM_Process
Win32_Process
CIM_Processor
Win32_Processor
CIM_AssociatedProcessorMemory
Win32_AssociatedProcessorMemory
CIM_ProcessExecutable
Win32_SessionProcess
Win32_ComputerSystemProcessor
Win32_SystemProcesses
CIM_ProcessThread
CIM_OSProcess
Win32_NamedJobObjectProcess
Win32_ProcessStartup
I'm filtering out the performance classes because they are not useful for our purposes. You can also use Get-CimClass
to explore other classes related to services, network configurations, and more.
# Find classes related to services
PS C:\> Get-CimClass -ClassName "win32*service*" | Where CimClassName -notMatch "Perf" | Select-Object CimClassName
CimClassName
------------
Win32_BaseService
Win32_Service
Win32_TerminalService
Win32_ApplicationService
Win32_LoadOrderGroupServiceDependencies
Win32_DependentService
Win32_SystemServices
Win32_LoadOrderGroupServiceMembers
Win32_ServiceControl
Win32_ServiceSpecification
Win32_ServiceSpecificationService
The majority of the classes you will use in the Root\CIMV2
namespace start with Win32_
.
If you install the PSScriptTools module, you can use the Find-CimClass
function to discover classes beyond the Root\CIMV2
namespace. This function provides a more user-friendly interface for searching and filtering classes.
PS C:\> Find-CimClass *adapter | Where CimClassName -notMatch "perf" |
Select-Object @{Name="Namespace";Expression={$_.CimSystemProperties.Namespace}},CimClassName |
Format-Table -AutoSize
Namespace CimClassName
--------- ------------
Root/CIMV2 CIM_NetworkAdapter
Root/CIMV2 Win32_NetworkAdapter
Root/CIMV2/ms_409 CIM_NetworkAdapter
Root/CIMV2/ms_409 Win32_NetworkAdapter
Root/StandardCimv2 MSFT_NetAdapter
Root/StandardCimv2 MSFT_NetEventNetworkAdapter
Root/StandardCimv2 MSFT_NetEventVmNetworkAdapter
Root/StandardCimv2 MSFT_NetImPlatAdapter
Root/StandardCimv2 MSFT_NetIPInterfaceAdapter
Root/StandardCimv2/MS_409 MSFT_NetAdapter
Root/StandardCimv2/MS_409 MSFT_NetEventNetworkAdapter
Root/StandardCimv2/MS_409 MSFT_NetEventVmNetworkAdapter
Root/StandardCimv2/MS_409 MSFT_NetIPInterfaceAdapter
Root/WMI MSNdis_EnumerateAdapter
Root/WMI MSNdis_WmiEnumAdapter
Root/WMI/ms_409 MSNdis_EnumerateAdapter
Detailed Method Exploration
Once you've identified a class of interest, you can examine its methods in detail:
# Get detailed information about Win32_Process class methods
$processClass = Get-CimClass -ClassName Win32_Process
$processClass.CimClassMethods | Format-Table Name, ReturnType, @{
Name = "Parameters"
Expression = { ($_.Parameters | ForEach-Object { "$($_.Name):$($_.CimType)" }) -join ", " }
}
This command provides a comprehensive view of all methods available in the Win32_Process class, including their return types and parameter signatures.
Name ReturnType Parameters
---- ---------- ----------
Create UInt32 CommandLine:String, CurrentDirectory:String, ProcessStartupP...
Terminate UInt32 Reason:UInt32
GetOwner UInt32 Domain:String, User:String
GetOwnerSid UInt32 Sid:String
SetPriority UInt32 Priority:SInt32
AttachDebugger UInt32
GetAvailableVirtualSize UInt32 AvailableVirtualSize:UInt64
If you have the PSScriptTools module installed, you can use the Get-CimClassMethod
function to retrieve method details more easily:
PS C:\> Get-CimClassMethod -ClassName Win32_Service
Class: Root/Cimv2:Win32_Service
Name ResultType Parameters
---- ---------- ----------
Change UInt32 {DisplayName, PathName, ServiceType, ErrorControl...}
ChangeStartMode UInt32 {StartMode}
Create UInt32 {Name, DisplayName, PathName, ServiceType...}
Delete UInt32 {}
GetSecurityDescriptor UInt32 {Descriptor}
InterrogateService UInt32 {}
PauseService UInt32 {}
ResumeService UInt32 {}
SetSecurityDescriptor UInt32 {Descriptor}
StartService UInt32 {}
StopService UInt32 {}
UserControlService UInt32 {ControlCode}
I'll cover how to use the parameters in a bit.
Filtering Methods by Functionality
You can filter methods based on specific criteria to find exactly what you need:
# Find all methods that contain "Start" in their name across all classes
PS C:\> Get-CimClass | ForEach-Object {
$className = $_.CimClassName
$_.CimClassMethods | Where-Object { $_.Name -like "*Start*" } |
Select-Object @{Name="Class"; Expression={$className}}, Name, ReturnType
}
Class Name ReturnType
----- ---- ----------
CIM_Service StartService UInt32
Win32_PnPSignedDriver StartService UInt32
Win32_BaseService StartService UInt32
Win32_BaseService ChangeStartMode UInt32
Win32_Service StartService UInt32
Win32_Service ChangeStartMode UInt32
Win32_TerminalService StartService UInt32
Win32_TerminalService ChangeStartMode UInt32
Win32_SystemDriver StartService UInt32
Win32_SystemDriver ChangeStartMode UInt32
CIM_BootService StartService UInt32
CIM_ClusteringService StartService UInt32
Win32_ApplicationService StartService UInt32
Win32_PrinterDriver StartService UInt32
Or use my Get-CimClassMethod
function:
PS C:\> Get-CimClass -classname WIN32_* | foreach {
Get-CimClassMethod $_.CimClassName -method "Stop*" -WarningAction SilentlyContinue} |
Select Classname,Name,Parameters
ClassName Name Parameters
--------- ---- ----------
Win32_PnPSignedDriver StopService {}
Win32_BaseService StopService {}
Win32_Service StopService {}
Win32_TerminalService StopService {}
Win32_SystemDriver StopService {}
Win32_ApplicationService StopService {}
Win32_PrinterDriver StopService {}
I need to fix a pipeline bug with this function.
Discovering Parameters
Understanding method parameters is crucial for successful method invocation. Each method parameter has specific characteristics including data type, direction (input/output), and whether it's required or optional.
Examining Parameter Details
# Get detailed parameter information for the Create method of Win32_Process
PS C:\> $processClass = Get-CimClass -ClassName Win32_Process
PS C:\> $createMethod = $processClass.CimClassMethods | Where-Object { $_.Name -eq "Create" }
PS C:\> $createMethod.Parameters | Select Name,CimType,Qualifiers
Name CimType Qualifiers
---- ------- ----------
CommandLine String {ID, In, MappingStrings}
CurrentDirectory String {ID, In, MappingStrings}
ProcessStartupInformation Instance {EmbeddedInstance, ID, In, MappingStrings}
ProcessId UInt32 {ID, MappingStrings, Out}
This reveals important information about each parameter, including:
- Name: The parameter identifier
- CimType: The data type (String, UInt32, Boolean, etc.) required for the parameter value.
- Qualifiers: Provide additional metadata about the parameters, such as whether they are input, output, or optional. If you see
In=True
it is a required parameter.
PS C:\> $CreateMethod.Parameters | Where { $_.Qualifiers['In'].Value} | Select Name,CimType
Name CimType
---- -------
CommandLine String
CurrentDirectory String
ProcessStartupInformation Instance
Understanding Parameter Direction
Parameters can be classified as:
- Input parameters: Values you provide to the method
- Output parameters: Values the method returns
- Input/Output parameters: Values that serve both purposes
Here's a code snippet you could use to build a PowerShell function that analyzes parameter directions.
# Analyze parameter directions for the Create method
$createMethod.Parameters | ForEach-Object {
$direction = 'Unknown'
if ($_.Qualifiers['In'] -and $_.Qualifiers['Out']) {
$direction = 'Input/Output'
}
elseif ($_.Qualifiers['In']) {
$direction = 'Input'
}
elseif ($_.Qualifiers['Out']) {
$direction = 'Output'
}
[PSCustomObject]@{
Name = $_.Name
Type = $_.CimType
Direction = $direction
Optional = [bool]$_.Qualifiers['Optional']
}
}
I would add the class name, namespace, and method name to the output. But this is a good start to understanding the parameters.
Name Type Direction Optional
---- ---- --------- --------
CommandLine String Input False
CurrentDirectory String Input False
ProcessStartupInformation Instance Input False
ProcessId UInt32 Output False
Summary
I think I've overwhelmed you enough for one day. Take some time to use my code samples, and commands from the PSScriptTools module to explore WMI methods. Find something you think you'd like to try, on a non-production system, of course. I'll get into invoking methods next time.