Running programs on remote computers is a common administrative task—useful, for example, in installing software updates. Windows 2000 and later include the Task Scheduler service, which lets you flexibly schedule programs to run on a local or remote computer. Task Scheduler lets you schedule tasks through an easy-to-use GUI, in contrast to the old Windows NT scheduler service, which required you to run the AT command at the command line.
Nevertheless, it's still sometimes useful to schedule jobs from the command line. The AT command is still available for backward compatibility, although its flexibility is limited because, by default, jobs scheduled with the AT command run under the context of the System account, which for security reasons doesn't have network access. This limitation can be a problem when you need to schedule a program that resides on a different computer.
Task Scheduler is useful in such cases, but Win2K doesn't include a built-in way to schedule commands executed at the command line to run under the context of a network account. However, Microsoft provides a tool that lets you manage the Windows Task Scheduler from a command line. You can find the tool—jt.exe—in the Microsoft Windows 2000 Resource Kit, or you can download it at ftp://ftp.microsoft.com/reskit/win2000/jt.zip. (Be aware that some users have reported intermittent problems with downloading from Microsoft's FTP site.) Jt.exe also works on Windows XP and later. I've written a VBScript script called JTRun.vbs that uses Jt's functionality to conveniently and securely run a program on a remote computer. You can find JTRun.vbs on the Windows Scripting Solutions Web site. Go to http://www.windowsitpro.com/windowsscripting, enter InstantDoc ID 45148 in the InstantDoc ID text box, then click the 45148.zip hotlink.
Although XP and later include the Schtasks utility, which provides a command-line interface to Task Scheduler, Schtasks doesn't provide the "run-now" functionality that Jt offers. That is, Jt lets you execute a command "now" (meaning in the next minute), whereas Schtasks forces you to enter a specific execution time. Other tools are available that let you run programs on remote computers, such as PsExec. (For more information about PsExec, see the Windows IT Pro article Windows Power Tools, "PsExec," July 2004, InstantDoc ID 42919.) However, PsExec has a security limitation: If you specify different credentials for the program, PsExec transmits the password over the network as plaintext. Task Scheduler (and thus Jt, which merely provides a command-line interface to Task Scheduler) doesn't have this limitation.
When you use Task Scheduler to schedule a program to run on a remote computer, the program won't appear on the remote computer's screen unless the remote computer is logged on using the same account under which the job was scheduled. As a result, you must make sure that any programs run in this way can proceed without user intervention.
The computer on which JTRun.vbs runs must have VBScript 5.6 installed. On a Win2K system, you must either install the scripting engine update or upgrade to Microsoft Internet Explorer (IE) 6.0. No changes are required on a system running XP or later. I briefly tested jt.exe and the script on an NT 4.0 system and found that both programs run fine if the Task Scheduler service (which is available via the IE Offline Browsing Pack component) and VBScript 5.6 are also installed.
Understanding the Jt Command Line
Before we explore JTRun.vbs, let's review Jt's command-line syntax. To schedule a command on a remote computer, the script uses five Jt command-line arguments: /sm, /saj, /sc, /ctj, and /sj. To get help with any of these options, run Jt and specify the /? (Help) option. For example, to obtain help text for the /sm option, run the command
The /sm option specifies the computer name. Jt requires you to precede the name with a double backslash (\\). When you run JTRun.vbs, you can specify the computer name without the \\; the script inserts the slashes for you if they're missing. Also, /sm must be the first option you specify on the Jt command line because Jt requires part of the computer name to be first on the command line. (The other options can be specified in any order.)
The /saj option tells Jt the name of the .job file it should create. The .job file will be created in the computer's Scheduled Tasks folder (\%SystemRoot%\Tasks). The script generates a job name that's based on the program name and current date and time. The /sc option provides the credentials (an account name and password) under which the job should run. Usually, this is an account that's a member of the local Administrators group on the remote computer.
The /ctj option specifies the scheduling options for the job. The /ctj option has a list of possible property/value pairs, which you enter in the format property=value. JTRun.vbs uses the following /ctj property/value pairs: StartTime=now, Type=once, and Disabled=0. StartTime=now tells Jt to schedule the program to run on the next minute from now; in other words, if you execute the command at 12:33:45, the job will be scheduled to run at 12:34:00. Type=once means that the program will run only once. Oddly, Disabled=0 is a required parameter. If you forget to include it, the job will be created and appear to be enabled but won't run.
The /sj option lets you specify several values, including the program to run, its command-line parameters, and an optional "start-in" directory (i.e., the directory from which the executable is started). Like the /ctj option, the /sj option also uses property/value pairs. JTRun.vbs uses these /sj properties: ApplicationName=path and filename of program to run, Parameters=program's command-line arguments, WorkingDirectory=path to the start-in directory, and DeleteWhenDone=1. ApplicationName, which is required, specifies a path and filename that are relative to the remote computer, not the current computer. The Parameters property, which is optional, specifies command-line arguments for the program. Make sure to enclose the contents of these properties in double quotes (") if they contain spaces. Also note that the values for ApplicationName and Parameters can't contain embedded double quotes; this is a limitation of the Jt utility. WorkingDirectory (optional) specifies the program's start-in directory. DeleteWhenDone (optional) controls whether the Task Scheduler service should delete the job after it runs successfully (1) or leave it in the Tasks folder (0).
Here's an example of how you'd run Jt at a command line:
/sc mydom\administrator password
/ctj StartTime=now Type=once Disabled=0
/sj ApplicationName="\\srv1 \updates\update 1.exe" DeleteWhenDone=1
(Commands wrap to multiple lines in this article because of space constraints, but you must enter them on one line.) The sample command line connects to the \\pc1 computer and creates a job called update 1. The job runs using the mydom\administrator account and the account's password. (I recommend you use an account that's a member of the local Administrators group on both the local and remote computers.) The job will run the next minute from now. The program to run is "\\srv1\updates\update 1.exe" (which is enclosed in quotes because it contains spaces), and the job will be deleted after completion if it runs successfully.
When the job runs, it won't be visible to the logged-on user unless the user happens to be logged on using the same account under which the job was scheduled. Thus, you should make sure that the program can run without user intervention. Otherwise, it will continue running invisibly on the computer until an administrator ends it.
Be aware that you might have problems running Jt; for example, you might get an Access denied error after executing the command. To avoid this type of problem, make sure that the specified account has the Access this computer from the network user correctly specified on the target computer. You should also make sure that the XP Service Pack 2 (SP2) firewall is disabled on the target system.
As you've seen, Jt provides quite a bit of flexibility, but its command line is complex. The JTRun.vbs script hides the complexity of the Jt command line while providing a general "run-this-program-now" capability that you can use to quickly and securely run programs on remote computers. You must use the CScript host to run JTRun.vbs. To configure CScript as the default scripting host for the current user, run the command
To run JTRun.vbs, you use this command-line syntax:
When you run JTRun.vbs at the command line, you must specify four required options: computer name, account name, account password, and program name. You can optionally specify command-line parameters for the program by using the /parameters option and for the program's start-in directory by using the /startin option. The /trace option, displays the exact Jt command line, its output, and its exit code. If any of the program's options contain spaces, you must enclose them in double quotes. Options can't contain embedded quotes.
To see firsthand how much easier JTRun.vbs is to use than Jt, let's look at the following sample command, which duplicates the functionality of the earlier Jt command-line example:
(You must precede the JTRun.vbs command with cscript if CScript isn't the default scripting host.) This command runs the "\\srv1\updates\update 1.exe" program the next minute from now on the \\pc1 computer and uses the mydom\administrator account. Notice that because the program name contains a space, its entire path and filename are enclosed in quotes. The sample JTRun.vbs command generates output similar to that in Figure 1.
If you added the /trace option to the sample command, you'd see output similar to that in Figure 2. If you examine the scheduled job's properties on the remote computer (look in the Scheduled Tasks folder), you'll see a dialog box that displays the arguments you specified on the JTRun.vbs command.
Inside the JTRun.vbs Script
Before you run JTRun.vbs in your environment, you'll find it helpful to understand how it works. The first part of the script uses the Option Explicit statement to declare variables (production scripts should always use this statement). Next, the script uses a Const statement to define the script's name. Finally, the script calls the Main subroutine shown in Listing 1, which contains the main body of the script.
The Main subroutine first calls the ScriptHost function to check whether the CScript host is running the script. If the current script host isn't cscript.exe, JTRun.vbs aborts and generates an error message. Using the CScript host lets you redirect the script's output to a file, a capability that can be invaluable for troubleshooting problems.
The Main subroutine is responsible for processing the script's command-line arguments and building a correctly constructed Jt command line. To do this, the subroutine uses the Wscript.Arguments.Named collection. This collection, first introduced in the VBScript 5.6 runtime, provides a flexible and simple way for scripts to process command-line arguments. The script uses a With statement to access the elements of the Wscript.Arguments.Named collection. If any of the four required command-line parameters is missing or empty, the script calls the Usage subroutine, which displays a short usage message and exits.
Next, JTRun.vbs processes the computer name by calling the FixComputerName function, which inserts the required backslash characters before the computer name if those characters aren't present. The script then builds the job name by calling the GetProgramName function, which uses the FileSystemObject object's GetBaseName method to return only the program's filename (in case a full path was specified). The script also calls the StrDateTime function, which returns the current date and time as a string in the form yymmdd_hhmmss. The script appends the value from the StrDateTime function to the end of the job name. This technique ensures that the job name has a high probability of being unique and also lets you tell at a glance when it was scheduled. For debugging purposes, the script also accepts the /trace command-line parameter. If this parameter is present, the value of the blnTrace variable used in the Main subroutine is True.
At callout A, the script builds the Jt command line. Each command-line argument is enclosed in double quotes. (In VBScript, to include a double quote mark inside a string, you have to write it twice, like this: "".) One limitation that I discovered after working with Jt is that the WorkingDirectory property of Jt's /sj option can't be blank. Thus, if the /startin option is specified on the JTRun.vbs command, the WorkingDirectory property is appended to the end of the command line; otherwise, the property isn't included.
At this point, the script is ready to run the Jt program, which it does by calling the RunCommandLine function and specifying two arguments: the entire Jt command line and the value of the blnTrace variable. Web Listing 1 (http://www.windowsitpro.com/windowssscripting, InstantDoc ID 45148) shows the RunCommandLine function. The return value of the RunCommandLine function is the exit code from jt.exe and is stored in the lngExitCode variable in the Main subroutine. Finally, the script exits by using the same exit code as the program run by the RunCommandLine function (i.e., jt.exe).
The RunCommandLine function uses the WshScriptExec object (added in the VBScript 5.6 runtime) to run a program. The function creates a WshScriptExec object by calling the WshShell object's Exec method and specifying a command line (i.e., the strCommand argument). The Do While loop checks whether the Jt program is still running by testing whether the Status property is 0; if it is, the script pauses for 100ms and checks again. Thus, script execution doesn't continue until the program has finished.
Next, the function checks the value of the blnShowOutput parameter that's passed to the function. If the value is True (i.e., if the /trace option was specified on the command line), the Jt program's standard output is written to the screen. Finally, the function returns the Jt program's exit code, which the script uses to exit as I mentioned earlier.
A Few Caveats
When you use JTRun.vbs to run commands on remote computers, you need to keep in mind several important characteristics of the script. First, be aware that directory names and filenames are relative to the remote computer, not the current computer. For example, if you schedule the program C:\bin\runme.exe, this file might exist on the local computer, but if the remote computer doesn't have runme.exe in the same location, the job will fail. However, the "run-at-the-next-minute" feature that Jt provides (StartTime=now) is relative to the current computer, not the remote computer. If your computers' clocks aren't synchronized, the scheduled program might not run when expected.
Also, because JTRun.vbs requires you to enter the account password in plaintext at the command line, it goes without saying that you should exercise caution when using the script. This caveat also applies if you use other scripts to schedule a command to run on multiple PCs; the script that calls JTRun.vbs will contain a plaintext password and must be guarded carefully. If you redirect the script's output and you're using the /trace option, be aware that the output file will also contain the password. Finally, remember to make sure that any program you schedule by using JTRun.vbs doesn't require user intervention, because the program won't interact with the remote computer's desktop unless the job happens to be scheduled by using the same credentials as the logged-on user.
An Enhanced Task Scheduler
Task Scheduler is a powerful system service. With the help of jt.exe, the JTRun.vbs script enhances the functionality of Task Scheduler to let you run programs on remote computers conveniently from a command line. Download the script and start benefiting from its useful capabilities today.