Targeting Down Level Clients with the Get-CimInstance PowerShell Cmdlet
This past weekend, I attended PowerShell Saturday 002 in Charlotte, NC. One of the sessions I attended was "Discovering the Power of WMI & PowerShell in the Enterprise" by Brian Wilhite.
One of the cool things that Brian covered in his session was how to use the new PowerShell version 3
Get-CimInstance
cmdlet to target down level clients that don't have PowerShell installed or don't
have PowerShell remoting enabled.
There's no parameter to change the protocol on the actual Get-CimInstance
or New-CimSession
cmdlets, but it can be added using the the New-CimSessionOption
cmdlet.
1$Opt = New-CimSessionOption -Protocol Dcom
2$CimSession = New-CimSession -ComputerName 'server2', 'server3' -SessionOption $Opt
3$CimSession
I'm able to query a 2003 R2 server that doesn't even have PowerShell installed and a 2008 R2 server that doesn't have PowerShell remoting enabled:
1Get-CimInstance -CimSession $CimSession -ClassName Win32_OperatingSystem -property csname, caption, version |
2select csname, caption, version |
3Format-Table -AutoSize
I've been switching back to the Get-WMIObject
cmdlet for down level clients, but then I have to
use one command for one set of machines and potentially another for the machines that have
PowerShell Remoting enabled if I want to take advantage of it. Brian's session got my wheels turning
and I started thinking "What if I want to query machines that have PowerShell remoting enabled and
down level clients via DCOM in the same command?"
I stored a couple of more CIM Sessions in a different variable using the default settings:
1$CimSession2 = New-CimSession -ComputerName 'www', 'vmhost'
2$CimSession2
You can see all the sessions I have open and what protocol they're using with the Get-CimSession
cmdlet:
1Get-CimSession
The only way I was able to use more than one variable when one or more of them is an array (contains
more than one item) was in a loop. I used the ForEach-Object
cmdlet in this example but that
seemed to be more complicated than it needed to be:
1$CimSession, $CimSession2 |
2ForEach-Object {Get-CimInstance -CimSession $_ -ClassName Win32_OperatingSystem -property csname, caption, version} |
3select csname, caption, version |
4Format-Table -AutoSize
Let's just start over. Removing the sessions and verifying they're removed:
1Get-CimSession | Remove-CimSession
2Get-CimSession
I've created the sessions similar to before except not placing them in a variable at the same time:
1New-CimSession -ComputerName 'server2', 'server3' -SessionOption $Opt
2New-CimSession -ComputerName 'www', 'vmhost'
You can see the sessions just like before by using the Get-CimSession
cmdlet:
1Get-CimSession
I'll place all of them into one variable, both the DCOM and WSMAN ones:
1$CimSession = Get-CimSession
2$CimSession
Now to run a simple one liner which query's all four clients, two with DCOM and two with WSMAN:
1Get-CimInstance -CimSession $CimSession -ClassName Win32_OperatingSystem -property csname, caption, version |
2select csname, caption, version |
3Format-Table -AutoSize
Is that cool or what? No more switching back and forth between Get-WMIObject
and Get-CimInstance
for me.
After thinking about this for a while, my thought was "Why do I even need to store this in a
variable?". I'll just put the Get-CimSession
cmdlet in parenthesis:
1Get-CimInstance -CimSession (Get-CimSession) -ClassName Win32_OperatingSystem -property csname, caption, version |
2select csname, caption, version |
3Format-Table -AutoSize
I took a look at the CimSession parameter of Get-CimInstance
cmdlet to see if it accepts pipeline
input. It does and the Get-CimSession
cmdlet produces a CimSession Object for its output:
1help Get-CimInstance -Parameter CimSession
2Get-CimSession | Get-Member
A lightbulb came on. I can pipe the output of Get-CimSession
to Get-CimInstance
:
1Get-CimSession |
2Get-CimInstance -ClassName Win32_OperatingSystem -property csname, caption, version |
3select csname, caption, version |
4Format-Table -AutoSize
I decided to take one last look at this. If I'm only going to use the variable for setting the
New-CimSessionOption
once, then I can use parenthesis and skip the variable. I double checked and
New-CimSessionOption
can't be piped to New-CimSession
. An additional option I found while
researching the different parameters is that the CIM sessions can be named as shown in this example
where I've used group1 for the first set of servers and group2 for the second set of servers:
1New-CimSession -ComputerName 'server2', 'server3' -SessionOption (New-CimSessionOption -Protocol Dcom) -Name group1
2New-CimSession -ComputerName 'www', 'vmhost' -Name group2
This way I can easily run commands against all of the servers that I've named group# by specifying group*. This is helpful if other CIM sessions besides the ones named group# exist.
1Get-CimSession -Name group* |
2Get-CimInstance -ClassName Win32_OperatingSystem -property csname, caption, version |
3select csname, caption, version |
4Format-Table -AutoSize
Or I can run commands against each group of servers individually:
1Get-CimSession -Name group1 |
2Get-CimInstance -ClassName Win32_OperatingSystem -property csname, caption, version |
3select csname, caption, version |
4Format-Table -AutoSize
5
6Get-CimSession -Name group2 |
7Get-CimInstance -ClassName Win32_OperatingSystem -property csname, caption, version |
8select csname, caption, version |
9Format-Table -AutoSize
If I have half a dozen different groups of servers defined as group1 through 6, I can pick and chose the different groups of servers as shown below instead of having to pick all the individual CIM sessions:
1Get-CimSession -Name group1, group2 |
2Get-CimInstance -ClassName Win32_OperatingSystem -property csname, caption, version |
3select csname, caption, version |
4Format-Table -AutoSize
µ