Search
Generic filters
Exact matches only
Filter by Custom Post Type

Making Awesome Dashboards from Windows Performance Counters

Having an understanding of your systems performance is a crucial part of running IT infrastructure.

If a user comes to us and says "why is my application running slowly?", where do we start? Is it their machine? Is it the database server? Is it the file server?

The first thing we usually do is open up perfmon.exe and take a look at some performance counters. You then see the CPU on the database server is 100% and think  "was the CPU always at 100% or did this issue just start today? Was it something I changed? If only I could see what was happening at this time yesterday when the application was running fine!". It might take you a few hours to find the performance issue on your infrastructure, and you are probably going to need to open up perfmon.exe on a couple of other systems. There is a better way!

What if you could turn your Windows performance counters into dashboards that look like this? How much time would you save?

Full Hyper-V Dashboard

Using a combination of the open source tools InfluxDB to store the performance counter data, Grafana to graph the data and the Telegraf agent to collect Windows performance counters, you will be a master of your metrics in no time!

Read the detailed walk through over at hodgkins.io

 

 

PowerShell Great Debate: "Fixing" Output

Uncategorized
Aug 27, 2013
4

When should a script (or more likely, function) output raw data, and when should it "massage" its output?

The classic example is something like disk space. You're querying WMI, and it's giving you disk space in bytes. Nobody cares about bytes. Should your function output bytes anyway, or output megabytes or gigabytes?

If you output raw data, how would you expect a user to get a more-useful version? Would you expect someone running your command to use Select-Object on their own to do the math, or would you perhaps provide a default formatting view (a la what Get-Process does) that manages the math?

The "Microsoft Way" is to use a default view - again, it's what Get-Process does. But views are separate files, and they're only really practical (many say) when they're part of a module that can auto-load them.

What do you think?

[boilerplate greatdebate]

Some Event 3 Notes

Uncategorized
May 18, 2013
3

I didn't see anyone (although I'll admit I haven't checked every entry) using my EnhancedHTML module from Creating HTML Reports in PowerShell. I am ensaddened.

But man, Event 3 shows that you can really do well by learning a wee bit of HTML. Knowing an H2 and HR tag makes for much pretty results. Take it as career advice.

As a nitpick, don't use Convert as a function verb unless all the function is going to do is convert something. It shouldn't "Get" as well. That said, because this event wants a single function that both gets and converts... which is something I'd ordinarily avoid packing into one function... no big. It's interesting to see the function names folks picked out.

Folks, test your scripts. Seriously.

I kinda giggled when I saw this comment in an entry:

# I'd like to Splat this but I don't know how / ran out of time

Heh. In general, this is like a cooking show. If you know your food doesn't taste good, don't bring it to the judges. And if you do bring it to them, don't tell them all the cool toppings you were going to add. Just give them what you made.

Note to self: Don't write scenarios that require HTML. It messes up the Scripting Games Web site. Duh.

You know, overall, I'm seeing good stuff. I ran through some of the low-scoring entries and didn't see anything that didn't deserve a lowered score. If you constructed your own HTML instead of using ConvertTo-HTML, you pretty much got universally dinged, and I can understand and support that philosophy.

Oh, and ConvertTo-HTML doesn't output to stdout, whoever wrote that. It writes to the pipeline. Big difference.

Folks, when using Get-WmiObject, use the -Filter parameter. Don't get everything and pipe it to Where-Object. Get the filtering done early - this is a huge performance concept.

This was interesting:

[Parameter(Mandatory=$True,ValueFromPipeline=$True)]
            [ValidateScript({Test-Connection -ComputerName $_ -Quiet -Count 2})]
            [STRING[]]$ComputerName,

I'm entirely unsure how I feel about this. I like the idea. I keep telling people than a Ping doesn't really tell you anything when you're about to use WMI, though. If the computer responds, WMI might still fail; if the computer doesn't respond, WMI might still succeed. A ping is not useful diagnostic information for WMI connections. I understand the desire to try and eliminate the WMI timeout, but you're not doing so. What if I block ICMP traffic but not WMI traffic - a very common thing at a lot of my clients? Just bear that in mind.

We're done with Hungarian notation ($objDisk, $strComputer). Time to move on.

Commenters: Dudes, you need to read. For example, this:

When localhost, an IP address, or an alias is provided, the actual computer name is not displayed on the web page and the file name is also incorrect. Consider using one of the properties from the WMI class that has the computer name instead of what the user provided on input.

