Automating the world one-liner at a time…
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
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.
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
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:> # If using HTTPS.
PS:> $sop = New-CimSessionOption –UseSSL
PS:> $session = New-CimSession –CN “machineName” -Credentials $creds –Port $httpsPort
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
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
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 *
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
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" }
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
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")
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