Comparing Installed Hotfixes on Servers

This week I had the need to scan several systems and see which hotfixes were installed on which machines and also figure out which hotfixes I needed to install to make sure all machines were identical. Now that PowerShell V2 has been RTM’d in windows 7 and is included in the Release Candidate of the Windows Management Framework, I am going to go with V2 features. However, this script could be ported to V1 if need be.

As I was thinking about this problem, there were a few tools in the toolkit I thought I could use. The first is the new cmdlet, get-hotfix. This is just a wrapper of Win32_QuickFixEngineering but its nice to have it abstracted up to the cmdlet level. The second tool I thought of using is compare-object. I have known about this cmdlet but haven’t had a real opportunity to use it much. It’s actually very powerful but it does take a bit of neuron firing to wrap your head around how it works.

The way I set this is up allows me to compare two servers.  I also have a credential parameter that is used to access servers so I can do my part in supporting the principle of Least Privilege and not be logged in with a Domain Admin account all the time.

So I pull the list of installed hotfixes from each server and select only the HotfixId property. This will make using the compare-object cmdlet a bit easier. If we do a get-member on compare-object we see that it outputs a PSObject with 2 noteproperties, a InputObject and a SideIndicator.

image

From the help on compare-object we find this description of the SideIndicator property:

The result of the comparison indicates whether a property value appeared only in the object from the Reference set (indicated by the <= symbol), only in the object from the Difference set (indicated by the => symbol) or, if the IncludeEqual parameter is specified, in both objects (indicated by the == symbol).

Well that does the job but frankly its output is difficult to easily interpret at first glance. The beauty of PowerShell is that if you really don’t like the way something works, you can easily work around it. In this case, I created a new array of custom objects that have three properties: KB, the name of the first server, and the name of the second server. (Lines 19 and 22)

Then I foreach’d (the new verb of the day) through the collection of compared hotfixes and switched on the “SideIndicator” property. I did this to make the output more clear so users of the script would not have to interpret all the arrows and equal signs generated by compare-object.

Here is a sample of the output:

image

I used Write-Host to generate some text output but I also get back an object that I can slice and dice later on.

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
Function Compare-InstalledHotfix {
param (
[parameter(Mandatory=$true,Position=0)]
$server1,

[parameter(Mandatory=$true,Position=1)]
$server2, 

[parameter(Mandatory=$true,Position=3)]
[Management.Automation.PSCredential]
$credential
)

$server1HotFix = get-hotfix -computer $server1 -Credential $credential | select HotfixId
$server2HotFix = get-hotfix -computer $server2 -Credential $credential | select HotfixId

$comparedHotfixes = compare-object $server2HotFix $server1HotFix -IncludeEqual

$result = @();

foreach ($c in $comparedHotfixes) {
    $kbinfo = "" | select KB,$server1,$server2
    $kbinfo.KB = $c.InputObject.HotfixId
    switch ($c.SideIndicator)
    {
    "==" {
            write-host -ForegroundColor Green "Both servers have $($c.InputObject.HotfixId)"
            $kbinfo.($server1) = $true
            $kbinfo.($server2) = $true
            $result += $kbinfo
         }
        
    "=>" {
            write-host -ForegroundColor Yellow "$server1 has $($c.InputObject.HotfixId) but $server2 doesn't"
            $kbinfo.($server1) = $true
            $kbinfo.($server2) = $false
            $result += $kbinfo
          }
         
    "<="  {
            write-host -ForegroundColor Magenta "$server2 has $($c.InputObject.HotfixId) but $server1 doesn't"
            $kbinfo.($server1) = $false
            $kbinfo.($server2) = $true
            $result += $kbinfo
          }
    } # End Switch
  } # End foreach
   $result
 } # End Function

This code is also available on the TechNet Code Gallery and up on PoshCode

Debugging using PowerShell ISE102

In my last post I gave a high level overview of how to get started with debugging in PowerShell 2.0, using the new Integrated Scripting Environment. In this installment, we are going to explore some of cmdlets that can be used to manipulate breakpoints.

First,  lets do some discovery. Whenever I am looking for cmdlets to work with something in particular, i use the get-command cmdlet with wildcards to help my search.

image

So from this it looks like we can get, set, enable, disable, and remove breakpoints. When we are setting breakpoints, you can specify the line and column you want to break on, but that can get really tedious. This is what the ISE does for you automatically when you toggle breakpoints. However, I find it much more useful to break when ever a variable is accessed.

Let’s use a basic function to demonstrate this.We’ll create a function foo and then set a breakpoint for the $c variable.

image

When we execute this code and call function foo, we get the output below. Notice that $a and $b is set, and that we break when we hit $c. One thing to note, when we specify the variable, we do not use the $ in front of the variable. This is the same as specifying the –outvariable common parameter for other cmdlets.

image

Last but not least, let’s take a look at how to navigate this “nested” prompt. If you type “?” or “h”, you will get the following information It is interesting that the “nested>” prompt displays this when you hit “?.” Under normal circumstances, the “?” is an alias for where-object, as we can see when we type ? at a normal prompt.