Was next to a 1-star vote. Totally inappropriate. 1 star, as the voting page clearly indicates, is when the script is totally non-functional. "Bad entry - does not function at all" is what it says under 1 star. This comment was on a working script. Maybe it didn't fulfill every requirement, but seriously, you'd fire a guy whose script simply put an IP address instead of a computer name? No. This was a 3-star script according to the guidelines.

Anyway... things are looking great. On to Event 4, which opens for voting real soon!

CIM vs WMI cmdlets-remote execution speed

Uncategorized
Apr 29, 2013
0

Following on from my previous post we’ll look at how the two types of cmdlets compare for accessing remote machines.

I used a similar format to the previous tests but was accessing a remote machine.

First off was the WMI cmdlet – using DCOM to access the remote Windows 2012 server

PS> 1..100 |
foreach {
Measure-Command -Expression{1..100 | foreach {Get-WmiObject -Class Win32_ComputerSystem -ComputerName W12SUS }}
} |
Measure-Object -Average TotalMilliseconds

Count    : 100
Average  : 2084.122547
Sum      :
Maximum  :
Minimum  :
Property : TotalMilliseconds

 

The CIM cmdlets are similar but apparently a bit slower – probably due to having to build the WSMAN connection and teat it down each time.

PS> 1..100 |
foreach {
Measure-Command -Expression{1..100 | foreach {Get-CimInstance -ClassName Win32_ComputerSystem -ComputerName W12SUS }}
} |
Measure-Object -Average TotalMilliseconds

Count    : 100
Average  : 2627.287458
Sum      :
Maximum  :
Minimum  :
Property : TotalMilliseconds

 

So what happens is you run the CIM command over a CIM session?

PS> $sess = New-CimSession -ComputerName W12SUS
PS> 1..100 |
foreach {
Measure-Command -Expression{1..100 | foreach {Get-CimInstance -ClassName Win32_ComputerSystem -CimSession $sess }}
} |
Measure-Object -Average TotalMilliseconds

Count    : 100
Average  : 877.746649999999
Sum      :
Maximum  :
Minimum  :
Property : TotalMilliseconds

This removes the setup and tear-down of the WSMAN connection. It suggests that the actual retrieval time for the CIM cmdlets should be reduced to 1749.540808 milliseconds for 100 accesses which is faster than the WMI cmdlets

It looks like the fastest way to access WMI information is across a CIM session. Next time we’ll look at running multiple commands

CIM cmdlets vs WMI cmdlets–speed of execution

Uncategorized
Apr 28, 2013
0

One question that came up at the summit was the comparative speed of execution of the new CIM cmdlets vs the old WMI cmdlets.  No of us knew the answer because we’d never tried measuring the speed.

I decided to perform some tests.

This first test is accessing the local machine.  In both cases the cmdlets are using COM.  WMI uses COM and CIM will use COM if a –ComputerName parameter isn’t used.

The results are as follows:

PS> 1..100 |
foreach {Measure-Command -Expression {
1..100 | foreach {Get-WmiObject -Class Win32_ComputerSystem} }
} | Measure-Object -Average TotalMilliseconds

Count    : 100
Average  : 2008.953978
Sum      :
Maximum  :
Minimum  :
Property : TotalMilliseconds

 

PS> 1..100 |
foreach {Measure-Command -Expression {
1..100 | foreach {Get-CimInstance -ClassName Win32_ComputerSystem} }
} | Measure-Object -Average TotalMilliseconds

Count    : 100
Average  : 2078.763174
Sum      :
Maximum  :
Minimum  :
Property : TotalMilliseconds

 

So for pure COM access the WMI cmdlets are marginally (3.4%) faster.

What if we use the ComputerName parameter?

PS> 1..100 |
foreach {
Measure-Command -Expression {
1..100 | foreach {Get-WmiObject -Class Win32_ComputerSystem -ComputerName $env:COMPUTERNAME } }
} | Measure-Object -Average TotalMilliseconds

Count    : 100
Average  : 1499.14379
Sum      :
Maximum  :
Minimum  :
Property : TotalMilliseconds

PS> 1..100 |
foreach {
Measure-Command -Expression {
1..100 | foreach {Get-CimInstance -ClassName Win32_ComputerSystem -ComputerName $env:COMPUTERNAME } }
} | Measure-Object -Average TotalMilliseconds

Count    : 100
Average  : 3892.921851
Sum      :
Maximum  :
Minimum  :
Property : TotalMilliseconds

