Skip to main content.


Our objective is to create a set of scripts which automatically install all of the applications we want. Important design goals for these scripts include:


The process goes like this:

  1. Map the install share as the Z: drive.
  2. Install Perl.
  3. Install everything else.

Perl is required because the handy utility scripts are written in Perl. You do not need to know Perl to use the scripts, but you should learn it anyway because it is a good tool.

Note that this process is normally invoked immediately after the unattended OS installation by the GuiRunOnce section in the unattend.txt file. In particular, this process assumes that the machine is already configured to automatically log on as the local Administrator after every reboot. To invoke this procedure standalone, you can use the script to enable or disable automatic logon.

Structure of the install share

The install share is the one you created when you set up the automated OS installation, by simply copying the install directory from the Unattended distribution.

Application installation relies on these subdirectories of the install share:

Contains various utility binaries and scripts. You should not need to modify these; if you do, please consider submitting a feature request or a patch.
Contains the installers for the applications themselves. You will need to populate this directory with the installers for your site's applications.
Contains the scripts for installing individual applications and sets of applications. The contents of this directory provides a fairly rich set of examples. You will probably need to edit these or write new ones; feel free to contribute changes and additions.
Contains site-specific configuration data like license keys. You will need to populate this directory with the data for your site before some of the sample scripts will work.

The driver script

One script to rule them all

One master Perl script,, oversees the entire installation process. All of the other scripts are designed to be invoked by maintains a "to-do list" on disk in the plain-text file C:\netinst\todo.txt. You can edit this file with an ordinary text editor, but normally you will not, because itself takes any number of commands as arguments and inserts them at the front of the to-do list. (As you will see, this is what you want.) When invoked as --go, the script removes the first command from the to-do list and executes it, then removes the next command from the list and executes it, and so on until the list is empty.

There are multiple advantages to this design:

In addition to simple commands, the to-do list may contain directives for Directives always start with a dot and include:

The .ignore-err directive

Like its author, is nigh-pathologically cautious about error checking. Any command which exits with a non-zero status will cause the script to halt and print a diagnostic.

Some installers always exit with zero status, even when they fail. There is not much we can do about this, and it keeps us awake at night.

Contrariwise, some installers always exit with a non-zero status even when they succeed. For such installers, you can use the .ignore-err directive to ignore only the expected exit status, while still halting if any unexpected errors occur.

For example, suppose you have an installer foo.exe which always exits with status 37. You would schedule it for invocation like this: ".ignore-err 37 foo.exe"

This will add ".ignore-err 37 foo.exe" to the to-do list. When processes this line, it will invoke foo.exe, silently ignoring exit status zero or 37. It will still treat other status codes as errors.

The .reboot directive

The .reboot directive instructs to reboot the machine after first patching the registry to cause itself to run the next time the current user logs in.

In other words, .reboot provides a controlled, fully synchronous mechanism for rebooting the machine and resuming where you left off. This is important, because uncontrolled reboots create race conditions as the OS kills all processes with indeterminate ordering and timing. When you write a script to install an application, you must suppress any reboots performed by the installer and use the .reboot directive instead.

The .reboot-on directive

The .reboot-on directive tells to run a command and compare the exit status to a specified value. If it matches, behaves as if it had seen a .reboot directive. Otherwise, it does nothing special; that is, it behaves as if .reboot-on were not present.

This directive is useful because most Microsoft installers exit with status 194 when they want to reboot the machine, but you suppressed the automatic reboot with a command-line switch. (This is by observation; as far as we know, it is documented nowhere.) For such installers, you would use .reboot-on like so: ".reboot-on 194 q999999.exe /q /r:n"

The .sleep directive

If you ever have to use this directive, then you are doing something wrong, because there is no such thing as a guaranteed time bound for any Windows operation.

Unfortunately, some installers make it impossible to do things right. For example, they might fork a subprocess and exit. In this case, the most convenient thing might be to delay a while.

This directive simply takes an integer number of seconds as argument. For example, to sleep 37 seconds, you would do: ".sleep 37"

The environment arranges to run all commands in a consistent environment, with the following variables set.


The PATH environment variable will have Z:\bin;Z:\scripts prepended to it before any commands are run. So the scripts may refer to each other and to the utility scripts without supplying a full pathname.


The WINLANG environment variable contains the three-letter language code for the currently running version of Windows. This is useful for writing language-independent scripts.


The WINVER environment variable will contain a short string representing the version of Windows:

For Windows 2000
For Windows 2000 Service Pack 4
For Windows XP
For Windows XP Service Pack 1
For Windows Server 2003

