In "Scripting a Corporate Update System (October 2002,, InstantDoc ID 26360), I described a system for keeping corporate clients up-to-date. I discussed the architecture, the updater scripts that reside on the server-side central script repository, and the client files that the system uploads to each client. In Part 2 (November 2002, InstantDoc ID 26632), I explained the client-side building blocks required to run the updater scripts.

In this article, I describe a simple script for automating client-file installation. Next time, I'll describe how you can extend the script for more complex environments.

Simple Client-File Installation
The way in which you manage your enterprise will typically dictate whether you use a simple or complex method for installing the corporate-update—system client files. The simplest method for uploading these files is to embed them in your existing client installation-and-build process. For example, you can modify new systems' OS installation routines to include the corporate-update—system client files. However, if your enterprise doesn't use a central build process or the departments within your organization purchase clients as needed with a preinstalled OS, you'll have to take a different approach.

For example, you could provide each user access through an email link, 3.5" disk, or CD-ROM to a script that installs the client files and ask the user to run the script. However, you would need to be prepared to work with those users who didn't run the script. (I'll show you later how you can know which clients the corporate update system is managing—i.e., which clients have run the script—so that you can easily track down individuals who haven't.)

Automated Client-File Installation
Although a simple manual distribution system can work, an automated solution that uses a central machine to install the client files on all clients in your organization is better. However, because automated solutions can fail for a variety of reasons, you need to include processing to alert administrators when the automated solution fails for a particular client (I introduced the SendSimpleMailToAdmins subprocedure in Part 2 of this series for this purpose).

Automated Installation Steps
To automate the corporate-update—system client-file—installation process, you must perform several steps. First, you must select a machine (let's call it the installation server) that you can use to run jobs and execute scripts. This machine can be a server or a client on your network. Refer to "Scripting Solutions with WSH and COM: Scheduling Automated Tasks" (see "Related Articles in Previous Issues," page 11) for information about setting up such a machine. The installation server must be able to read and write to the folder that contains the log files, payload files, and crucial installers responsible for uploading files to the clients. I placed these components in the \\ScriptServer\Scripts\CUS\Installation folder (as I listed in Table 1 of the first article of this series). The \Logs subfolder stores installation log files for all attempted installations, and the \Client's CUS Folder subfolder stores the payload client files that the corporate update system uploads to every client.

Second, you must create an Active Directory (AD) group for the clients on your network that the corporate update system is managing. I called my group Centrally Managed PCs. I also added three constants (i.e., MGMT_GRP, MGMT_GRP_DN, and MGMT_GRP_ADSPATH) to CUSLibrary.vbs that let me reference the names of the clients in my AD group directly by using a distinguished name (DN) or by using an ADsPath. You'll need to edit the details in the library to match your AD group and domain names.

Third, you must create a scheduled task that regularly executes the script SimpleCUSClientTargetter.wsf, which Listing 1 shows. My scheduled task executes the script at the same time every day to install files on each client. SimpleCUSClientTargetter.wsf uses several constants and a function in the library file CUSLibrary.vbs. One of the functions that the listing calls is CUSClientFilesInstaller, which copies the two payload files to each client. The techniques in "Scripting Solutions with WSH and COM: Scheduling Automated Tasks" can help you create the scheduled task if you're unfamiliar with the process.

After you perform these three tasks, your simple corporate-update—system installation system will be ready to go. Let's take a closer look at this system to see how it works.

Installing Files on the Client
Listing 1, like the other Windows Script (WS) files in this series, imports constants and functions from CUSLibrary.vbs and another library file, GenericLibrary.vbs. Web Listing 1 and Web Listing 2 (, InstantDoc ID 26969) present excerpts from CUSLibrary.vbs and GenericLibrary.vbs, respectively. (Make sure that you change the paths in Listing 1 to point to your libraries.) Although I've reused a lot of code from these libraries throughout this article series, code reuse isn't always possible. For example, I would have liked to import generic constants (such as FSO_FORCE_DELETE and FSO_COPY OVERWRITE) from GenericLibrary.vbs into the CUSClientFilesInstaller function in CUSLibrary.vbs. Then, I wouldn't have had to define the constants in CUSLibrary.vbs, or at least I would have been able to use values of TRUE in SimpleCUSClientTargetter.wsf without having to remember what each TRUE value meant. However, because the libraries are VBScript files and not WS files, SimpleCUSClientTargetter.wsf can't use the XML tags to import the libraries. Instead of using complex mechanisms, I simply redefined the two FileSystemObject constants in CUSLibrary.vbs.

The CUSClientFilesInstaller function installs the two payload files onto a target client PC. The constant CUS_INSTALLATION_PATH represents the path where these two files are stored. Two other constants, CUS_CLIENT_JOB_NAME and CUS_CLIENT_SCRIPT_NAME, represent the individual files themselves. As I mentioned in Part 2 of this series, the installation server doesn't typically deliver the CUSClientLastUpdate.txt file to a client. If the CUSClientUpdater.wsf script (described last month) can't find the text file, the script assumes that it needs to attempt to retrieve update number 1 from the central script repository. In this case, the script creates the text file because the file doesn't already exist and places the number of the last successfully completed update into the new text file.

CUSClientFilesInstaller needs to know the client's IP name to know where to copy the schedule task .job file and script files. (The function can use the client's IP address instead, but Listing 1 always passes CUSClientFilesInstaller the IP name.) The function declares variables and assembles paths that the script in Listing 1 will use later. Although the function doesn't have to use the variables strPCAdminShare and strPCTasksFolder, both of these components help make the corporate update system easily extensible and modifiable.