This one surprised me – the WMI cmdlets are 2.5 times faster.  I suspect that is because the CIM cmdlet has to build and then breakdown the WSMAN connection each time.

Next time we’ll look at accessing a remote machine.

Use PowerShell to Modify WMI Data Such as Drive Labels

Uncategorized
Apr 20, 2013
0

Summary: The Scripting Wife learns how to use the CIM cmdlets and Windows PowerShell to assign a new drive label by modifying WMI data.

Weekend Scripter: Changing WMI information

Microsoft Scripting Guy, Ed Wilson, is here. Well yesterday, after we got through an extremely long security line at the airport, and we jiggled around in a very cramped airplane for over five hours on our flight to Seattle for the Windows PowerShell summit, we met up with Windows PowerShell MVP Jeff Wouters at the SEATAC airport. We gave him a ride into Redmond and spent some time hanging out. Here is a picture of Jeff and the Scripting Wife I took near the Seattle Space Needle. You see, Jeff has never been to Seattle, and so we thought we would show him around.

clip_image002

The Windows PowerShell summit has already been a success, in my mind, and the first session has not even kicked off. Of course, the biggest thing for the summit for me is not the sessions, but the chance to talk and to interact with members of the community. In this regard, the summit has already exceeded expectations.

I decided to head down to the lobby of the hotel to grab something to eat this morning. It is still very early for people from the West Coast, but for the Scripting Wife and I it is already late due to the three time zone change for us. Of course, for our European friends the change is twice as great. I am sipping a cup of English Breakfast tea, munching on a croissant, and checking my email, when a loud thudding sound accompanied by a sudden shaking of the table woke me from my revelry–it is the Scripting Wife.

I hazard a query, “Why are you are up so early?”

“Hey, it is nearly noon back in Charlotte–I am ready to go,” she chirped.

“Go? Go where?” I ventured.

“Out. We are getting together a group, and we are heading out,” she explained.

“Where is out? Have you no specific destination in mind?” I asked.

“Well, maybe downtown Seattle. There is great shopping there, a cool aquarium, ferry terminals, and wonderful restaurants. We are in a real city my friend, and I for one am not going to let the opportunity pass,” she pontificated.

“I can appreciate that. But before you go, let me show you something you might need to know for the 2013 Scripting Games,” I said.

“OK, but make it quick, because when the group shows up, I am out of here,” she said.

Using the CIM cmdlets to set a WMI property

I opened the Windows PowerShell console with admin rights by right clicking the Windows PowerShell console icon and selecting Run As Administrator from the action menu. Admin rights are required to modify WMI objects.

“OK. Look over here,” I directed. “Find WMI classes related to disks.”

Find disk classes

The Scripting Wife thought for a few minutes, and typed the following.

Get-Cimc<tab><space>*disk*<enter>

The command she typed appears here.

Get-CimClass *disk*

Get dynamic classes

“Now, narrow your output to only dynamic WMI classes,” I instructed.

She used the Up arrow to retrieve the previous command and she added the Dynamic qualifier to her command. Here is what she typed:

<up arrow><space>-q<tab>dynamic<enter>

The command looks like this when complete.

Get-CimClass *disk* -QualifierName dynamic

Get logical disk info

“Cool. Now what we want to do is to find out information about drive C. To do this use the Get-CimInstance cmdlet and retrieve information about the Win32_LogicalDisk WMI class,” I said.

The Scripting Wife thought for a second, and then she began to type. Here is what she did:

Get-CimI<tab><space>Win32_LogicalDisk<enter>

The command looks like the following:

Get-CimInstance win32_logicaldisk

“OK, now look at the output and see if you can tell the property that indicates that it references the drive names,” I said.

She looked at the laptop screen. The output looks like the following:

PS C:\> Get-CimInstance win32_logicaldisk

DeviceID DriveType ProviderName VolumeName Size FreeSpace

-------- --------- ------------ ---------- ---- ---------

C: 3 159486308352 10707682...

D: 3 System Reserved 366997504 113971200

F: 3 ExtraDisk 499738734592 43558884...

Narrow down the drives by letter

“So you did a good job. You found information about all of the logical disks. What I want you to do now, is to return only drive C . To do that you will need to use the Filter property,” I said.

It did not take the Scripting Wife long at all before she used the Up arrow to retrieve her previous command and to add the Filter parameter. Here is what she typed.

<up arrow><space>-f<tab><space>’DeviceID=’c:’”<enter>

