CimSession PowerShell Scripting
Last week, I was working on updates to the PSScriptTools module. I am adding new functions based on Get-CimClass
. As I was working, I ended up diving deeper into how the CIM cmdlets work and realized I could write more efficient functions with the results of my research and testing.
Normally, whenever I write a PowerShell function that wraps around Get-CimInstance
, I always try to have parameter sets so that the user can specify a computer name or an existing CIMSession.
Function Get-CimOS {
[CmdletBinding(DefaultParameterSetName = 'ClassNameComputerSet')]
Param(
[Parameter(
Position = 0,
ParameterSetName = 'CimInstanceSessionSet',
Mandatory,
ValueFromPipeline
)]
[Microsoft.Management.Infrastructure.CimSession[]]$CimSession,
[Parameter(
Position = 0,
ParameterSetName = 'ClassNameComputerSet',
ValueFromPipeline
)]
[Alias('CN', 'ServerName')]
[string[]]$ComputerName = $env:Computername,
[Alias('OT')]
[uint32]$OperationTimeoutSec
)
Begin {
Write-Verbose "Starting $($MyInvocation.MyCommand)"
$PSBoundParameters.Add('Namespace', 'Root\CimV2')
$PSBoundParameters.Add('ClassName', 'Win32_OperatingSystem')
$PSBoundParameters.Add('ErrorAction', 'Stop')
$PSBoundParameters.Add('Property', @('CSName','Caption','Version','BuildNumber','InstallDate','OSArchitecture'))
Write-Verbose ($PSBoundParameters | Out-String)
} #begin
Process {
Write-Verbose "Using parameter set $($PSCmdlet.ParameterSetName)"
Try {
Get-CimInstance @PSBoundParameters |
Select-Object @{Name = 'Computername'; Expression = { $_.CSName } },
@{Name = 'Name'; Expression = { $_.Caption } },
Version, BuildNumber,InstallDate, OSArchitecture
}
Catch {
Write-Warning $_.Exception.Message
}
} #process
End {
Write-Verbose "Ending $($MyInvocation.MyCommand)"
} #end
} #end function Get-CimOS
I like having the flexibility.
PS C:\> Get-CimOS
Computername : PROSPERO
Name : Microsoft Windows 11 Pro
Version : 10.0.22631
BuildNumber : 22631
InstallDate : 5/12/2022 9:01:53 AM
OSArchitecture : 64-bit
PS C:\> Get-CimSession -ComputerName dom1 | Get-CimOS
Computername : DOM1
Name : Microsoft Windows Server 2019 Standard Evaluation
Version : 10.0.17763
BuildNumber : 17763
InstallDate : 3/27/2024 11:21:54 AM
OSArchitecture : 64-bit
Sometimes, I'll include a parameter for the ComputerName
parameter set to allow the user to specify a credential. In the function, I then create a CIMSession. Internally, the function then always uses the CIMSession. Either the one passed by the user or the one created by the function. This means extra work as I also have to clean up the temporary CIMSession.
However, other native PowerShell cmdlets also take this approach. Consider the syntax for Get-Volume
. This command has a parameter that allows you to specify a computer name or CIMSession.

