Downloads
45818.zip

Windows systems administrators have to make sure that the software on their systems is current. The Windows service pack is one of the most important factors in determining whether the OSs on managed computers are up-to-date. IT shops with large budgets can afford management software such as Microsoft Systems Management Server (SMS) that distributes and reports on Windows service packs. However, small shops might not have the budget or expertise to manage an enterprise-level patch-management solution like SMS.

I've written two VBScript scripts that let you query remote computers for their installed OSs and service packs. The first script, GetSP.vbs, connects to a single computer and reports on that computer's installed OS and service pack. The second script, SPCheck.vbs, reads a list of computers from a text file and creates a report suitable for import into a spreadsheet or database.

The remote computers must be running Windows 2000 or later. The computer executing these scripts must be running Win2K with VBScript 5.6, or Windows XP or later. (VBScript 5.6 is part of the OS in XP and later.) The Win2K VBScript 5.6 installer (scripten.exe) is available for download on Microsoft's Web site. Note that if you install Microsoft Internet Explorer (IE) 6.0 or later on Win2K, VBScript 5.6 is installed.

Using GetSP.vbs
I wrote GetSP.vbs to illustrate how to use Windows Management Instrumentation (WMI) to obtain information from a remote computer. Although you can use other tools (e.g., Sysinternals' PsInfo) to retrieve information from remote computers, you typically need to parse the output to retrieve just the service pack version. A short WMI-based script like GetSP.vbs can provide relevant details without relying on other tools. In addition, you can easily customize the script as needed.

The command-line syntax to run GetSP.vbs is

GetSP.vbs \[computername\]

where computername is the name of the computer for which you want to obtain the OS and service pack version. If you don't provide a computer name, GetSP.vbs will obtain the information from the local computer.

Inside GetSP.vbs
As Listing 1 shows, GetSP.vbs starts by using the Option Explicit statement to force variable declaration. Next, the script defines the SCRIPT_NAME constant and calls the Main subroutine, which does the bulk of the script's work.

Callout A in Listing 1 shows the Main subroutine. This subroutine declares several variables, then uses the WScript.Arguments collection to parse the command line. In keeping with standard conventions, when the /? option is present, GetSP.vbs displays a short usage message and exits. When the command line contains at least one unnamed argument (i.e., an argument that doesn't start with the / character), the script uses that argument as the computer name. Otherwise, the script assumes you want to use the local computer.

Next, the script disables the default error handler with the On Error Resume Next statement because the script itself will handle any errors. It then uses VBScript's GetObject function to connect to the WMI provider on the target computer. InstancesOf is a WMI method that returns a collection of objects of the specified class. In this case, the script obtains a collection of WMI objects from the Win32_OperatingSystem class in the root\CIMV2 namespace.

If the GetObject function call fails, the script engine raises an error. GetSP.vbs uses the Err object to check for this error condition. The Err object's default property is Number, so you don't have to write it explicitly. When the Err object's Number property is nonzero, the script exits gracefully with an error message. When no error occurs, the script uses the On Error GoTo 0 statement to re-enable the default error handler.

At this point, the objSystems variable contains a collection of Win32_OperatingSystem class objects. GetSP.vbs uses a For Each...Next statement to iterate through the collection, even though, in this particular case, the collection contains only one member—the object representing the target computer. The script then uses the WScript object's Echo method to output information about that computer. It reads the CSName property to retrieve the computer's name, the Description property to obtain the computer's description (if defined), the Caption property to get the OS name, and the ServicePackMajorVersion property to obtain the service pack version. (For more information about these and other Win32_OperatingSystem class properties, go to http://msdn.microsoft.com/library/default.asp?url=/library/en-us/wmisdk/wmi/win32_operatingsystem.asp.)

Inside GetSP.vbs
As Listing 1 shows, GetSP.vbs starts by using the Option Explicit statement to force variable declaration. Next, the script defines the SCRIPT_NAME constant and calls the Main subroutine, which does the bulk of the script's work.

Callout A in Listing 1 shows the Main subroutine. This subroutine declares several variables, then uses the WScript.Arguments collection to parse the command line. In keeping with standard conventions, when the /? option is present, GetSP.vbs displays a short usage message and exits. When the command line contains at least one unnamed argument (i.e., an argument that doesn't start with the / character), the script uses that argument as the computer name. Otherwise, the script assumes you want to use the local computer.

Next, the script disables the default error handler with the On Error Resume Next statement because the script itself will handle any errors. It then uses VBScript's GetObject function to connect to the WMI provider on the target computer. InstancesOf is a WMI method that returns a collection of objects of the specified class. In this case, the script obtains a collection of WMI objects from the Win32_OperatingSystem class in the root\CIMV2 namespace.

If the GetObject function call fails, the script engine raises an error. GetSP.vbs uses the Err object to check for this error condition. The Err object's default property is Number, so you don't have to write it explicitly. When the Err object's Number property is nonzero, the script exits gracefully with an error message. When no error occurs, the script uses the On Error GoTo 0 statement to re-enable the default error handler.

At this point, the objSystems variable contains a collection of Win32_OperatingSystem class objects. GetSP.vbs uses a For Each...Next statement to iterate through the collection, even though, in this particular case, the collection contains only one member—the object representing the target computer. The script then uses the WScript object's Echo method to output information about that computer. It reads the CSName property to retrieve the computer's name, the Description property to obtain the computer's description (if defined), the Caption property to get the OS name, and the ServicePackMajorVersion property to obtain the service pack version. (For more information about these and other Win32_OperatingSystem class properties, go to http://msdn.microsoft.com/library/default.asp?url=/library/en-us/wmisdk/wmi/win32_operatingsystem.asp.)

Using SPCheck.vbs
SPCheck.vbs is a more sophisticated script that creates a report containing the computer names, descriptions, OSs, and service pack levels for the computers listed in a text file. The script supports several different command-line arguments that provide a fair amount of flexibility in the text file's format. The SPCheck.vbs command-line syntax is

SPCheck.vbs inputfile outputfile
 \[/T:num\] \[/I:char\] \[/O:char\]
 \[/S:lines\] \[/N\]

where inputfile is the name of the text file that contains the list of computers. This document must be an ASCII (i.e., non-UNICODE) text file. The outputfile argument specifies the name of the output report. This text file must not already exist.

The /T option specifies the number (num) of the token that contains the computer name. For example, suppose the input file contains tab-delimited lines that look like those in Figure 1. The first token contains the computer name, and the second token contains the computer description. In cases in which the computer name isn't the first token on each line, you can use the /T option to specify the token number. For example, /T:2 would tell SPCheck.vbs that the second token on each line contains the computer name.

The /I option lets you specify the delimiter character that separates the tokens in the input file. The default value is a tab. If your text file contains any other delimiter, you'd use the /I option. For example, if your text file is comma-delimited, you'd specify /I:,. If you need to specify a space character, enclose it in double quotes (e.g., /I:"").

The /O option specifies the delimiter character for the output file. By default, the script adds a header line to the output file, then for each computer listed in the input file, reports the following details: name, description, OS, service pack version, and error code. (I'll discuss the error code in the next section.) By default, a tab separates these items. If you want to use a different delimiter, you'd specify /O: followed by the desired delimiter. I recommend that you don't use commas or spaces because both these characters occur in the OS descriptions.

Input files can sometimes include headers. To make sure that SPCheck.vbs doesn't read the headers, you use the /S option to tell the script to skip those lines. For example, if the first three lines of an input file are headers, you'd specify /S:3 so that the script skips the first three lines of the input file and starts reading the fourth line. The /N option suppresses the header line in the script's output file.

The input and output filenames must be the first two unnamed arguments on the command line. Other than this requirement, the options can appear in any order. For example, to create a semicolon-delimited report called report.txt that obtains information from the computers listed in mycomputers.txt, you'd use the command

spcheck.vbs mycomputers.txt
 report.txt /o:;

(Although this command appears on several lines here, you would enter it on one line in the command-shell window. The same holds true for the other multiline commands in this article.) You could then import report.txt into a spreadsheet or database tool for further analysis. For example, you could filter by the OS column and sort by the service pack column to get a listing of XP machines that haven't been updated to Service Pack 2 (SP2).

Creating the Input File
You can create the input file for SPCheck.vbs several ways. In an Active Directory (AD) domain, you can generate a list of computers in a variety of formats. For example, you can navigate to an organizational unit (OU) in the Microsoft Management Console (MMC) Active Directory Users and Computers snap-in, right-click the OU, and choose Export List to create a tab- or comma-delimited text file containing the OU's contents. The text file will contain a header line, so you need to use the /S option. For example,

spcheck.vbs myOU.txt /s:1
 servicepacks.txt

tells SPCheck.vbs to process the computers in the tab-delimited file myOU.txt, skipping the first line. The output would be saved in the servicepacks.txt file.

Another method you can use to obtain a list of computers is the Dsquery command-line tool, which comes with XP or later. For example, the following command uses Dsquery to list in the domaincomputers.txt file the first 500 computers in the current domain:

dsquery computer -o rdn
 -limit 500 > domaincomputers.txt

The -o rdn option outputs only the relative distinguished names (DNs) of the objects (i.e., only the computer names). Alternatively, you can list the computers in an OU and its subtree in the file myOU.txt by using the command

dsquery computer -o rdn
 "OU=My OU,DC=mydomain,
 DC=com"> myOU.txt

In Dsquery's output, each computer name is surrounded by double quotes. SPCheck.vbs automatically handles these quotes. You can then create a report containing the service packs of the computers in this file by using a command such as

spcheck.vbs myOU.txt
 servicepacks.txt

Inside SPCheck.vbs
SPCheck.vbs is too long to print in its entirety here. You can find this script on the Windows Scripting Solutions Web site. Go to http://www.windowsitpro.com/windowsscripting, enter 45818 in the InstantDoc ID text box, then click the 45818.zip hotlink.

SPCheck.vbs first declares a set of constants and a global variable that will contain a FileSystemObject object. The script then executes the Main subroutine, which Listing 2 shows. After the Main subroutine declares its variables, it uses the With statement in callout A to examine the WScript.Arguments collection. If the /? argument is present or if there are fewer than two unnamed arguments (meaning the input and/or output filenames weren't specified), the script calls the Usage subroutine, which displays a short usage message and exits.

When two unnamed arguments are present, the script determines the input and output filenames, the delimiter characters, the number of lines to skip at the top of the input file (if any), and the token number for the computer name. If the /S or /T arguments exist, the script uses the GetNumber function to retrieve the numbers specified for the arguments on the command line. If the arguments don't exist or they don't specify numeric values, the script uses sensible defaults (i.e., 0 for /S and 1 for /T).

Next, SPCheck.vbs creates a reference to a FileSystemObject object. Note that this reference is a global variable that will be accessible anywhere in the script. The script then disables the default error handler with the On Error Resume Next statement because the remainder of the script will perform its own error handling.

The script then opens the input file in read-only mode by using the FileSystemObject object's OpenTextFile method to create a TextStream object. If the OpenTextFile call fails, the script will end with an error message. The script performs the same operation when attempting to open the output file. Before opening the output file, the script makes sure that the file doesn't already exist. If the output file already exists, the script will end with an error message.

Next, SPCheck.vbs uses the TextStream object's SkipLine method to bypass the number of lines at the top of the input file as specified by the /S option. If this number is zero, the For Next statement isn't executed. If the /N option wasn't specified on the command line, the script writes the header line to the output file.

The script uses a Do...Loop statement to read each line in the input file. This loop continues until the TextStream object's AtEndOfStream property returns True, which indicates the end of the input file.

At the beginning of the loop, the script uses the GetToken function to obtain a remote computer's name, then assigns that computer name to the strComputer variable. When the string in strComputer has at least one character (i.e., it's not an empty string), the script calls the Alive function to determine whether it can ping the specified computer.

When the ping succeeds, the script connects to the computer's WMI provider to obtain the collection of Win32_OperatingSystem objects. Should the ping operation fail, the script outputs an error line that contains the computer's name and an error code of 1. If the WMI connection fails, the script outputs an error line containing the WMI error code. Otherwise, the script outputs the computer's name, description, OS, and service pack version, as well as an error code of 0 (which indicates no errors occurred).

Now that you have a general picture of this loop, let's take a closer look at the two main functions in it.

Inside the GetToken function. GetToken is a general-purpose function that lets you parse a line for a specific token, using a particular delimiter. The code at callout B in Listing 2 shows the call to GetToken. The function requires three parameters: the text to parse, the delimiter to use, and the number of the token to retrieve. In this case, the TextStream object's ReadLine method obtains the text to parse from the input file. The strInputDelim variable specifies the delimiter, which is either the character specified with the /I option or a tab if the /I option wasn't used on the command line. The lngToken variable contains the number of the token to retrieve, which is the number specified with the /T option, or 1 if the /T option wasn't used. For detailed information about how GetToken works, see Snippets to Go, "Use the GetToken Function to Parse Delimited Lines."

Inside the Alive function. Because WMI can take a long time before timing out when trying to connect to an inaccessible remote computer, SPCheck.vbs first attempts to ping each remote computer by calling the Alive function. As callout C shows, this function requires only one argument: the name of the computer to which you want to connect. In this case, strComputer contains the computer name.

The Alive function first creates a temporary file, then uses the Ping command (ping.exe) to ping the specified host. After redirecting the Ping command's output to the temporary file, the function determines whether a ping reply exists in that output. If a reply is found, the function returns a value of True; otherwise, it returns a value of False.

Note that the Alive function isn't foolproof. For example, computers running a host-based firewall such as XP SP2's Windows Firewall don't allow ping requests by default, so SPCheck.vbs won't attempt to connect to these computers. If your computers are running a host-based firewall, you need to disable the firewall on the remote computers or allow both ping requests and WMI connections. (For information about how to manually configure Windows Firewall in XP SP2, go to http://www.microsoft.com/technet/community/columns/cableguy/cg0204.mspx or http://msdn.microsoft.com/library/default.asp?url=/library/en-us/wmisdk/wmi/connecting_through_windows_firewall.asp. For information about how to use Group Policy to configure Windows Firewall, check out http://www.microsoft.com/technet/prodtechnol/winxppro/maintain/mangxpsp2/mngwfw.mspx.)

Useful Reporting Tools
GetSP.vbs and SPCheck.vbs provide a simple, flexible way to retrieve the OSs and service pack versions running on the remote computers in your network. After you become familiar with how to use these scripts, you can adapt them to capture information from other Win32_OperatingSystem class properties. For example, you can capture information about how much physical memory and virtual memory is available by adapting SPCheck.vbs so that it reads the FreePhysicalMemory and FreeVirtualMemory properties, respectively. No matter whether or not you adapt them, GetSP.vbs and SPCheck.vbs are useful reporting tools.