...and so on. This variable is useful in scripts whose behavior needs to vary based on OS; e.g., when installing hotfixes.


The Z environment variable contains the drive letter for the installation share (default Z:). We added this variable after a user complained that his site was already using the Z: drive for another purpose. Unless you are another such user, you do not need to worry about this.


The Z_PATH environment variable contains the full UNC path of the share which is mapped to the Z: drive. Some applications (e.g., the MSDN Library) remember which pathname was used to install them, and they will occasionally search for things there. Since Z: is only used during the installation process, users may not have it mapped, so installing from Z: can cause the application to fail later. For such applications, Z_PATH provides a workaround; see msdn.bat for an example.


Some examples should help. All of these are from the install/scripts directory in the distribution.

Adobe Reader

The adobe-reader.bat script installs Adobe Reader. This is about as simple as an installation script can get.

To invoke this script manually, you would type:

    Z:\bin\ adobe-reader.bat
    Z:\bin\ --go

Obviously, you could just invoke the Adobe installer directly. But that would lose the consistent environment and error checking performed by

Office XP

The officexp.bat script installs Microsoft Office XP and reboots the machine. First, it pushes the .reboot directive onto the to-do list. Then it pushes directives to install each update for Office XP. Finally, it pushes the directive to install Office itself.

To invoke this script manually, you would type:

    Z:\bin\ officexp.bat
    Z:\bin\ --go

Combining scripts

To perform both the Office XP and Adobe Reader installations at once, you would type:

    Z:\bin\ officexp.bat adobe-reader.bat
    Z:\bin\ --go

The first line adds officexp.bat and adobe-reader.bat to the to-do list. The second command processes the list. The script begins by removing officexp.bat from the front of the to-do list and executing it. The officexp.bat script itself starts by pushing .reboot onto the front of the to-do list.

At this point, the to-do list contains .reboot followed by adobe-reader.bat, and we are still in the middle of executing the officexp.bat script itself.

Next, officexp.bat pushes the updates onto the to-do list, and finally it pushes the instruction to install Office itself and exits. Then regains control and continues processing the to-do list; that is, it installs office followed by its updates. Next, it processes the .reboot directive, by arranging to run itself after the next logon and rebooting the machine. After the reboot, starts up again, removes adobe-reader.bat from the to-do list and executes it. And so on.

The final result is that Office XP and Adobe Reader are both installed, even though the machine had to reboot in the middle.

A more complex example

The winxpsp2-updates.bat script installs all of Microsoft's "critical" and "recommended" updates for Windows XP Service Pack 2. All this script does is push a bunch of items onto the to-do list, including the occasional .reboot directive.

This example illustrates how to add a command with arguments to the to-do list by putting it in quotes. Without the quotes, spaces would separate multiple commands.

This example also illustrates the use of the .ignore-err directive.

Finally, this example illustrates an important consequence of the "last in, first out" semantics of Since it always adds items to the front of the to-do list, commands will execute in the opposite order from which they are added. On the other hand, will preserve the order of the commands if you pass several of them on a single command line. Put another way, " X" followed by " Y" has same effect as " Y X".

A high-level example

The base.bat script performs a "base workstation" installation for a organization. This includes a bunch of free software.

This example illustrates the use of the WINVER environment variable. The %WINVER%-updates.bat name, for example, will expand to win2ksp4-updates.bat on Windows 2000 Service Pack 4 and winxpsp2-updates.bat on Windows XP Service Pack 2.

A higher-level example

The sales.bat script performs a "salesperson laptop" installation for a organization. As you can see, this just performs a base installation, then adds Microsoft Office, Lotus Notes, the AT&T global network dialer, and the Shiva VPN client (now technically the Intel Netstructure VPN client, but I fear change).

These last two examples also illustrate how easily you can compose low-level scripts into high-level ones, no matter how many reboots the low-level scripts perform. Observe that if you make a change to the configuration in base.bat, the sales.bat script will automatically inherit it.

Unlimited composability is nice.

Database of "unattended" switches for various applications

To create an installation script for an application, you need to know how to install that application in "unattended" mode. To help you, we are collecting a list of unattended/silent mode installer switches for common installers and applications. Contributions to this list are most welcome.

Other utility scripts

Although is the most important script, there are others in Z:\bin which you might find useful.