image

I hope this helps you with debugging your scripts.

Debugging using PowerShell ISE 101

I have always found the origin of words to be fascinating.  Apparently, the terms bug and debugging in regards to computers are attributed to Admiral Grace Hopper in the 1940’s.

While she was working on a Mark II Computer at Harvard University, her associates discovered a moth stuck in a relay and thereby impeding operation, whereupon she remarked that they were "debugging" the system.

When I first started scripting and writing a little code, the concept of debugging something seemed really hard. However, I have found that with a few simple steps I can debug most of my scripts pretty quickly.

Ninety nine percent of the time, debugging scripts requires being able to watch a variable at some point in a script or a function. Have you ever written a function and thought, “If only I knew what x was before y started messing with it?”

The PowerShell Integrated Scripting Environment makes this pretty easy. You can set a breakpoint on any line in the ISE using the Debug Menu and choosing “Toggle Breakpoint.” or by using the F9 shortcut key.

image

When you toggle a breakpoint on and then run the script, the script will stop at that point. You should note that at this point, the highlighted line has not been executed yet.

Here’s what it looks like when you hit a breakpoint. You get thrown into a nested prompt where you can poke around and look at any variables you want to. Notice the >>> prompt and notice $b has not yet been set, but $a has.

 image

Now that you are paused right around the line of code you want to check out, you can use the “Step Into” Feature.

This will execute the next line of code in the script (the highlighted line) and then stop.

image

Now you can see that $b has been set but $c doesn’t have a value yet.

image

You can continue to step through as much as many lines as you need to until you see something that is not quite right.

This is just the beginning of what we can do in regards to debugging scripts and functions. In the next couple weeks I plan to share more as I learn about debugging and the ISE. Just to whet your appetite, you can run the command get-command –noun psbreakpoint to see what kinds of goodies await!

PowerShell 2.0 for Vista and Server 2008

The release candidate for the Windows Management Framework is now available on the Microsoft Connect web site. The framework includes PowerShell 2.0, WinRM 2.0 and BITS 4.0. There are actually two downloads, one for PS and WinRM and another for Bits that make the whole framework.

Download it here:

https://connect.microsoft.com/windowsmanagement/Downloads

Seattle PowerShell Script Club #2 9/3/2009

I am happy to announce that Avanade will be hosting the second Seattle PowerShell Script Club on Thursday, September 3rd at 7:00 PM. You can register here.

What is a PowerShell Script Club?

Script Clubs are like a hands on lab with no set topic or teacher. You bring an idea for a script, and ask your fellow PowerShell users for help getting the script written.

James Brundage, from the PowerShell at team at Microsoft, will be joining us for the evening.

About Seattle PowerShell Script Club

1. You Always Talk About Script club
2. You Always Talk About Script Club
3. If Someone asks for Help, And You Can Help, You Help
4. Two People Help One Person at One Time
5. One Module Per Person Per Night
6. All Scripts, All PowerShell
7. Scripts will be as short as they can be
8. If This is your First time at Script Club, You Have to Script

Dynamic Binary Modules Follow Up

There was a very interesting post over on the PowerShell team blog on Dynamic Binary Modules where Nigel Sharples describes how to run a cmdlet on a remote machine if you have the source code to the cmdlet and don’t want to deal with any intermediate assembly files to clean up.

I went and tried to do this straight away on my local machine without the remoting bit. I copied some cmdlet source code from MSDN. As a Systems Engineer, I haven’t written many compiled cmdlets in C# but I have edited a few for my liking and written one or two when script just wouldn’t cut it. One thing I have learned is that you always need a Snapin to register your cmdlet. So you have your namespace with a class for your snapin and then a class for each cmdlet that you create.

The bottom line here is that with dynamic modules you don’t need to create a Snapin class for your cmdlet. The only other tweak is that I used the –language parameter on Add-Type to specify CSharpVersion3 so I could use automatic properties and not have to explicitly write the getter and setter for the parameter “Name.”

[string]$code = @"
using System.Management.Automation; 

namespace SendGreetingDemo1
{

  [Cmdlet(VerbsCommunications.Send, "Greeting")]
  public class SendGreetingCommand : Cmdlet
  {
    [Parameter(Mandatory=true,Position=0)]
    public string Name {get;set;}
    
    protected override void ProcessRecord()
    {
      WriteObject("Hello " + Name + "!");
    }
  }
}
"@            
            
$assembly = (Add-Type -TypeDefinition $code `
          -Language CSharpVersion3 `
          -passthru ).Assembly            
$assembly | Import-Module

Remote Desktop mstsc /admin

For some reason back when Vista came out, MS changed the /console switch from /console to /admin for MSTSC.exe which is used to connect to remote desktop sessions.

I find myself using /admin when there are already two users logged on and I can’t get in without knocking someone off.

A friend of mine showed me a nifty trick that I thought was worth sharing.

You can use the /admin switch in the Remote Desktop window itself. You don’t have do go back to the run menu or a cmd PowerShell prompt and run mstsc /console.

image