Common Information Model (CIM) cmdlets have been around for a while, and come in handy when you are performing any CIM operation in PowerShell. They are easy to use, and provide a good user experience. In this blog post, I cover a few tips and tricks that can be useful for users of CIM cmdlets.
To get started, let’s get the complete list of cmdlets. The PowerShell module name for these cmdlets is CimCmdlets, and we can use the Get-Command cmdlet to see the cmdlets exposed by this module.
PS :> # Get all CIM Cmdlets PS :> Get-Command –module CimCmdlets CommandType Name ModuleName -------------------- ------------------------------------- ---------------------- Cmdlet Get-CimAssociatedInstance CimCmdlets Cmdlet Get-CimClass CimCmdlets Cmdlet Get-CimInstance CimCmdlets Cmdlet Get-CimSession CimCmdlets Cmdlet Invoke-CimMethod CimCmdlets Cmdlet New-CimInstance CimCmdlets Cmdlet New-CimSession CimCmdlets Cmdlet New-CimSessionOption CimCmdlets Cmdlet Register-CimIndicationEvent CimCmdlets Cmdlet Remove-CimInstance CimCmdlets Cmdlet Remove-CimSession CimCmdlets Cmdlet Set-CimInstance CimCmdlets |
Tip#1: Working with CIM sessions
A CIM session is a client-side object representing a connection to a local or remote computer. The CIM session contains information about the connection, such as ComputerName, the protocol used for the connection, session ID, and instance ID.
a. When you are trying to connect to a remote machine, you might have to pass credentials. The New-CimSession cmdlet consumes a PSCredential object when credentials are used to create a CIM session. The constructor of PSCredential object accepts only a secure password string.
PS:> # Type in the password when you are prompted PS:> .$creds = Get-Credential -Credential username
PS:> # Save credentials for future use PS:> $creds | Export-Clixml -Path c:\a.clixml
PS:> # Use the saved credentials when needed PS:> $savedCreds = Import-Clixml -Path C:\a.clixml
PS:> # Create CimSession with Credentials PS:> $session = New-CimSession –ComputerName “machineName” -Credentials $savedCreds
|
b. If the ComputerName parameter is added, the protocol used is WS-Management (WS-Man).
PS:> # If the ComputerName parameter is not added, the cmdlet uses DCOM/COM PS:> # Creates DCOM session PS:> $session = New-CimSession PS:> # Performs enumerate over DCOM PS:> $inst = Get-CimInstance Win32_OperatingSystem
PS:> # If ComputerName is added, the cmdlets go over WS-Man PS:> $session = New-CimSession –ComputerName localhost PS :> $inst = Get-CimInstance Win32_OperatingSystem –ComputerName localhost
|
c. If you use any parameter except “–Protocol DCOM” with New-CimSessionOption, the session option helps generate the WS-Man session.
PS:> # DCOM Session PS:> $sessionOp = New-CimSessionOption –Protocol DCOM PS:> $sessionDcom = New-CimSession –SessionOption $sessionOp –ComputerName localhost
PS:> # WS-Man Session: the parameter UseSSL added to the New-CimSessionOption command specified the WS-Man protocol PS:> $sessionOp2 = New-CimSessionOption –UseSSL PS:> $sessionWSMAN = New-CimSession –SessionOption $sessionOp2 –ComputerName localhost
|
d. The Default protocol in your New-CimSessionOption command corresponds to WS-Man.
PS:> # Create a CimSession using Default protocol PS:> New-CimSession –ComputerName localhost –SessionOption (New-CimSessionOption –Protocol Default)
|
e. If you are performing a large number of remote operations,, I recommend that you reuse sessions. This can provide a significant performance gain.
PS:> # Perform Get-CimInstance using computerName PS:>$time 1 = Measure-Command { 1..100 | %{ Get-CimInstance –ClassName CIM_ComputerSystem –ComputerName remoteMachine } }
PS:> # Create a CimSession PS:> $session = New-CimSession –ComputerName remoteComputer PS:> $time2 = Measure-Command { 1..100 | %{ Get-CimInstance –ClassName CIM_ComputerSystem –CimSession $session } }
|
In the above example, $time1 > $time2, because the Get-CimInstance cmdlet calls that use ComputerName parameter create a CIM session under the hood every time. In the second scenario, where we use an explicitly created CIM session, the cost of creating a CIM session in each call is not there.
Tip#2: Fan-out
a. Working with multiple servers
All CIM cmdlets accept an array of CIM sessions or computer names. These can be used to perform operations on multiple servers in one line.
PS :> # Create multiple sessions PS :> $allSessions = New-CimSession -ComputerName “machine1”, “machine2”, “machine3” –Credentials $credentials
PS :> # Fan-out with CIM session Array PS :> Get-CimInstance –ClassName Win32_OperatingSystem –CimSession $allSessions
PS :> # Reboot all machines in one line PS :> Invoke-CimMethod –ClassName Win32_OperatingSystem –MethodName Reboot –CimSession $allSessions
PS:> # OR
PS:> Invoke-CimMethod –ClassName Win32_OperatingSystem –MethodName Reboot –ComputerName “Machine1”, Machine2”,”Machine3”
|
b. Don’t have to pass session if you use pipes. Instances are machine-aware
If an instance received from one cmdlet is piped to another cmdlet, the cmdlet on the right side of the pipe is not required to specify computer names or CIM sessions, because the instances are machine-aware. The cmdlet on the right-side of the pipe can extract machine-related information from the instances.
PS:> # Get instance from a class PS:> $session = New-CimInstance -ComputerName machine1 PS:> $inst = Get-CimInstance –Query “Select * from TestClass where v_key = 2” –Namespace root/test –CimSession $session
PS:> # Pass CIM instance PS:> $props = @{ boolVal = $true } PS:> $inst | Set-CimInstance –CimInstance $inst –Property $props
# OR
PS:> # Pipe result of get into set PS:> Get-CimInstance –Query “Select * from TestClass where v_key = 2” –Namespace root/test –CimSession $session | Set-CimInstance –CimInstance $inst –Property $props
|
Tip#3: Working with non-Windows CIMOMs
a. Connecting to devices that need resource URIs
Some vendors support non-DMTF resource URIs, (here DMTF stands for Distributed Management Task Force) but the parameter set of CIM cmdlets that uses ClassName might not work in that scenario. CIM cmdlets offer a parameter set that accepts non-DMTF resource URIs, and lets you manage those devices. The following example demonstrates this:
PS:> # Non DMTF resource URI PS:> $resourceuri = “http://intel.com/wbem/wscim/1/amt-schema/1/AMT_PETFilterSetting"
PS :> # Get instances PS:> $inst = Get-CimInstance -Namespace $namespace -ResourceUri $resourceuri -CimSession $session
PS :> # Query instances PS:> $inst = Get-CimInstance -Query $query -Namespace $namespace -ResourceUri $resourceuri -CimSession $session
PS:> # Modify instance PS:> $propsToModify = @{LogOnEvent=$false} PS:> Set-CimInstance -InputObject $inst -Property $ propsToModify -ResourceUri $resourceuri -CimSession $session |
b. If the Common Information Model Object Manager (CIMOM) does not support TestConnection, the New-CimSession command will fail. How to make sure a session can be created with such a CIMOM?
PS:> # Creating CIM session when the server/CIMOM does not support TestConnection PS:> $session = New-CimSession –CN “machineName” -Credentials $creds–SkipTestConnection
|
c. If the CIMOM is listening on a port other than the default port, you can make sure New-CimSession calls the particular port.
PS:> # Default HTTP port for WS-Man on Windows PS:> $httpPort = 5985 PS:> # Default HTTPS port for WS-Man on Windows PS:< $httpsPort = 5986
PS:> # Port parameter is exposed by New-CimSession and not New-CimSessionOption PS:> $session = New-CimSession –CN “machineName” -Credentials $creds –Port $httpPort
PS:> $sop = New-CimSessionOption –UseSSL PS:> $session = New-CimSession –CN “machineName” -Credentials $creds –Port $httpsPort
|
Tip#4: Get associated instances
Associations are important in the CIM world, as they define the relationship between two classes. Get-CimAssociatedInstance provides a way of figuring out these relationships.
PS :> # Get instance of Win32_LogicalDisk class with DriveType =3 (hard drives) PS :> $disks = Get-CimInstance -class Win32_LogicalDisk -Filter 'DriveType = 3'
PS :> # Get the all instances associated with this disk PS :> Get-CimAssociatedInstance -CimInstance $disks[0]
PS :> # Get instances of a specific type PS :> Get-CimAssociatedInstance -CimInstance $disks[0] -ResultClassName Win32_DiskPartition
PS :> # Finding associated instances through a specific CIM relationship PS :> Get-CimAssociatedInstance -CimInstance $disks[0] -Association Win32_LogicalDiskRootDirectory
|
Tip#5: Passing ref and embedded instances
Did you know? If you pass an instance as REF, only key values get passed on to the provider by the infrastructure; for example, even if all the properties are populated inside the instance, the provider only gets to see the key values.
There are some edge-case scenarios that come up with REF arrays.
a. If the instances are created with the ClientOnly parameter in a New-CimInstance command, while passing it into a method that expects a REF array, we should typecast it as ref[].
PS:> # Create an instance with New-CimInstance -ClientOnly : Type cast it to [ref[]] PS:> $inst1 = New-CimInstance –className foo –Namespace root/test –Property @{A= 1; B= 2} –ClientOnly
PS:> $inst2 = New-CimInstance –ClassName foo –Namespace root/test –Property @{A= 3; B= 4} –ClientOnly PS:> $instArray = @($inst1, $inst2) PS:> Invoke-CimMethod –ClassName Class1 –Namespace root/test –Method MethodRefArray –Property @{vals = [ref[]] $instArray}
|
b. If the instances come from Get-CimInstance, and are the same as a server object, then method invocation should be done by typecasting that instance array as Ciminstance[].
PS :> # Get an instance from Get-CimInstance : Typecast it to [Ciminstance[]] PS :> $inst = Get-CimInstance –ClassName foo –Namespace root/test PS :> Invoke-CimMethod –ClassName Class1 –Namespace root/test –Method MethodRefArray –Property @{vals = [Ciminstance[]] $inst}
|
c. The next example shows invoking methods with embedded instances.
PS :> $nums = New-CimInstance –ClassName numbers -Namespace root/test -Property @{numbers = @([int64]5 , [int64]6); count = [uint32]2} –Local
PS :> Invoke-CimMethod -ClassName TestCalculator -MethodName AddNumbers -Arguments @{numbers = $nums} –Namespace root/test
PS :> $nums2 = Get-CimInstance –ClassName numbers -Namespace root/test PS :> Invoke-CimMethod -ClassName TestCalculator -MethodName AddNumbers -Arguments @{numbers = [CimInstance] $nums} -NS root/test
|
Tip#6: Using indications
Receiving indications allows the developer to notify the consuming application that certain system configuration data has changed, without the application having to poll Windows Management Instrumentation (WMI) continuously for this data. From the CIM cmdlet layer, registering for indications is easy. The following script block describes how to register, get, and unsubscribe from indications.
PS :> # Register for an event/indications PS :> Register-CimIndicationEvent -ClassName Test_IndicationClass -Namespace root\test
PS :> # Get all events generated so far PS :> $x = Get-Event
PS :> # Unregister a particular event. PS :> $sourceID = $x[0].SourceIdentifier PS :> Unregister-Event -SourceIdentifier $sourceID
PS :> # Clean up all the events received so far PS :> Remove-Event *
# OR
PS :> # Define your own SID PS:> $sid = “TestSID” PS:> Register-CimIndicationEvent -ClassName Test_IndicationClass -Namespace root\test –SourceIdentifier $sid
PS:> $x = Get-Event –SourceIdentifier $sid PS:> Unregister-Event -SourceIdentifier $sid |
Tip#7: Typecasting/Using DateTime/Using UInt8
While using a hash table as input to the parameters of CIM cmdlets, you might have to typecast data that is being passed. The simple rules here are:
· If the class schema is passed while running the cmdlet, the data type is discovered at run time, so you don’t have to explicitly cast the values.
· If the class schema is not passed, the default data types are used, which can cause failures while running the cmdlets. Typecasting is needed in this scenario.
The following sample code clarifies this:
PS:> Invoke-CimMethod -ClassName TestCalculator -Namespace root/test -MethodName Add –Arguments @{Left = 4; right=5}
Invoke-CimMethod : Type mismatch for parameter "Left“ => If Left and Right are SInt64 values
PS:> # The above failure happened because by default all values are treated as UInt32.
PS:> Invoke-CimMethod -ClassName TestCalculator -Namespace root/test -MethodName Add –Arguments @{Left = [int64]4; right=[int64]5}
PS:> # If the CimClass parameter is used, no typecasting is needed. PS:> $c = Get-CimClass -Namespace root/test -ClassName TestCalculator PS:> Invoke-CimMethod -CimClass $c -MethodName Add -Arguments @{Left = 4; right=5}
|
While using any DateTime type parameter, we must typecast the string representation of DateTime. Similarly, while passing in a value that is UInt8, we must typecast it to Byte.
PS:> $inst = New-CimInstance -ClassName TestClass -Namespace root\test -Key v_uint8 -Property @{ v_uint8 = [byte] 3; v_dateTime = [DateTime] "12/31/1899 3:59:59 PM" } |
Tip#8 Passing operationOptions
There is no way of passing operation Options values to the WMI provider from CIM cmdlets. To pass operationOptions values to your provider, you might have to take the following route:
PS :> $s = New-CimSession
PS :> # Create MethodParameterCollection PS :> $x = New-Object Microsoft.Management.Infrastructure.CimMethodParametersCollection PS :> $param = [Microsoft.Management.Infrastructure.CimMethodParameter]::Create("number", 40, [Microsoft.Management.Infrastructure.cimtype]::UInt64, [Microsoft.Management.Infrastructure.CimFlags]::None) PS :> $x.Add($param)
PS :> Create a OperationOption object Ps :> $operationOption = new-object Microsoft.Management.Infrastructure.Options.CimOperationOptions
PS :> $operationOption.SetCustomOption("TEST_SET_NAMESPACE", $true, $false)
PS :> # Invoke the method PS :> $s.InvokeMethod("root/test", "TestalCalculator", "PrimeFactors", $x, $operationOption) PS :> $returnVal.OutParameters["factors"].Value.CimSystemProperties |
Tip#9 Instances of an AssociationClass only have references to the two ends of the association, and not actual instances
When instances of an association class are enumerated, the properties returned contain the references to the two end points of that association. As these properties are reference-only, only key values are populated.
PS :> Get-CimInstance win32_DiskDriveToDiskPartition Antecedent : Win32_DiskDrive (DeviceID = \\.\PHYSICALDRIVE0) Dependent : Win32_DiskPartition (DeviceID = "Disk #0, Partition #0")
|
Tip#10 Making Get/Enumerate efficient
a. Save a round trip by using -ClientOnly to create instances
If the key values of an instance are known, and the intended operation is to get an instance from the server, then the user can create a ClientOnly instance of a class, and then run Get-CimInstance on it. This saves one round trip to the server.
PS:> $instLocal = New-CimInstance –className foo –Namespace root/test –Property @{A= 1; B= 2} –ClientOnly
PS:> $instFromServer = Get-CimInstance –InputObject $instLocal –CimSession $session
|
b. Only get the key properties.
If the whole instance is not needed, and the intent is only to check the key properties of an instance, the user can get key properties of an instance only, if desired.
PS:> $inst = Get-CimInstance -Class Win32_Process –KeyOnly
PS:> # This can be used to invoke a method PS:> Invoke-CimMethod -InputObject $inst -MethodName "Terminate"
|
c. How to refresh instance data in PowerShell without grabbing the WMI object again.
The original object remains untouched, but the data refreshes.
PS:> # Get instance of a class PS:> $p = Get-CimInstance -ClassName Win32_PerfFormattedData_PerfOS_Processor PS:> # Perform get again by passing the instance received earlier, and get the updated properties. The value of $p remains unchanged. PS:> $p | Get-CimInstance | select PercentProcessorTime |
I hope these tips and tricks will make your experience with CIM cmdlets even better. If you have any questions regarding these cmdlets, please feel free to let us know.
Thanks
Vaibhav Chugh [MSFT]
Standards Based Management