Many of these scripts are good examples of how to use WMI, which can do quite a few things. WMI is a standard part of Windows 2000 and XP, and it is available as a free download for NT.
Configures the Automatic Updates feature introduced with Windows 2000 Service Pack 3. The registry settings it tweaks are not really documented; this third-party article is all we could find. Run --help for full usage instructions. Reboot to make the changes take effect.
Patches the registry to enable or disable the "automatic logon" facility. Can also set the default user name and domain. Run --help for complete documentation.
For some reason, all of my unattended installations end up displaying a boot menu with an unbootable "Previous Operating System on C:\" option. This even happens if I wipe the disk with zeroes first. This script edits the hidden system file boot.ini to get rid of the useless menu option.
This script adds a certificate to the ROOT certificate store. It depends on the CryptoAPI COM interface (CAPICOM), which you must install first. This means just copying the DLL to the right place and registering it; see capicom.bat for a sample installation script.
This is an AutoIt script to defragment the primary hard drive from a command prompt. Since we use a FAT partition which is converted to NTFS, the initial installation tends to be somewhat fragmented. I like to run a disk defragmentation before installing any software or hotfixes (to collect the free space), then again just before delivering the machine to the user.
According to Microsoft's Guide to Unattended Setup, passwords in the unattend.txt file are erased when the installation finishes. I have not found this to be true. So I wrote this script to replace all passwords in unattend.txt with X marks.
WMI has many useful classes. This is a generic script to enumerate the instances of any WMI class.

Running --help will give you brief usage instructions. Try running it with arguments like Win32_Process, Win32_OperatingSystem, Win32_BIOS, or Win32_BaseBoard.
The Windows 2000 Resource Kit includes a tool named instsrv.exe which lets you install a service from the command line. It is the only such tool we could find, but invoking it requires including the password on the command line. The script uses WMI to perform the same task, but it prompts you for the password instead of using a command-line argument.

Yes, strictly speaking, using this script means your installation will no longer be "fully unattended". But I do not like embedding passwords in world-readable scripts, and I hate using the GUI.
This script enables or disables the Remote Desktop service (formerly known as "Terminal Services"). It simply invokes the SetAllowTSConnections method of the Win32_TerminalServiceSetting WMI class. Run --allow=1 to enable the Remote Desktop and --allow=0 to disable it. As usual, the --help switch will yield full documentation.
This script takes two arguments, a variable name and a value. It sets the corresponding "System" environment variable to have that value, just as if you had set it from the GUI. This script can also set variables for specific users and variables on remote machines; run it with --help for more information.
This script creates a Windows shortcut. What makes it interesting is that it uses Windows Script Host so that it can locate the various special folders for you. So, for example, you could use "C:\Foo\foo.exe" special:AllUsersDesktop to create a desktop shortcut for all users.

Run --help for documentation.
Once upon a time, you could get shutdown.exe from the NT or 2000 Resource Kit. Now with XP, there is no Resource Kit, but shutdown.exe is standard. Of course, the new shutdown.exe uses completely different command-line switches from the old Resource Kit tool, making it annoying and confusing to use in a script. is a full-featured shutdown utility in 65 lines of Perl. Most of those lines are documentation and, of course, error checking. Run --help for details.

(Note that the installation scripts do not use this program; they use the .reboot directive to instead. But I am including it anyway for the heck of it.)
This script lets you set the "startup type" (automatic, manual, disabled, etc.) for a service from the command-line. There are probably other tools out there to do this, but I got tired of looking for them and wrote my own.
As you are no doubt aware, Windows displays lots of annoying first-time logon junk ("tips"). This script gets rid of them for Windows 2000 Service Pack 4. Note: This script represents my taste in things to disable; you may want to modify it for your site.

Incidentally, this script includes examples of editing the registry settings for the default user; that is, the settings inherited by every new user who logs into the machine. Mostapproaches I have seen to this involve copying NTUSER.DAT from some other profile to the default user profile, but with Perl, you can edit this registry hive directly.
Similarly, but for Windows XP Service Pack 2.
This script allows the output of one command to specify the environment for a second command. The script takes two arguments, which are the commands to run. The first command should output one or more lines of lines of the form:

The script will parse this output, set the corresponding variables in the local environment, and execute the second command.

You might be wondering why anybody would want this. Well, the Windows command prompt is a pretty weak scripting language, but I cannot bring myself to depend on something else when I have Perl around. So, for example, I have a Perl script at my site (z:\site\ which looks up the Office XP product key for the current machine in a software license spreadsheet, and prints a single line of the form PIDKEY=xxx. Then I invoke officexp.bat from sales.bat like this: z:\site\ officexp.bat

The result is that the correct product key for Office is provided at installation time so that the user is not prompted for it later. Isn't software licensing fun?