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.
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:
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