Hey, Scripting Guy! Can I Determine Which Servers Have Specific Hotfixes Installed?

ScriptingGuy1

Share this post:

Hey, Scripting Guy! Question

Hey, Scripting Guy! I have a text file that contains a number of computer names. I would like to read that text file, query each computer in the list, and find out the status of a particular hotfix. If I could write the information to a text file, it would be even better. Here is the deal; there was a recent security update that was released. My boss came in and asked me if that update had been applied to each of our 200 servers. I told him I was pretty sure it had, but he wanted a report he could turn in.

I really do not have time to go around to 200 different servers to do this. It would be great if the script was written in such a way that the hotfix could be supplied from the command line. In this way, the script would work kind of like a utility. Do you have time to help me out?

— JH

Hey, Scripting Guy! Answer

Hello JH,

Microsoft Scripting Guy Ed Wilson here. This morning, I am feeling kind of mellow. I am listening to Vanessa-Mae on my Zune, sipping a cup of green tea with cinnamon stick and lemon grass, and trolling through the Official Scripting Guys Forum! It seems that I answered a question similar to yours recently. In fact, I even wrote a script and posted it on the TechNet Script Center Gallery. Let me dig it up, and let’s take a look at it.

This week we are looking at questions that have been posted on the Official Scripting Guys Forum. This user forum is a great place to ask questions related to VBScript or Windows PowerShell. It is also an excellent resource to learn scripting by either reading answers to others questions or by becoming involved in discussions through posting answers. In addition to being a learning resource, the forum is also a fun place to interact with people from around the world who have an interest in scripting. Bonus: The Official Scripting Guys Forum is free. If you wish to post a question or propose an answer to a question, you will need to log in with your Windows Live ID.

GetHotFixReport.ps1

Param(
  [String[]]$hotFix = @("KB973815", "KB952286"),
  [string[]]$computer = (get-content -path c:\fso\servers.txt),
  [string]$filepath = "C:\fso\hotfx.txt",
  [switch]$logToFile
) #end Param

Function Get-HotFixStatus([string]$hotFix, [string]$computer)
{
  Get-WmiObject -class  win32_QuickFixEngineering `
  -Filter "HotFixID = ‘$hotfix’" -computername $computer
} #end function Get-HotFixStatus

Function Get-HotFixReport([string[]]$hotFix, [string[]]$computer)
{
  foreach($c in $computer)
  {
    foreach ($h in $hotfix)
    {
     $status =  $(if(Get-HotFixStatus -hotfix $h -computer $c) {$true} else {$false})
     $object = New-Object -TypeName PSObject
     $object | Add-Member -MemberType NoteProperty -Name Computer `
                     -Value $c
     $object | Add-Member -MemberType NoteProperty  -Name HotFix `
                     -Value $h
     $object | Add-Member -MemberType NoteProperty -Name Installed `
                     -Value $status
    $object
    } #end foreach hotfix
   } #end foreach computer
} #end function Get-FixReport

Function Out-HotFixLog ([string]$filepath)
{
 "Hot Fix Report $(Get-Date)" | Out-File -filepath $filepath -append
 Get-HotFixReport -hotfix $hotfix -computer $computer |
 Sort-object -property hotfix |
 Out-File -filepath $filepath -append
} #end function Out-HotFixLog

# *** Entry Point to Script ***

"Hot Fix Report $(Get-Date)"
Get-HotFixReport -hotfix $hotfix -computer $computer |
Sort-object -property computer |
Format-Table -Property * -autosize

# write to text file
if($logToFile) { Out-HotFixLog -filepath $filepath }

The first thing the GetHotFixReport.ps1 script does is create several command-line parameters. The first parameter hotfix accepts an array of hotfix IDs. These hotfix IDs are the ones that will be checked on the different servers. Because it is likely that you would want to check for the presence of more than one hotfix, the hotfix parameter is configured to accept an array of strings. The [string] symbol is used to cast something to a string. When empty square brackets are added at the end, it becomes an array of strings: [string[]]. There are several ways to create an array in Windows PowerShell. The easiest is to just assign multiple values to a variable. This is seen here:

PS C:\> $a = 1,2,3
PS C:\> Foreach ($i in $a) {$i }
1
2
3
PS C:\>

The problem with this approach is that each of the different command-line parameters must be separated by commas. To deal with the problem of the commas, we use the ampersand and a pair of parentheses to create an array:

PS C:\> $b = @(1,2,3)
PS C:\> Foreach ($i in $b) {$I }
1
2
3
PS C:\>

The computer parameter is also cast as an array of strings. You can supply the computer names from the command line when calling the script or you can change the path to your text file of computer names. The Get-Content cmdlet is used to read the contents of the text file. Each computer name will need to reside on its own line, as seen here:

 

HSG-09-10-09-01

If you want one, a text file can be created that will produce a status report for the specified hotfixes. The filepath parameter points to the location where the report will be stored, and the logToFile switched parameter is used to tell the script to generate the log file. The parameter section of the GetHotFixReport.ps1 script is seen here:

Param(
  [String[]]$hotFix = @("KB973815", "KB952286"),
  [string[]]$computer = (get-content -path c:\fso\servers.txt),
  [string]$filepath = "C:\fso\hotfx.txt",
  [switch]$logToFile
) #end Param