The function begins by creating an fso variable that references the Scripting::FileSystemObject object to allow for some file manipulation. Next, the FileSystemObject::CopyFile method copies the task scheduler file to the client. If an error occurs, the function quits with a return code of 1 so that the calling script knows that an error occurred. Because an error at this point in the process would be a failure to copy the first file to the client, the script can safely quit because it hasn't yet added any files to the client PC. When an error doesn't occur, the function uses the same FileSystemObject::CopyFile method to copy the client updater script to the client. If an error occurs during this copy operation, the function uses the FileSystemObject::DeleteFile method to remove the previously copied task scheduler .job file from the client. After the function removes the .job file, the function quits with a return code of 1. When the function successfully copies both files to the client, the function quits with a return code of 0.

The SimpleCUSClient Targetter.wsf Script
The script in Listing 1 starts by declaring and initializing several variables. The strLogFilePath variable represents a path to the log file that records all attempted client installations. The CreateFilenameFromTimestamp function in GenericLibrary.vbs outputs a formatted date string. The script uses the Now() VBScript function to produce from the date string a filename for the log files. The filenames that the CreateFilenameFromTimestamp function returns appear in a year-month-day-hour-minute-second format (e.g., 2002-05-23-13-05-09 to indicate May 23, 2002, 13:05:09), which lets you easily sort them in a directory listing. (You can modify this output to suit the way you want your dates formatted.) The script also uses adsMgmtGroup, which gets passed a Lightweight Directory Access Protocol (LDAP) path, to hold an Active Directory Service Interfaces (ADSI) reference to the AD group.

The next section of the script opens and initializes the log file. To ensure that the log file is readable, the script uses a separator, such as a string of dashes (—-) or equals signs (===), that's defined as a constant called SEPARATOR in GenericLibrary.vbs. The script also calls the GetCurrentUsername and GetCurrentComputername functions in GenericLibrary.vbs to record details for debugging purposes.

After the script prepares the log file, the real work begins. The script must identify all workstations that aren't part of the Centrally Managed PCs AD group and target each one for installation. The script uses ADO to locate the AD objects. (For information about using ADO, see "An ADSI Primer, Part 5: Using ADO to Script Fast ADSI Searches" and "Automating Excel to Create Trend Charts" in "Related Articles in Previous Issues." The former article introduces ADO, and the latter introduces the SearchAD function that Listing 1 calls.) Because the script needs to search AD for IADsComputer objects that aren't members of the Centrally Managed PCs group, the script uses an ADO filter that specifies that it's looking only for computers (i.e., objectClass=Computer) that aren't members of the AD group we're interested in—i.e., !(memberOf=Centrally Managed PCs). The script uses an ampersand (&) at the beginning of this filter to combine these two requirements.