I always wondered how this worked, and after some digging, I have a better understanding. While I typically encourage PowerShell scripters to use cmdlets wherever possible and avoid using the .NET Framework directly, there are performance benefits with the latter. And if you subscribe to this newsletter, I'm assuming you are looking for ways to take that next step. Let's see what we can do.
Microsoft.Management.Infrastructure.CimSession
If we're going to get down and dirty, this means using the Microsoft.Management.Infrastructure.CimSession
class. Let's take a peek at it with my Get-TypeMember
function.
PS C:\> Get-TypeMember Microsoft.Management.Infrastructure.CimSession
Type: Microsoft.Management.Infrastructure.CimSession
Name MemberType ResultType IsStatic
---- ---------- ---------- --------
Close Method Void
CloseAsync Method CimAsyncStatus
Create Method CimSession True
CreateAsync Method CimAsyncResult`1 True
CreateInstance Method CimInstance
CreateInstanceAsync Method CimAsyncResult`1
DeleteInstance Method Void
DeleteInstanceAsync Method CimAsyncStatus
EnumerateAssociatedInstances Method IEnumerable`1
EnumerateAssociatedInstancesAsync Method CimAsyncMultipleResults`1
EnumerateClasses Method IEnumerable`1
EnumerateClassesAsync Method CimAsyncMultipleResults`1
EnumerateInstances Method IEnumerable`1
EnumerateInstancesAsync Method CimAsyncMultipleResults`1
EnumerateReferencingInstances Method IEnumerable`1
EnumerateReferencingInstancesAsync Method CimAsyncMultipleResults`1
GetClass Method CimClass
GetClassAsync Method CimAsyncResult`1
GetInstance Method CimInstance
GetInstanceAsync Method CimAsyncResult`1
GetType Method Type
InvokeMethod Method CimMethodResult
InvokeMethodAsync Method CimAsyncResult`1
InvokeMethodAsync Method CimAsyncMultipleResults`1
ModifyInstance Method CimInstance
ModifyInstanceAsync Method CimAsyncResult`1
QueryInstances Method IEnumerable`1
QueryInstancesAsync Method CimAsyncMultipleResults`1
Subscribe Method IEnumerable`1
SubscribeAsync Method CimAsyncMultipleResults`1
TestConnection Method Boolean
TestConnectionAsync Method CimAsyncResult`1
ComputerName Property String
InstanceId Property Guid
You can probably already identify some methods of interest.
Creating an Instance
This class doesn't have a New()
constructor. But there is a Create()
static method.
PS C:\> [Microsoft.Management.Infrastructure.CimSession]::create.OverloadDefinitions
static CimSession Create(string computerName)
static CimSession Create(string computerName, Microsoft.Management.Infrastructure.Options.CimSessionOptions sessionOptions)
This is pretty simple.
$cs = [Microsoft.Management.Infrastructure.CimSession]::create("DOM1")
You could also define an instance like this:
PS C:\> [Microsoft.Management.Infrastructure.CimSession]"DOM1"
ComputerName InstanceId
------------ ----------
DOM1 1be319ed-c242-4820-a701-188502b5a6bc
This object is not an actual connection. You won't know if it is valid until you use it. The instance uses your credential as the default.
CimSessionOptions
You may have noticed another creation definition using CimSessionOptions
. What's that?
PS C:\> Get-TypeMember Microsoft.Management.Infrastructure.Options.CimSessionOptions
Type: Microsoft.Management.Infrastructure.Options.CimSessionOptions
Name MemberType ResultType IsStatic IsEnum
---- ---------- ---------- -------- ------
AddDestinationCredentials Method Void
GetType Method Type
SetCustomOption Method Void
Culture Property CultureInfo
Timeout Property TimeSpan
UICulture Property CultureInfo
We might as well explore this. This class does have a New()
constructor.
PS C:\> [Microsoft.Management.Infrastructure.Options.CimSessionOptions]::new
OverloadDefinitions
-------------------
Microsoft.Management.Infrastructure.Options.CimSessionOptions new()
Let's create one.
PS C:\> $cso = [Microsoft.Management.Infrastructure.Options.CimSessionOptions]::new()
PS C:\> $cso
Timeout Culture UICulture
------- ------- ---------
00:00:00 en-US en-US
We need to look at that AddDestinationCredentials()
method.
PS C:\> Get-TypeMember Microsoft.Management.Infrastructure.Options.CimSessionOptions -MemberName AddDestinationCredentials | Select-Object Syntax
Syntax
------
$obj.AddDestinationCredentials([CimCredential]credential)
Or I can look at the method on my sample object.
PS C:\> $cso.AddDestinationCredentials.OverloadDefinitions
void AddDestinationCredentials(Microsoft.Management.Infrastructure.Options.CimCredential credential)
Ok. This is referencing another .NET type, a CimCredential
. There don't appear to be any properties or static methods.
PS C:\> Get-TypeMember Microsoft.Management.Infrastructure.Options.CimCredential
Type: Microsoft.Management.Infrastructure.Options.CimCredential
Name MemberType ResultType IsStatic IsEnum
---- ---------- ---------- -------- ------
GetType Method Type
Let's check for a New()
constructor.

