I used to use Scheduled Tasks to run all my systems administration scripts. However, in a few situations, I found that Scheduled Tasks wasn't as efficient as I would've liked. Sometimes, I needed a script to run continuously or I had to know immediately when a script failed. For these special situations, I tried to use the Schtasks utility as well as a script to schedule and manage tasks. Although Schtasks works well for its intended purpose, I had limited success in using it for my purpose because it was difficult to get the systems administration scripts' results in the format I wanted. And using a script to schedule and manage tasks only works when the task is created using the Win32_ScheduledJob class or the At utility.
So, I tried running a few of my systems administration scripts as services. When you run a script as a service, the script can run constantly. Plus, you have several recovery options available. Through the Recovery tab in a service's Properties dialog box, you can configure the service to restart itself if it stops. Alternatively, you can configure the service to run another script or similar type of program to alert you when it stops. Another option is to restart the computer, but in a production environment, that isn't advisable.
Nowadays, I run scripts as services in addition to using Scheduled Tasks. (These two approaches are complementary rather than mutually exclusive.) To make it easy to install a script as a service, I created an HTML Application (HTA). I chose to create an HTA because I like the Windows look and feel of HTAs. More important, users know exactly what information they need to provide. And if they happen to fill out a text box incorrectly, they can just re-enter the information (providing they didn't click the Execute button). In addition, with HTAs, you can easily include Help information.
You can download my HTA file, Install_service.hta, from the Windows Scripting Solutions Web site. Before you use it, it's helpful to know how the HTA's UI works, how the HTA runs a script as a service, and how to adapt a script that you want to run as a service.
How the HTA's UI Works
I knew that other people at my company would use Install_ Service.hta, so I made it straightforward to use. As Figure 1 shows, the HTA's UI has text boxes in which users specify the name of the computer to install the service on, the service's name and description, the location of the script to run as a service, and optional credentials for remote connections.
Note the BROWSE button that's next to the text box in which users enter the script's pathname. When users click this button, they can browse for the file, just like they can do in most Windows applications. To achieve this functionality, the HTA uses the UserAccounts.
CommonDialog object. This object provides the standard File Open dialog box. Unfortunately, this object is available only in Windows XP. However, if you enter a network or local pathname, the HTA won't use this object, so the script will work on Windows 2000 and later OSs. (For information about the User-Accounts.CommonDialog object, go to http://www.microsoft.com/technet/scriptcenter/resources/qanda/jan05/hey0128.mspx.)
Besides the browsing capability, the HTA offers a built-in Help document, which users can access by clicking the help link on the UI. Instead of opening an Internet address, this link triggers an onclick event, which prompts the helpdoc subroutine to run. This subroutine opens a Microsoft Internet Explorer (IE) window and displays information about how to use the HTA.
When users click the Install Service button in the HTA's UI, the script calls the getfile subroutine, which Listing 1 shows. This subroutine searches the Admin$\system32 folder on the target computer for srvany.exe. Srvany.exe, which you can find in any Windows resource kit, is a Windows service that starts another application. (For information about how to create a userdefined service with srvany.exe, go to the article at http://www.support.microsoft.com/default.aspx?scid=http://support.microsoft.com:80/support/kb/articles/q137/8/90.asp& NoWebContent=1.)
If the getfile subroutine doesn't find srvany.exe, a message box appears, prompting the user to enter the file's location in the HTA's UI. After the user provides a pathname, the subroutine uses VBScript's InStr function to search that pathname for the string ftp to determine whether to look on a network or an FTP site for srvany.exe. (The company I work for has an FTP site on which we store utilities and other files.) When srvany.exe is on an FTP site, the subroutine uses the HttpRequest object (Microsoft.XMLHTTP) to download the file to the Admin$\system32 folder, as callout A in Listing 1 shows. When srvany.exe is on a network share, the subroutine uses the Microsoft Scripting Runtime Library's FileSystemObject object to copy the file to the Admin$\system32 folder, as callout B in Listing 1 shows. You're probably familiar with the FileSystemObject object but that might not be the case with the HttpRequest object, which is part of the Microsoft XML Document Object Model (DOM). So, I'll give a brief description of how the getfile subroutine uses this object.
You can use the HttpRequest object to send an HTTP request to a URL, receive the response, and have that response parsed by the XML DOM engine. In this case, the getfile subroutine directs the HTTP request to a file on an FTP site, then passes the response to a Stream object (adodb.stream). You can use the Stream object, which is part of ActiveX Data Objects (ADO), to read, write, and manage a stream of binary data or text. In this case, the subroutine uses the Stream object to write the response to a binary file on the client.
How the HTA Runs a Script as a Service
Now that you know how the HTA's UI works, let's look at how the HTA runs a script as a service. This feat is accomplished by connecting to the target computer on which the service will run, creating a service on that machine, configuring the service's registry settings, starting the service, and displaying the script's progress.
Connecting to the target computer. The HTA uses Windows Management Instrumentation (WMI) to connect to the target computer and to retrieve and set the information required for the service to function correctly. On remote computers, you need administrative privileges to create the service. If needed, you can specify alternate credentials for the remote connection in the HTA UI. If you're already an administrator on the remote computer, you can leave these text boxes blank.
After the connection is made, the HTA finds the %systemroot% folder using the Win32_OperatingSystem class's SystemDirectory property. With this information, the HTA can copy the files needed (mainly srvany.exe and the script to run as the service) to the correct location and write the correct location to the registry to let the service know where to find the files. If the HTA can't copy the files, it will attempt to map a network drive (Z: to Admin$system32) using the alternate credentials provided and try to copy the files from that location. The script exits if the files can't be copied.
Creating the service. The HTA uses the Win32_BaseService class's Create method to create the service on the target computer. The Create method accepts up to 12 parameters. The HTA sets about half of them:
- The first parameter specifies the name of the service. In this case, the Service variable contains this value, which the HTA retrieves from the second text box in the UI.
- The second parameter specifies the service's display name. The HTA uses the Service variable's value for the display name.
- The third parameter specifies the fully qualified path to the executable file that implements the service (in this case, srvany.exe).
- The fourth parameter specifies the type of service. The HTA sets the service type as Own Process (value of 16), which means the script will run in its own process. To make it easy for others to discern the service type when reviewing the HTA code, a constant named OWN_ PROCESS specifies the service type.
- The fifth parameter indicates the severity of the error if the Create method fails to start when the computer restarts. The HTA uses a constant (NORMAL_ERROR_CONTROL) to specify the Normal level (value of 1), which means the user will be notified if a failure occurs.
- The sixth parameter specifies the service's startup mode. The HTA sets this parameter to Automatic, which means the service starts up whenever the computer is restarted.
- The seventh parameter determines whether the service can interact with the desktop. The HTA uses the NOT _INTERACTIVE constant to tell the service not to interact with the desktop.
To learn about the other parameters you can set for the Create method, go to http://www.msdn.microsoft.com/library/default.asp?url=/library/en-us/wmisdk/wmi/create_method_in_class_win32_baseservice.asp.
Configuring the service's registry settings. Without some registry edits, the service won't run. So, the HTA uses WMI's StdRegProv class to make the necessary registry changes, as callout A in Listing 2 shows. This class provides methods for writing to and reading from the registry.
The HTA first creates a registry key named HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Services/service_name, where service_name is the name of the service you're creating. The HTA then adds a Parameters subkey. In the Parameters subkey, the HTA adds two entries, as callout B in Listing 2 shows. The first entry is named Application. To this entry, the HTA assigns a REG_SZ value that specifies the path to WScript (which will execute the script that runs as a service) and the path to the script. The second entry is named Description. To this entry, the HTA assigns a REG_SZ value that describes the service. The HTA retrieves this value from the third text box in the UI. The description is a nice addition because anyone who looks at the Control Panel Services applet will know what the service is for.
Starting the service. After the registry changes are made on the target computer, the service is ready to be started. The HTA uses the Win32_ Service class's StartService method to start the service. Note that anytime you make changes to the script, you need to restart the service for the changes to become effective.
Displaying the script's progress. To display the progress of the script that's running as a service, the script opens an IE window. Using an IE window to display a script's progress is necessary because the typical approaches don't work. For example, you can't use VBScript's MsgBox function or the WScript.Shell object's Popup method because they would cause the HTA to pause during execution. (Although the HTA uses a message box to prompt the user to enter the path to srvany.exe when that utility can't be found, that pause isn't detrimental because the script can't run without that path anyway.) You can't use the Echo method because it's an object intrinsic to the WScript and CScript engines, and HTAs use mshta.exe for execution. You can't display progress messages in the HTA UI because the script would appear to hang. Even though the script would be running, you wouldn't see any progress messages. You're get the progress messages only after the script finished running or if the script stopped running due to an error. By using an IE window, the HTA can provide progress reports the entire time the script is running.
How to Adapt a Script
As I mentioned previously, scripts that need to run continuously are good candidates for running as services. For example, the MoveFiles.vbs script in Listing 3 would be a good candidate. This script uses the FileSystemObject object to move to a network share any files that users save to a local folder named C:\TempImages. Note that this script doesn't report results or handle errors. So, if you want to use this script in a production environment, you'll need to make it more robust by adding code that would handle errors and report results.
It's important to keep in mind that whatever script you intend to run as a service must run totally unattended. Otherwise, the script might fail. For example, if a script that uses the MsgBox function were to run under the WScript engine, the script would display a message box and wait for you to click a button before the script continued. But you might not see the message box if the script were running under an account that differs from the one you're currently logged on with, so the script would wait indefinitely. You could run the script with CScript, but you run the risk of having a command window pop up if certain service parameters are set.
The bottom line is that you need to avoid using such elements as the MsgBox function and the Popup method. You should also avoid using the WScript object's Echo method. However, if you want to leave an Echo statement in a script for testing purposes later, you can insert the code
WScript.interactive = Falseat the beginning of the script to disable all prompts, as callout A in Listing 3 shows.
If you want to remove an HTAcreated service, the Microsoft TechNet Script Center (http://www.microsoft.com/technet/scriptcenter/default.mspx) has a script to remove services on local or remote machines.
2 Tools for the Job
You need the right tools to successfully run your systems administration scripts. When it comes to scheduling scripts to run, you have at least two tools at your disposal. Under many circumstances, Schedule Tasks works well to run scripts. However, when you need a script to run continuously or when you need to know immediately if a script fails, you should consider running the script as a service.