Hey, Scripting Guy! Question

Hey Scripting Guy! I need to generate a list of hotfixes that are installed on my servers. I have 100 servers on the network for which I am responsible. Each of the server names is listed in a text file called servers.txt. I need a way to read the text file and then query each of the servers. Can you help me?

-- GM

Hey, Scripting Guy! Answer

Hello GM,

Microsoft Scripting Guy Ed Wilson here. Today is a special day. For months and months and months we have had a weekly Script Center migration meeting. Today there is not a meeting. That means I have an extra hour to read through e-mail sent to scripter@microsoft.com. It is not that the migration is completed; however, getting the Script Center Script Gallery online was a biggie. It is amazing how much one can get done when meetings get canceled (don't get me wrong—I love meetings; they are a great way to build consensus on projects, an efficient method of disseminating complex information, and a good way to get to know one’s team members.) When you work with a large number of different people on a number of different projects, meeting time can rapidly outpace production time. I am very sure you know what I’m saying.

I looked in the Script Gallery and found the List Installed Hot Fixes script that is seen here.


strComputer = "."
Set objWMIService = GetObject("winmgmts:" _
    & "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")

Set colQuickFixes = objWMIService.ExecQuery _
    ("Select * from Win32_QuickFixEngineering")

For Each objQuickFix in colQuickFixes
    Wscript.Echo "Computer: " & objQuickFix.CSName
    Wscript.Echo "Description: " & objQuickFix.Description
    Wscript.Echo "Hot Fix ID: " & objQuickFix.HotFixID
    Wscript.Echo "Installation Date: " & objQuickFix.InstallDate
    Wscript.Echo "Installed By: " & objQuickFix.InstalledBy

This is not one of those scripts you want to translate into Windows PowerShell. If you did a direct translation, you would end up with a script that looks like the TranslatedHotFixScript.ps1 seen here.


$strComputer = "."

$colItems = get-wmiobject -class "Win32_QuickFixEngineering" -namespace "root\CIMV2" `
-computername $strComputer

foreach ($objItem in $colItems) {
      write-host "Caption: " $objItem.Caption
      write-host "CS Name: " $objItem.CSName
      write-host "Description: " $objItem.Description
      write-host "Fix Comments: " $objItem.FixComments
      write-host "HotFix ID: " $objItem.HotFixID
      write-host "InstallationDate: " $objItem.InstallDate
      write-host "Installed By: " $objItem.InstalledBy
      write-host "Installed On: " $objItem.InstalledOn
      write-host "Name: " $objItem.Name
      write-host "Service Pack In Effect: " $objItem.ServicePackInEffect
      write-host "Status: " $objItem.Status

There is nothing wrong with the TranslatedHotFixScript.ps1 script. It will work properly, as seen here:

Image of evidence that TranslatedHotFixScript.ps1 working properly

The script is easy to understand for someone making a transition from VBScript. The bad thing about TranslatedHotFixScript.ps1 is that it does not take advantage of Windows PowerShell objects. The same basic information can be obtained by typing a single line of code. The alias for the Get-WmiObject cmdlet is gwmi. When you use the alias, the command becomes the one seen here:

PS C:\> gwmi win32_QuickFixEngineering

The output from the short WMI command is seen here:

Image of WMI command output

A command-line solution is fine for some short testing, but I prefer to write a script if I am going to be performing a task more than once. The GetServersFromTextGetHotFixesWriteToLog.ps1 is still significantly shorter than the other hotfix scripts (except for the name), yet it does much more. The complete GetServersFromTextGetHotFixesWriteToLog.ps1 is seen here.


$servers = Get-Content -path c:\fso\servers.txt
$logfile = "C:\fso\hotfixlog.txt"
ForEach ($server in $servers)
  "Checking hotfixes on $server" | Out-FIle -filepath $logfile -encoding ASCII -append
  Get-WmiObject -class Win32_QuickFixEngineering -Computername $Server |
  Format-Table -property HotFixID, InstalledON, Description -autosize |
  Out-FIle -filepath $logfile -encoding ASCII -append

The GetServersFromTextGetHotFixesWriteToLog.ps1 script begins by creating two variables that are used to point to the log file and to the text file that holds the names of each of the servers. The text file is a simple file that has a computer name on each line of the file. No additional data is required other than the computer name. The Get-Content cmdlet reads the text from the servers.txt file. The ForEach statement is used to walk through the array of computer names that is created by using the Get-Content cmdlet to read the contents of the servers.txt file. This is seen here:

$servers = Get-Content -path c:\fso\servers.txt
$logfile = "C:\fso\hotfixlog.txt"
ForEach ($server in $servers)

Inside the script block for the ForEach statement, the first thing we do is write the computer name to the log file. This is done by piping the string directly to the log file. The encoding of the text file is set to ASCII (by default it uses Unicode), and the append switch is used to keep the file from overwriting itself. This is seen here:

"Checking hotfixes on $server" | Out-FIle -filepath $logfile -encoding ASCII –append

The Get-WmiObject cmdlet is used to retrieve the data from WMI. The resulting management object is piped to the Format-Table cmdlet. This is seen here:

Get-WmiObject -class Win32_QuickFixEngineering -Computername $Server |

The Format-Table cmdlet will create a table. The columns are the order in which the properties are selected. The autosize switch is used to remove excess space between columns. The table is piped to the Out-File cmdlet. This is seen here:

Format-Table -property HotFixID, InstalledON, Description -autosize |
Out-FIle -filepath $logfile -encoding ASCII -append


When the script is run, the only output is written to the log file:

Image of script output written to the log file

GM, that is about all there is to reading a text file, retrieving hotfix information, and writing it to a log file. We hope you enjoyed our little excursion today. Join us tomorrow as we continue to review scripts we found in the new Script Center Script Gallery.

We invite you to follow us on Twitter or Facebook so that you can keep up to date with the latest Script Center news. If you have a question, you can always post it on the Official Scripting Guys Forum, or send it to us by e-mail at scripter@microsoft.com.


Ed Wilson and Craig Liebendorfer, Scripting Guys