PowerShell CIM Sessions: Efficient Multi-Server Queries
─□✕

PowerShell CIM Sessions: Efficient Multi-Server Queries

PowerShell Tips Editor 5 min read
PowerShell CIM Sessions: Efficient Multi-Server Queries

The Cost of Ad-Hoc CIM Connections

Every time you run Get-CimInstance -ComputerName server01 without a pre-established session, PowerShell negotiates a new WSMan or DCOM connection from scratch. Across a handful of servers that overhead is tolerable. At 50 or 100 servers, each query adding 300–800ms of connection setup, your inventory script that should finish in seconds starts taking minutes. CimSession objects persist the connection so that subsequent queries reuse it, eliminating repeated negotiation overhead for every cmdlet call.

Quick Answer

Create persistent connections with New-CimSession, pass the resulting objects to Get-CimInstance via the -CimSession parameter for all subsequent queries, and clean up with Remove-CimSession when done. For hosts that only speak WMI/DCOM, supply a New-CimSessionOption with -Protocol Dcom.

Creating CimSessions with New-CimSession

New-CimSession accepts an array of computer names and opens sessions to all of them. Failed connections throw errors immediately rather than silently, so wrapping in -ErrorAction Stop inside a try-catch block lets you track which hosts are unreachable and continue with the rest. Store the sessions in a variable and reuse it for every subsequent query.

$servers = @("SRV01","SRV02","SRV03","SRV04","SRV05")

$sessions = @()
foreach ($srv in $servers) {
    try {
        $s = New-CimSession -ComputerName $srv -ErrorAction Stop
        $sessions += $s
        Write-Host "Connected: $srv"
    }
    catch {
        Write-Warning "Could not connect to ${srv}: $_"
    }
}

Write-Host "Active sessions: $($sessions.Count)"

Using CimSessions Across Multiple Get-CimInstance Calls

The performance benefit becomes clear when you run several queries against the same session array. Each additional Get-CimInstance call reuses the established connections without any new negotiation. This pattern is especially valuable in inventory scripts that collect OS info, disk space, services, and installed software in a single pass.

# All three queries reuse the same connections
$os = Get-CimInstance -CimSession $sessions -ClassName Win32_OperatingSystem
$disk = Get-CimInstance -CimSession $sessions -ClassName Win32_LogicalDisk `
    -Filter "DriveType = 3"
$services = Get-CimInstance -CimSession $sessions -ClassName Win32_Service `
    -Filter "State = 'Running'"

$os | Select-Object PSComputerName, Caption, LastBootUpTime | Format-Table -AutoSize
$disk | Select-Object PSComputerName, DeviceID,
    @{N="FreeGB"; E={ [math]::Round($_.FreeSpace/1GB,1) }} | Format-Table -AutoSize

Session Options: DCOM vs WSMan Protocol Selection

By default, New-CimSession uses WSMan (WinRM) on port 5985. This works for all modern Windows versions with WinRM enabled. For older servers, DMZ hosts, or workgroup machines where WinRM is not configured, you can force the DCOM protocol instead. Create a CimSessionOption object and pass it to New-CimSession with the -SessionOption parameter.

# WSMan (default, modern hosts)
$wsmanOption = New-CimSessionOption -Protocol WSMan
$modernSession = New-CimSession -ComputerName "SRV-NEW" `
    -SessionOption $wsmanOption -ErrorAction Stop

# DCOM (legacy hosts, no WinRM)
$dcomOption = New-CimSessionOption -Protocol Dcom
$legacySession = New-CimSession -ComputerName "SRV-OLD" `
    -SessionOption $dcomOption -ErrorAction Stop

Write-Host "Modern session protocol: $($modernSession.Protocol)"
Write-Host "Legacy session protocol: $($legacySession.Protocol)"

Connecting to Hosts That Only Support WMI/DCOM

Windows Server 2003 and some specialty appliances do not support WSMan at all. For those hosts, DCOM is the only option. DCOM requires TCP port 135 plus dynamic high ports (49152–65535), so firewall rules are a prerequisite. Once connected, the CimSession object provides the same API as WSMan sessions — the protocol difference is transparent to the querying cmdlets.

$legacyServers = @("LEGACY01","LEGACY02")
$dcomOpt = New-CimSessionOption -Protocol Dcom

$legacySessions = foreach ($srv in $legacyServers) {
    try {
        New-CimSession -ComputerName $srv -SessionOption $dcomOpt -ErrorAction Stop
    }
    catch {
        Write-Warning "DCOM connect failed for ${srv}: $_"
    }
}

# Query works identically regardless of protocol
Get-CimInstance -CimSession $legacySessions -ClassName Win32_BIOS |
    Select-Object PSComputerName, SerialNumber, Manufacturer

Handling Session Failures and Reconnecting

Sessions to remote hosts can become stale if the target reboots or experiences a network interruption. Checking the TestConnection state of a session before querying it prevents cryptic errors mid-script. The pattern below tests each session and attempts a single reconnect before marking a host as failed.

function Test-CimSessionAlive {
    param([Microsoft.Management.Infrastructure.CimSession]$Session)
    try {
        $null = Get-CimInstance -CimSession $Session `
            -ClassName Win32_ComputerSystem -ErrorAction Stop
        return $true
    }
    catch { return $false }
}

$liveSessions = $sessions | Where-Object { Test-CimSessionAlive $_ }
Write-Host "Live sessions: $($liveSessions.Count) of $($sessions.Count)"

Cleaning Up with Remove-CimSession

CimSession objects hold remote resources. Always call Remove-CimSession at the end of your script, or use a try-finally block to guarantee cleanup even when the script encounters errors. Leaving sessions open wastes connection slots on the target server and can hit WinRM connection limits in large environments.

try {
    # ... your query work here ...
    $os = Get-CimInstance -CimSession $sessions -ClassName Win32_OperatingSystem
    $os | Select-Object PSComputerName, Caption | Export-Csv "os_report.csv" -NoTypeInformation
}
finally {
    Remove-CimSession -CimSession $sessions
    Write-Host "All CIM sessions closed."
}

Common Errors

  • WSMan protocol fails on older Windows versions. Windows Server 2003 and XP do not have WinRM installed. The error “WS-Management cannot complete the operation” is the giveaway. Switch to -Protocol Dcom via New-CimSessionOption for those hosts rather than falling back to the deprecated Get-WmiObject.
  • CimSession to DMZ hosts fails with a firewall error. WSMan uses TCP 5985 (HTTP) or 5986 (HTTPS). DMZ firewall rules often block both. Either open the required port for your management server’s IP, or use DCOM which uses port 135 plus dynamic RPC ports — also blocked in strict DMZ configurations. Confirm firewall rules before troubleshooting the PowerShell side.

Related Cmdlets / See Also

Wrapping Up

CimSession objects eliminate per-query connection overhead and pay dividends immediately when querying more than five or six servers. Create sessions once, query multiple classes across them, handle protocol differences with session options, and always clean up in a finally block. The investment in this pattern makes large-fleet inventory scripts dramatically faster and more reliable.

Send-Item -To