The search appears as a call to SearchAD, which specifies that the script wants to search the entire domain for PCs, starting at the root and going through all subtrees, and apply the filter as the search progresses. For any computer objects that aren't part of the Centrally Managed PCs AD group, the script retrieves the dNSHostName and the ADsPath and places both components into the arrPCs array for later manipulation. SearchAD also assigns a value of True or False to indicate whether the search was successful.

After a successful search, arrPCs contains a multidimensional array of attributes that match the search criteria. These attributes are the dNSHostName (i.e., the Fully Qualified Domain Name—FQDN—of the computer object, such as and ADsPath (i.e., the full LDAP path to the object in AD). The script loops through the array and attempts to run the CUSClientFileInstaller function on each client that the array elements represent. To perform this loop, the script uses the intIndex variable to loop from 0 to the last element in the array. If the search returned 12 results, arrPCs(5,0) and arrPCs(5,1) indicate the dNSHostName and ADsPath, respectively, of the fifth item in the array—assuming arrPCs(x,y) and assuming that x=0..11 and y=0..1. Because the script uses intIndex, it must ensure that it uses arrPCs(intIndex,0) and arrPCs(intIndex,1) within the loop to reference the correct array elements for that cycle through the loop. To make this process even easier, you can use strPC each time to represent arrPCs(intIndex,0).

For each client the script identifies as a member of the managed group, the script uses a function called IsHostReachable in GenericLibrary.vbs to ensure that the installation server can ping the client. This function returns True or False and uses the Ping command to test whether the installation server can contact the client.

When the installation server can reach the client, the script uses strPC, the client's DNS IP name, to call the CUSClientFilesInstaller function. If the function returns a result of 0, the installation was successful and the script uses the IADsGroup::Add method with the computer object's full LDAP path to add the client to the Centrally Managed PCs AD group.

Keeping Count of Successes and Failures
Because any number of things can go wrong with the corporate update system, tracking errors and reporting problems are important. To that end, the script in Listing 1 maintains a detailed log file and emails the administrator when the script finishes. In addition, to ensure that an administrator can obtain a quick view of a particular script run, the script writes a brief summary of the events of that particular script run for the log file and email alert.

If SimpleCUSClientTargetter.wsf installs the client files on a client, the script increments the intSuccess counter by 1. After a successful file installation, the script either increments intGrpAddSuccess if the script successfully added the client to the Centrally Managed PCs AD group or increments intGrpAddFail if the script failed to add the client to this group. The script also uses intFailedInstallScript to indicate that the script failed to install the necessary files on a client and intFailedUnreachable to indicate that the script was unable to even reach the host. All these variables create quick summary statistic lines that the script places into the strSummary variable. The script then adds these lines to the log file and to the body of the email message the script sends to the administrator. The SendMailToAdmins message-sending subprocedure in GenericLibrary.vbs adds support for attachments to the SendSimpleMailToAdmins subprocedure I described in Part 2. If the third parameter in the SendMailToAdmins subprocedure is blank, the script doesn't add a file attachment to the email message; otherwise, the script passes the file path to the subprocedure to attach the file to the email message. SendMailToAdmins doesn't include any error checking, but you can easily modify the subprocedure to ensure that the third parameter is a valid file path and that the file can be attached.

Looking Ahead
In the final article of this series, I'll concentrate on tips and tricks I've learned over the years while using the corporate update system. With the final piece of the puzzle, you'll be able to see how you can extend and enhance this system for your own use.

You can obtain the following articles from the Windows Scripting Solutions Web site at

"Scripting a Corporate Update System, Part 2," November 2002, InstantDoc ID 26632

"Scripting a Corporate Update System," October 2002, InstantDoc ID 26360

"Scripting Solutions with WSH and COM: Advanced Methods for Task Scheduling,"
May 2002, InstantDoc ID 24592

"Rem: Using the Ping Command in WSH Code," March 2002, InstantDoc ID 23931

"Scripting Solutions with WSH and COM: Scheduling Automated Tasks,"
March 2002, InstantDoc ID 23935

"Automating Excel to Create Trend Charts," March 2000, InstantDoc ID 8186

"An ADSI Primer, Part 5: Using ADO to Script Fast ADSI Searches,"
May 1999, InstantDoc ID 5281

"An ADSI Primer, Part 2: Manipulating Active Directory Objects," February 1999, InstantDoc ID 4818