Here is the command she created.

Get-CimInstance win32_logicaldisk -Filter "deviceid='c:'"

The command and output from the command are shown here:

PS C:\> Get-CimInstance win32_logicaldisk -Filter "deviceid='c:'"

DeviceID DriveType ProviderName VolumeName Size FreeSpace

-------- --------- ------------ ---------- ---- ---------

C: 3 159486308352 10707628...

Setting a WMI property

“Notice that you have no label, or VolumeName,” I said. “To set that you will use the Set-CimInstance cmdlet and use a hash table for the Property parameter,” I said.

“Huh?” she asked. “You know that I do not speak geek.”

“It makes more sense when you just do it,” I said.

“Well, hurry up. I just got an email on my phone and they will be here in a few minutes,” she said.

“OK. First of all, you need to use the Up arrow and retrieve your previous command. Then you need to pipe the command to the Set-CimInstance cmdlet. Do that, but do not press ENTER because there is more to the command than that,” I said.

The Scripting Wife thought for a few minutes, and then began typing. Here is what she typed.

<Up Arrow><space>Set-Cimi<tab>

She paused.

“OK, now you need to add the Property parameter and an ampersand and an opening and a closing curly bracket. Do not press ENTER,” I said.

This time, she did not hesitate. Here is what she typed:

-p<tab><space>@{}

“Good. Now inside the two curly brackets you add your 'property name equals new value' statement. Here, you are going to set a value for the VolumeName. Give it a value of SSD_Drive,” I said.

Here is what she typed:

volumename='SSD_Drive'

“Awesome. You are nearly there. Add the PassThru parameter so the modified WMI object returns to the Windows PowerShell line, and press ENTER,” I said.

Here is what she typed:

-p<tab><enter>

The complete command is shown here. (This is a one-line command,broken at the pipe so it is more readable.)

Get-CimInstance -ClassName win32_Logicaldisk -Filter "deviceid='c:'" |

Set-CimInstance -Property @{volumename='SSD_Drive'} –PassThru

The command and the associated output are shown in the following image.

clip_image004

 

“Well, isnt that special,” she mocked.

“Actually, I think it is pretty cool,” I said somewhat defensively.

“I do too. I was just giving you a hard time,” She said.

At that instant, the automatic doors to the lobby swung open and in walked a couple of obvious PowerShellers. I could tell by the T-shirts, but the Scripting Wife obviously knew them.

“Well, I am outta here. Have fun working on your presentation,” she said.

“So when will I see you again?” I asked.

“We’re meeting Don Jones and others at Azteca at 5:00,” she said.

“Zulu?”

Without missing a beat, she said “No. Pacific Standard Time.” And she was gone.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy

Advanced Practice Event

Apr 20, 2013
1

I want to direct your attention to this forums post, which I think is worth anyone's time to look through. I've left a pretty long reply with some comments on the entry that would also be worth a read.

I find that a LOT of folks - like the gentleman who posted his script - have a really good approach to PowerShell scripts. They want to use parameters. They want verbose output. They want to proactively check for errors. Where I think folks get lost is in the fine points of how PowerShell enables these features. I see folks working harder than they need to, coding functionality that the shell will actually give them for free. I also see some not-entirely-perfect approaches to things like parameters and error handling, and some occasional mis-use of advanced features (I often see SupportsShouldProcess declared but not actually implemented).

Sometimes, this simply happens because a lot of these advanced features aren't well-documented in one convenient spot - they're all spread out - and because folks are learning from blog posts, which may themselves have been written by someone with an incomplete understanding. Or, they're pasting bits together without really knowing what they're doing. That's cool - what you have to sometimes do is take a whack at something like this poster did, and get some feedback. I'm really glad he did, because it offers an opportunity to clear up some misunderstandings, which will just make his scripts even better in the future.

I hope everyone's looking at the Games as a learning opportunity. I hope everyone will vote on folks' entries and leave comments when they do; I hope as many people as possible spend some time blogging about what  they see, what they've learned, and what they don't understand. That's how we'll all improve.

Let me give you a perfect example (we're no longer discussing the forums post, here - I'm moving on to a new topic):

Try {
  $continue = $true
  $bios = Get-WmiObject -class Win32_BIOS -computername $computer -EA Stop
} Catch {
  $continue = $false
  $computer | Out-File errors.txt -append
}
if ($continue) {
 $os = Get-WmiObject -class Win32_OperatingSystem -computername $computer
 # and so on...
}