I hope you are detecting a development pattern here. The same process and techniques I'm using to dig into this .NET class can be applied to any class.
Ok. I see authenticationMechanism
as a parameter. What is that? The output doesn't show me a full .NET type name, but usually PowerShell can help. At a prompt start typing the class name inside an open [
.
PS C:\> [authenticationmechanism
Now let the PSReadLine module autocomplete. Press Ctrl+Space
If there are multiple entries, you'll have to make an educated guess or use trial-and-error. In this case, there is only a single completion.
PS C:\> [System.Management.Automation.Runspaces.AuthenticationMechanism
I can add a closing ]
and press Enter to see that is an Enum
. This means the credential constructor wants a value from this class.

Those values probably look familiar. If you look run help New-CimSession -Parameter Authentication
you'll see the same values. But now you know where they come from and how they will be used.
If I want to re-invent the wheel, and sometimes that's ok, I can build a function that wraps around this definition.
Microsoft.Management.Infrastructure.Options.CimCredential new(string authenticationMechanism, string domain, string userName, securestring password)
If I want to specify a credential, I need to create the CimSessionOption
object, add the credential, and then include the option object when I create the CimSession
.
Function New-jhCimSession {
[CmdletBinding()]
Param(
[Parameter(
Position = 0,
ValueFromPipeline,
HelpMessage = 'Enter the name of the remote computer to connect to'
)]
[Alias('CN', 'ServerName')]
[ValidateNotNullOrEmpty()]
[string]$ComputerName = $env:COMPUTERNAME,
[Parameter(HelpMessage = 'Enter an alternate credential for the remote computer')]
[ValidateNotNullOrEmpty()]
[System.Management.Automation.PSCredential]$Credential,
[ValidateNotNullOrEmpty()]
[System.Management.Automation.Runspaces.AuthenticationMechanism]$Authentication = 'Default'
)
if ($Credential) {
$cso = [Microsoft.Management.Infrastructure.Options.CimSessionOptions]::new()
$domain = $Credential.GetNetworkCredential().Domain
$user = $Credential.GetNetworkCredential().UserName
$csCredential = [Microsoft.Management.Infrastructure.Options.CimCredential]::new($Authentication, $domain, $user, $Credential.Password)
$cso.AddDestinationCredentials($csCredential)
$cs = [Microsoft.Management.Infrastructure.CimSession]::Create($ComputerName, $cso)
}
else {
$cs = [Microsoft.Management.Infrastructure.CimSession]::Create($ComputerName)
}
#write the CIMSession object to the pipeline
$cs
}
I now have an alternate way to create a CimSession
object.
PS C:\> $d = New-jhCimSession dom1 -Credential company\artd
PowerShell credential request
Enter your credentials.
Password for user company\artd: ********
PS C:\> $d
ComputerName InstanceId
------------ ----------
dom1 57c69b89-8d56-4ddb-9cbe-75f604328db9
Creating this object does not create a standard CimSession
. If I run Get-CimSession
I won't see anything related to DOM1
.
Testing the Connection
There's nothing in the object that indicates the presence of an alternate credential. However, I can test it.
PS C:\> $d.TestConnection()
True
This is handy.
PS C:\> $foo = New-jhCimSession -ComputerName Foo
PS C:\> $foo.TestConnection()
False
I can test a remote connection quicker than trying to run Get-CimInstance
and waiting for it to fail.
With this in mind, I can update my custom function and add a parameter to validate the object.
Function New-jhCimSession {
[CmdletBinding()]
Param(
[Parameter(
Position = 0,
ValueFromPipeline,
HelpMessage = 'Enter the name of the remote computer to connect to'
)]
[Alias('CN', 'ServerName')]
[ValidateNotNullOrEmpty()]
[string]$ComputerName = $env:COMPUTERNAME,
[Parameter(HelpMessage = 'Enter an alternate credential for the remote computer')]
[ValidateNotNullOrEmpty()]
[System.Management.Automation.PSCredential]$Credential,
[ValidateNotNullOrEmpty()]
[System.Management.Automation.Runspaces.AuthenticationMechanism]$Authentication = 'Default',
[Switch]$Validate
)
if ($Credential) {
$cso = [Microsoft.Management.Infrastructure.Options.CimSessionOptions]::new()
$domain = $Credential.GetNetworkCredential().Domain
$user = $Credential.GetNetworkCredential().UserName
$csCredential = [Microsoft.Management.Infrastructure.Options.CimCredential]::new($Authentication, $domain, $user, $Credential.Password)
$cso.AddDestinationCredentials($csCredential)
$cs = [Microsoft.Management.Infrastructure.CimSession]::Create($ComputerName, $cso)
}
else {
$cs = [Microsoft.Management.Infrastructure.CimSession]::Create($ComputerName)
}
#write the CIMSession object to the pipeline if it passes the test
if ($cs.TestConnection()) {
$cs
}
else {
Write-Warning "Connection to $($ComputerName.ToUpper()) failed"
#remove the CIMSession object
$cs.Dispose()
}
}
This might be useful.
PS C:\> New-jhCimSession Srv4 -Validate | Tee -variable s4
WARNING: Connection to SRV4 failed
PS C:\> New-jhCimSession Srv3 -Validate | Tee -variable s3
ComputerName InstanceId
------------ ----------
Srv3 b47a30f5-53ff-4324-bd29-05a20b7d4e0a
PS C:\>
Using the CimSession
Once I have a valid CimSession
object, one that I created with my .NET code, I can use it the same way I would an object created with New-CimSession
.
PS C:\> $cs | Get-CimInstance win32_operatingSystem | Select-Object PSComputername,Caption
PSComputerName Caption
-------------- -------
DOM1 Microsoft Windows Server 2019 Standard Evaluation
PS C:\> Get-Volume -CimSession $cs -DriveLetter c | format-list
ObjectId : {1}\\DOM1\root/Microsoft/Windows/Storage/Providers_v2\WSP_Volume.ObjectId="{d1c57349-ec6e-11ee-
83fc-806e6f6e6963}:VO:\\?\Volume{62e20fe4-3b7e-4f80-be03-c9a0254dec96}\"
PassThroughClass :
PassThroughIds :
PassThroughNamespace :
PassThroughServer :
UniqueId : \\?\Volume{62e20fe4-3b7e-4f80-be03-c9a0254dec96}\
AllocationUnitSize : 4096
DedupMode : NotAvailable
DriveLetter : C
DriveType : Fixed
FileSystem : NTFS
FileSystemLabel :
FileSystemType : NTFS
HealthStatus : Healthy
OperationalStatus : OK
Path : \\?\Volume{62e20fe4-3b7e-4f80-be03-c9a0254dec96}\
Size : 135951020032
SizeRemaining : 117020053504
PSComputerName : DOM1
Summary
I'm just getting started. So far, I've avoided cmdlets and created custom tooling to setup a CIMSession. Using cmdlets always incurs an overhead cost, which we normally are willing to pay for the convenience and benefits of a cmdlet. However, there may be times when we want to take matters into our own hands and build customized tooling that meets our requirements.
Next time, I'll show you how I incorporate my custom CimSession
objects to query CIM classes without relying on Get-CimInstance
.