The Get-HotFixStatus function is used to determine the status of a hotfix that may or may not be installed on a computer. It accepts two parameters: the first is the hotfix parameter, and the second is the computer parameter. The Get-HotFixStatus function is used to retrieve the status of a single hotfix from a single computer. To do this, the function uses the Get-WmiObject cmdlet to query the Win32_QuickFixEngineering WMI class. A filter is used to limit the hotfixes that are returned in response to the query to those whose value of the hotfixid property matches the value of the hotfix parameter that is supplied when the Get-HotFixStatus function is called. The computername parameter of the Get-WmiObject cmdlet is used to tell WMI which computer to connect to. This is seen here:

Function Get-HotFixStatus([string]$hotFix, [string]$computer)
{
  Get-WmiObject -class  win32_QuickFixEngineering `
  -Filter "HotFixID = ‘$hotfix’" -computername $computer
} #end function Get-HotFixStatus

The Get-HotFixReport function is used to query each of the computers for each of the hotfixes that are specified from the command line. To do this, two foreach statements are used. The first foreach statement walks through the array of computers, and the second foreach statement walks through the array of hotfixes. If the hotfix is present on the computer, the value of the $status variable is set to true. If the hotfix is not present on the computer, the value of the $status variable is set to false. This is seen here:

Function Get-HotFixReport([string[]]$hotFix, [string[]]$computer)
{
  foreach($c in $computer)
  {
    foreach ($h in $hotfix)
    {
     $status =  $(if(Get-HotFixStatus -hotfix $h -computer $c) {$true} else {$false})

A new object is created and stored in the $object variable. The object is a PSObject and will be used to store information that will be used in the hotfix report. The New-Object cmdlet is used to create the custom object as seen here:

     $object = New-Object -TypeName PSObject

The first property to create on the new custom object is a property named Computer. To add the Computer property, use the Add-Member cmdlet and add a NoteProperty to the object that has the name Computer and the value that is supplied via the ForEach loop that is contained in the $c variable. This is seen here:

     $object | Add-Member -MemberType NoteProperty -Name Computer `
                     -Value $c

The other properties are added to the custom PSObject in the same manner that the computer property was added. You pipe the object to the Add-Member cmdlet to specify the MemberType (NoteProperty in this example), the name of the property, and the value. This is seen here:

     $object | Add-Member -MemberType NoteProperty  -Name HotFix `
                     -Value $h
     $object | Add-Member -MemberType NoteProperty -Name Installed `
                     -Value $status

After the custom PSObject has been created, it is returned to the calling code. To do this, place the variable name that contains the object on a line by itself. Because Windows PowerShell functions automatically return objects to the calling code, it is not necessary to use the return keyword or some other technique to return the object. This is seen here:

    $object
    } #end foreach hotfix
   } #end foreach computer
} #end function Get-FixReport

The Out-HotFixLog function is used to write information to the hotfix log file that is supplied via the $filepath variable. The first thing that is done inside the Out-HotFixLog function is write a header to the text file that includes the date. This is seen here:

Function Out-HotFixLog ([string]$filepath)
{
 "Hot Fix Report $(Get-Date)" | Out-File -filepath $filepath –append

After the report heading has been written, the Get-HotFixReport function is called. It is passed the hotfix and the computer name. The resulting object is sorted by hotfix name and appended to the text file. This is seen here:

 Get-HotFixReport -hotfix $hotfix -computer $computer |
 Sort-object -property hotfix |
 Out-File -filepath $filepath -append
} #end function Out-HotFixLog

The entry point to the script writes a string to the Windows PowerShell console that includes the time when the report was run. This is seen here:

"Hot Fix Report $(Get-Date)"

The first function that is called is the Get-HotFixReport function. It is passed the array of hotfixes stored in the $hotfix variable and the array of computer names contained in the $computer variable. This is seen here:

Get-HotFixReport -hotfix $hotfix -computer $computer |

As the objects are returned from the Get-HotFixReport function, they are sorted based upon the computer name property of the custom object that is returned. This is seen here:

Sort-object -property computer |

The sorted objects are converted into a table and displayed on the Windows PowerShell console. This is seen here:

Format-Table -Property * -autosize

The display on the Windows PowerShell console is seen here:

HSG-09-10-09-02

Next, if the logToFile switched parameter was specified when the script was called, the Out-HotFixLog function is called. This is seen here:

if($logToFile) { Out-HotFixLog -filepath $filepath }

The Out-HotFixLog function is automatically set up to append to the log file that is specified in the $filepath variable. When I run the script on my computer, the following report is displayed:

HSG-09-10-09-03

 

JH, thank you for your question that you sent to scripter@microsoft.com. It was a great question, especially because it had already been answered in the Official Scripting Guys Forum. Join us tomorrow for Quick-Hits Friday.

We welcome you to follow us on Twitter or Facebook. If you have any questions, send e-mail to us at scripter@microsoft.com or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, keep on scripting.

 

Ed Wilson and Craig Liebendorfer, Scripting Guys

0 comments

Discussion is closed.

Feedback usabilla icon