This is how I used to code for error handling when querying multiple WMI classes. I'd set a "flag" variable, $continue, to $false if the first WMI call failed, so that I didn't waste time on subsequent calls. Note that this is just a snippet; it isn't an entire script. Then I had a student who coded it this way:

Try {
  $bios = Get-WmiObject -class Win32_BIOS -computername $computer -EA Stop
  $os = Get-WmiObject -class Win32_OperatingSystem -computername $computer
  # and so on...
} Catch {
  $computer | Out-File errors.txt -append
}

Much more concise, and same effect. If the first WMI call fails, I jump into the Catch block, and skip the remaining code anyway. So there are constantly learning opportunities in seeing someone else's approach. For me, I learn new approaches that are sometimes better than what I've been doing. I also learn how to better teach PowerShell to people, by seeing common mistakes and misunderstandings. It's great to share your failures - that's how we grow!

Update: Someone dropped me a line and made a couple of points, which I want to address:

In the reply to the blog post you say: "Please consider properly setting -ErrorAction on the command (Get-WmiObject, in your case) and using a Try/Catch construct to actually handle errors, not just hide them." The example shown does the exact opposite. Any terminating error is caught, logged to a file, but not re-thrown effectively hiding the exception.

I disagree. First, handling an error may still involve suppressing the error message. But I'm suppressing it for just one command, not the entire script; I'm also handling the error by, in my case, logging it to a file. How you choose to handle may differ. What I don't want to do is toss a terminating exception - I'm in a loop, and want my command to continue processing the next object.

Also the $os  = ... part is missing the -errorAction STOP.

That's deliberate. If there's going to be an anticipated error - lack of connectivity, bad credentials, etc., I'm going to get an error on the first WMI call ($bios). I'll trap it, log it, and move on to the next computer (one presumes those snippets of mine are running in a loop of some kind, processing one computer at a time). If there's an unexpected error, like a corrupt WMI repository or something, the second WMI call ($os) will explode, generating an error that I very much want to see, because I didn't anticipate it.

Notice a word that I used a lot there: "I." I'm coding the script for the way I want to it to run. I want anticipated errors logged, and I want unanticipated errors to continue exploding. You may want your scripts to do different things. I'm not putting these snippets out there as the One True Way To Code, because there's no such thing. What I am saying is that you need to think about why you're coding the way you are, and have some justification for it.

Globally suppressing error messages, but not doing anything to handle errors that you do suppress, is a poor practice. Beyond that, do what you need to do. I'm fine with someone suppressing an error they've dealt with. But if your code isn't dealing with it, then the person running the script needs to see something's gone wrong.

 

Weekend Scripter: Use PowerShell to Find the Version of Windows

Uncategorized
Apr 19, 2013
0

Summary: The Scripting Wife learns about using Windows PowerShell to find computer hardware information in prep for the 2013 Scripting Games.

Microsoft Scripting Guy, Ed Wilson, is here. This morning did not start with a nice leisurely cup of tea on the lanai at home. Instead, it started by rushing around to get packed, loading up the vehicle, vying for a parking place at the airport, taking a shuttle from parking to the Charlotte terminal, standing in line for an hour for a security screen that was more intimate than my last physical exam, putting shoes and belt back on and repacking my suitcase, and standing in another really long line to buy an overpriced cup of warm milk with a shot of acidic coffee in it. Then another long line to board a really, really cramped airplane. Luckily, the Scripting Wife is with me, and she helps make the entire process a little more pleasant. Yep, we are finally on our way to Seattle for the Windows PowerShell Summit.

Scripting Wife uses WMI

After takeoff, I open my laptop and the Windows PowerShell console and start playing around. Before long the Scripting Wife is poking me in my shoulder.

“Yes,” I say.

“So I think this would be the perfect time for you to talk to me about using WMI with Windows PowerShell,” she stated.

“OK, good idea,” I said as I slid my laptop over to allow her to use it.

“So what now?” she asked.

“Well the first thing to know about WMI is that there are lots of WMI classes. They are also arranged into different namespaces, but we will not get into that right now. To find WMI classes, use the Get-CimClass cmdlet in Windows PowerShell 3.0,” I said.

“Well, that makes sense. So what do I do if I need to find the name of my operating system?” she asked.

“Why don’t you try it? Use the Get-CimClass cmdlet, and put os into a pair of wildcard characters,” I suggested.

She typed the following:

Get-Cimc<tab><Space>*os*<enter>

The command is shown here:

Get-CimClass *os*

The command and the associated output are shown in the following image.

Image of command output

“Well, that did not do what I expected it to do,” she said.

“So, you have found out that WMI does not really have a class named OS, or anything similar to OS. What are you really looking for?” I asked.

“I want to know the version of Windows,” she said.

“The cool thing about Get-CimClass is that you can search for classes that contain a specific property. So why don’t you look for WMI classes that contain a property of version?” I suggested.

The Scripting Wife typed the following:

Get-Cimc<tab><space>-p<tab><space>version<enter>

The following is the command she created.

Get-CimClass -PropertyName version

The command and the output associated with the command are shown in the image that follows.

Image of command output

“It is still a lot of stuff,” she complained.

“Well one of the things you need to know about WMI is that there are abstract and dynamic classes,” I began.

“And why do I need to know that?” she interrupted.

“Maybe you do not need to know all that, but keep in mind you want to use dynamic classes,” I began again.

“Why?” she asked.

“Because dynamic WMI classes go off and get information. So they are the ones that you want to use. The abstract classes are more used by developers,” I said.

“Well OK. So I only need to remember to use dynamic classes. I got it. Now what?” she said.

“The Get-CimClass cmdlet has a QualifierName parameter. You can specify that you only want dynamic WMI classes returned. It will give you a better output,” I said.

“Cool. So let’s do it,” she said energetically.

“Use the Up arrow to retrieve your previous command. Go to the end of the command and add the QualifierName parameter,” I said. “And don’t forget to use tab completion to help you.”

The Scripting Wife typed:

<uparrow><space>-q<tab><space>dynamic<enter>

Here is the command she created:

Get-CimClass -PropertyName version -QualifierName dynamic

The command and associated output are shown here.

Image of command output

“Cool,” she said. “So where do I find the version?”

“Look at the output. See the Win32_OperatingSystem?” I started.

“Oh yeah, I get it,” she said.

“Now use Get-CimInstance to retrieve the operating system information,” I said.

She typed the following:

Get-Cimi<tab><space>Win32_Op<tab><enter>

Here is the command:

Get-CimInstance Win32_OperatingSystem

Here is the command and the output from the command:

Image of command output

“Well ain’t that just special,” she said as she pushed the laptop back over to me. She then opened her Surface and began reading a book that she had downloaded.

I returned to my Windows PowerShell console, and tried to make sense of life.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy 

Get CIMInstance from PowerShell 2.0

Uncategorized
Apr 10, 2013
0

I love the new CIM cmdlets in PowerShell 3.0. Querying WMI is a little faster because the CIM cmdlets query WMI using the WSMAN protocol instead of DCOM. The catch is that remote computers must be running PowerShell 3 which includes the latest version of the WSMAN protocol and the WinRM service. But if your computers are running 3.0 then you can simply run a command like this:

get-content computers.txt | get-ciminstance win32_operatingsystem

However, if one of the computers is running PowerShell 2.0, you’ll get an error.

get-ciminstance-error

In this example, CHI-DC02 is not running PowerShell 3.0. The solution is to create a CIMSession using a CIMSessionOption for DCOM.

$opt = New-CimSessionOption -Protocol Dcom
$cs = New-CimSession -ComputerName CHI-DC02 -SessionOption $opt
$cs | get-ciminstance win32_operatingsystem

get-ciminstance-dcom

So there is a workaround, but you have to know ahead of time which computers are not running PowerShell 3.0. When you use Get-CimInstance and specify a computername, the cmdlet setups up a temporary CIMSession. So why not create the temporary CIMSession with the DCOM option if it is needed? So I wrote a “wrapper” function called Get-MyCimInstance to do just that.

The heart of the function is a nested function to test if a remote computer is running WSMAN 3.0.

Function Test-IsWsman3 {
[cmdletbinding()]
Param(
[Parameter(Position=0,ValueFromPipeline)]
[string]$Computername=$env:computername
)

Begin {
    #a regular expression pattern to match the ending
    [regex]$rx="\d\.\d$"
}
Process {
    Try {
        $result = Test-WSMan -ComputerName $Computername -ErrorAction Stop
    }
    Catch {
        Write-Error $_.exception.message
    }
    if ($result) {
        $m = $rx.match($result.productversion).value
        if ($m -eq '3.0') {
            $True
        }
        else {
            $False
        }
    }
} #process
End {
 #not used
}
} #end Test-IsWSMan

The function uses Test-WSMan and a regular expression to get the remoting version. If it is 3.0 the function returns True. In Get-MyCIMInstance I test each computer and if not running 3.0, create the CIMSession option and include it when creating the temporary CIMSession.

Try {
#test if computer is running WSMAN 2
$isWSMAN3 = Test-IsWsman3 -Computername $computer -ErrorAction Stop

if (-NOT $isWSMAN3) {
    #create a CIM session using the DCOM protocol
    Write-Verbose "Creating a DCOM option"
    $opt = New-CimSessionOption -Protocol Dcom
    $sessparam.Add("SessionOption",$opt)
}
Else {
        Write-Verbose "Confirmed WSMAN 3.0"
}

Try {               
    $session = New-CimSession @sessParam
}
Catch {
    Write-Warning "Failed to create a CIM session to $computer"
    Write-Warning $_.Exception.Message
}

I’m using a Try/Catch block because if the computer is offline, my test function will throw an exception which I can catch.

Catch {
        Write-Warning "Unable to verify WSMAN on $Computer"
     }

Otherwise, all is good and  I can pass the rest of the parameters to Get-CimInstance.

#create the parameters to pass to Get-CIMInstance
        $paramHash=@{
         CimSession= $session
         Class = $class
        }

        $cimParams = "Filter","KeyOnly","Shallow","OperationTimeOutSec","Namespace"
        foreach ($param in $cimParams) {
          if ($PSBoundParameters.ContainsKey($param)) {
            Write-Verbose "Adding $param"
            $paramhash.Add($param,$PSBoundParameters.Item($param))
          } #if
        } #foreach param

        #execute the query
        Write-Verbose "Querying $class"
        Get-CimInstance @paramhash

At the end of the process, I remove the temporary CIMSession. With this, now I can query both v2 and v3 computers.
get-myciminstance01
Notice for CHI-DC02 I’m creating the DCOM option. Here’s the command without all the verboseness.
get-myciminstance02
I could have created a proxy function for Get-CimInstance, but not only are they more complicated, I didn’t want that much transparency. I wanted to know that I’m querying using my function and not Get-CimInstance. Here’s the complete script.

#requires -version 3.0

<#
  ****************************************************************
  * DO NOT USE IN A PRODUCTION ENVIRONMENT UNTIL YOU HAVE TESTED *
  * THOROUGHLY IN A LAB ENVIRONMENT. USE AT YOUR OWN RISK.  IF   *
  * YOU DO NOT UNDERSTAND WHAT THIS SCRIPT DOES OR HOW IT WORKS, *
  * DO NOT USE IT OUTSIDE OF A SECURE, TEST SETTING.             *
  ****************************************************************
 #>

Function Get-MyCimInstance {

<#
.Synopsis
Create on-the-fly CIMSessions to retrieve WMI data
.Description
The Get-CimInstance cmdlet in PowerShell 3 can be used to retrieve WMI information
from a remote computer using the WSMAN protocol instead of the legacy WMI service
that uses DCOM and RPC. However, the remote computers must be running PowerShell
3 and the latest version of the WSMAN protocol. When querying a remote computer,
Get-CIMInstance setups a temporary CIMSession. However, if the remote computer is
running PowerShell 2.0 this will fail. You have to manually create a CIMSession
with a CIMSessionOption to use the DCOM protocol.

This command does that for you automatically. It is designed to use computernames.
The computer is tested and if it is running PowerShell 2.0 then a temporary session
is created using DCOM. Otherwise a standard CIMSession is created. The remaining 
CIM parameters are then passed to Get-CIMInstance.

Get-MyCimInstance is essentially a wrapper around Get-CimInstance to make it easier
to query data from a mix of computers.
.Example
PS C:\> get-content computers.txt | get-myciminstance -class win32_logicaldisk -filter "drivetype=3"
.Notes
Last Updated: April 11, 2013
Version     : 1.0
Author      : Jeffery Hicks (@JeffHicks)

Read PowerShell:
Learn Windows PowerShell 3 in a Month of Lunches
Learn PowerShell Toolmaking in a Month of Lunches
PowerShell in Depth: An Administrator's Guide

.Link
http://jdhitsolutions.com/blog/2013/04/get-ciminstance-from-powershell-2-0

.Link
Get-CimInstance
New-CimSession
New-CimsessionOption

.Inputs
string

.Outputs
CIMInstance

#>

[cmdletbinding()]

Param(
[Parameter(Position=0,Mandatory,HelpMessage="Enter a class name",
ValueFromPipelineByPropertyName)]
[ValidateNotNullorEmpty()]
[string]$Class,
[Parameter(Position=1,ValueFromPipelineByPropertyName,ValueFromPipeline)]
[ValidateNotNullorEmpty()]
[string[]]$Computername=$env:computername,
[Parameter(ValueFromPipelineByPropertyName)]
[string]$Filter,
[Parameter(ValueFromPipelineByPropertyName)]
[string[]]$Property,
[Parameter(ValueFromPipelineByPropertyName)]
[ValidateNotNullorEmpty()]
[string]$Namespace="root\cimv2",
[switch]$KeyOnly,
[uint32]$OperationTimeoutSec,
[switch]$Shallow,
[System.Management.Automation.Credential()]$Credential = [System.Management.Automation.PSCredential]::Empty
)

Begin {
    Write-Verbose -Message "Starting $($MyInvocation.Mycommand)"  
    Write-verbose -Message ($PSBoundParameters | out-string)

    Function Test-IsWsman3 {
        [cmdletbinding()]
        Param(
        [Parameter(Position=0,ValueFromPipeline)]
        [string]$Computername=$env:computername
        )

        Begin {
            #a regular expression pattern to match the ending
            [regex]$rx="\d\.\d$"
        }
        Process {
            Try {
                $result = Test-WSMan -ComputerName $Computername -ErrorAction Stop
            }
            Catch {
                #Write the error to the pipeline if the computer is offline
                #or there is some other issue
                write-Error $_.exception.message
            }
            if ($result) {
                $m = $rx.match($result.productversion).value
                if ($m -eq '3.0') {
                    $True
                }
                else {
                    $False
                }
            }
        } #process
        End {
         #not used
        }
        } #end Test-IsWSMan

} #begin

Process {
    foreach ($computer in $computername) {
        Write-Verbose "Processing $computer"

        #hashtable of parameters for New-CimSession
        $sessParam=@{Computername=$computer;ErrorAction='Stop'}
        if ($credential) {
            Write-Verbose "Adding alternate credential for CIMSession"
            $sessParam.Add("Credential",$Credential)
        }
        Try {
        #test if computer is running WSMAN 2
        $isWSMAN3 = Test-IsWsman3 -Computername $computer -ErrorAction Stop

        if (-NOT $isWSMAN3) {
            #create a CIM session using the DCOM protocol
            Write-Verbose "Creating a DCOM option"
            $opt = New-CimSessionOption -Protocol Dcom
            $sessparam.Add("SessionOption",$opt)
        }
        Else {
                Write-Verbose "Confirmed WSMAN 3.0"
        }

        Try {               
            $session = New-CimSession @sessParam
        }
        Catch {
            Write-Warning "Failed to create a CIM session to $computer"
            Write-Warning $_.Exception.Message
        }

        #create the parameters to pass to Get-CIMInstance
        $paramHash=@{
         CimSession= $session
         Class = $class
        }

        $cimParams = "Filter","KeyOnly","Shallow","OperationTimeOutSec","Namespace"
        foreach ($param in $cimParams) {
          if ($PSBoundParameters.ContainsKey($param)) {
            Write-Verbose "Adding $param"
            $paramhash.Add($param,$PSBoundParameters.Item($param))
          } #if
        } #foreach param

        #execute the query
        Write-Verbose "Querying $class"
        Get-CimInstance @paramhash

        #remove the temporary cimsession
        Remove-CimSession $session
     } #Try
     Catch {
        Write-Warning "Unable to verify WSMAN on $Computer"
     }
    } #foreach computer

} #process

End {
    Write-Verbose -Message "Ending $($MyInvocation.Mycommand)"
} #end

} #end Get-MyCimInstance

I hope you’ll let me know what you think and if you find this useful.

UPDATE: I’ve revised this script and article since it’s original posting to better handle errors if you can’t test WSMAN. I also added support for alternate credentials, which is something you can’t do with Get-CimInstance.

Manning Deal of the Day – April 6 2013

Uncategorized
Apr 4, 2013
0

My PowerShell and WMI book will be Manning’s deal of the day for 6 April 2013.  The deal will go live at Midnight US ET and will stay active for about 48 hours.

This is your chance to get the book with a 50% discount.

Use code dotd0406au at manning.com/siddaway2/

The Deal of the Day offer also applies to SharePoint Workflow in Action (http://www.manning.com/wicklund/).

Enjoy

Skip to toolbar