A practically painless primer to Windows shell scripting
Several years ago, my phone rang late at night. A server hardware upgrade at my company had gone bad. The network card on one of our main file servers failed and no longer had network connectivity. We had spares but were unable to install them because the server didn't seem to have any cached domain credentials on it and the local Administrator account name and password combination didn't work. Thus, we were locked out of our own box. At that time, no handy break-in tools were available to help us out. We eventually hacked into our own server, but in my mind, that experience underscored the importance of maintaining, managing, and verifying server Administrator account names and passwords. Often, systems administrators emphasize the importance of changing Administrator passwords for security reasons. This emphasis is definitely justified because the local Administrator account holds the key to the information kingdom on that server. However, along with this security element, you need to consider the unusual situations in which you must have 100 percent confidence that, when you sit down at the console, your logon information will work. There isn't a more gut-wrenching feeling than having an improper-username/password warning pop up when you're attempting a local logon.
Manually checking and changing passwords on many servers takes a lot of time. Because of the time it takes, an administrator might be tempted to check and change the passwords less often, thereby reducing network security. And errors might creep in—errors that could lead to a serious situation like the one I just described.
Fortunately, scripts can perform repetitive operations such as checking and changing passwords with greater speed and accuracy than is possible with the built-in tools that are typically used. Scripts also offer several advantages over third-party GUI tools. (To learn about these advantages, see the sidebar "GUI Utilities vs. Scripted Solutions," page 62.) Even if you've never done any scripting or code development, you can easily get started in Windows shell scripting by looking at and using PWcheck-change, a fully functional script that checks and changes passwords. After I give you a quick introduction to this script, you'll get to peek under the hood. Then, I'll show you how to customize and use PWcheck-change.
PWcheck-change is a simple, practical script that you can use to accurately check and change passwords on many servers in just a few seconds. To effectively maintain, manage, and verify passwords with PWcheck-change, you need to run the script in three situations. First, you need to periodically run the script to perform routine password changes. PWcheck-change will reset the passwords when you launch it with the 6
Second, you need to periodically run the script to determine whether any passwords have changed that shouldn't have. PWcheck-change will verify your servers' passwords and report any incorrect passwords when you launch it with no argument:
Finally, you need to run the script as soon as possible in an emergency. For example, if the computer security staff determines that the passwords on one or more servers have been compromised, you need to make an emergency run of the script as quickly as possible.
When I wrote PWcheck-change, I had to make several assumptions about the users who would be running the script. There are five assumptions:
1.The script assumes that you're following the standard best practice of using unique Administrator account names and unique passwords for your servers. However, if all your Administrator account names are the same or if you're using one password for all your servers, you can specify that common account name or password for each server, the same way you'd specify the unique name or password.
2.The script assumes that your servers are online. Because Administrator account information is in the local server account database and not in Active Directory (AD), your servers must be online for changes to take place. PWcheck-change will determine whether the server is online before attempting to check or change a password.
3.The script assumes that your passwords comply with the password length and complexity restrictions you've configured for your system. For example, if you've set a minimum length requirement of eight characters for your Administrator passwords, you shouldn't use the script to set a password that's only six characters long. The same holds true if you're using strong passwords—don't use the script to set a password that doesn't contain the required character mix. The good news is, if you violate your own password policies, any errors in the change will be recorded to the log file.
4.The script assumes that your passwords don't include any Windows shell scripting reserved characters. There are a handful of characters that might conceivably be used in passwords that are also reserved characters in shell scripting. Avoid using the following reserved characters as password characters:
, ^ & < > ( ) | = " ;
If you attempt to use these characters in your passwords, the password change will likely fail.
The following characters are OK to use:
$ % @ # ! ` ' ~ . + _ - * : \ /
However, the difference between a back tic (`) and a single quote (') can be difficult to distinguish, as are some common letters and numbers. For example, the lowercase letter l, an uppercase letter I, and the number 1 can all look alike, as can the uppercase letter O and the number 0. You can use hard-to-distinguish characters, letters, and numbers, but make sure that you're using the one you intended.
5.The script assumes that there are no persistent connections open to any of your servers. In check mode, PWcheck-change connects through the IPC$ share to test the username and password combination. If a server has a persistent connection open, the script might not be able to kill the connection and you might get incorrect feedback about your passwords.
A Peek Under the Hood
If you're trying your hand at scripting for the first time, looking under the hood of PWcheck-change might be a bit intimidating. Don't be discouraged. Most all scriptwriters start out thinking that scripts are hard to understand. But, after a while, they start borrowing others' scripts, then they start modifying those scripts. Eventually, they end up writing scripts from scratch.
Listing 1 shows PWcheck-change. Like most all scripts, PWcheck-change includes several important sections of code.
A header area that contains initialization code and configuration information. Callout A highlights the header area, which usually contains some initialization code. The initialization code typically includes the Echo Off command, which turns off a command-echoing feature (so that only a command's results and not the command itself will be displayed on screen), and the Title command, which specifies a title for the command-shell window that will open at run time. You can further customize the command-shell window by, for example, changing the window's font color, background color, or size.
Next, the header area includes the Setlocal command. This command keeps variables in the script local to the script.
Finally, PWcheck-change's header area provides comments about how to configure the script. Scripts often use an input file (which contains information that the script needs to run), an output file (which holds results and other data produced by the script), and tools (Microsoft or third-party tools that the script needs to perform a task). When a script uses an input file, output file, or tool, you need to specify its location. In PWcheck-change, you need to configure paths to an input file (which contains the server name, Administrative account name, and password of each server on which you want to check or change passwords), output file, and two tools (Sysinternals' PsPassword and the local.exe resource-kit utility).
In the header area, you're not limited to comments about configuration information. You can also include comments about other important details, such as the script's author and version number.
Code that reads in the information in the input file. If a script uses an input file, you need code that reads in the information from that file. As the code at callout B shows, PWcheck-change uses a For command to read in and parse each line in the input file. The delims option specifies the delimiter to use to split each line into segments. In this case, a comma is the delimiter. The tokens option specifies the segments (i.e., values) to capture in each line. PWcheck-change captures the first three segments, which it assigns to the Server, AdminName, and Password variables, respectively. The usebackq option handles any spaces in the input file path by allowing the use of double quotes to enclose the file path.
Code that performs an operation or chain of operations. People often write scripts to automate tasks. These scripts must include a section of code that performs an operation or chain of operations to achieve those tasks. Not surprisingly, PWcheck-change includes code that performs a chain of operations, as the code at callout D shows. But what might surprise you is that the first operation is a ping test rather than the operation to check or change a password. Some utilities have long timeouts when they're directed to perform a function on a server that's not online. Thus, it's wise to first test whether the server is online with a simple ping test.
Note that the Ping command is embedded in a For command. Previously, the script used a For command to parse an input file. In this instance, the script uses a For command to parse the Ping command's output and look for the string Reply. If the script doesn't find this string (i.e., the server is offline), it logs an error. If the script finds the string (i.e., the server is online), it begins the password change or check operation.
To change passwords, PWcheck-change uses PsPasswd, which expands the functionality of the built-in Net User command. Net User changes local account and domain account passwords, but it can't change a remote system's local account password, which is what the script needs to do to change the Administrator account passwords.
PsPasswd can change local account passwords on remote systems, but it's designed to change the password on only one machine, which isn't very helpful if you have more than one machine in your environment. Thus, PWcheck-change calls that single-change utility multiple times after reading from the input file the information about the server name, Administrator account name, and password for each server.
In check mode, PWcheck-change skips over the change code and uses the Net Use command and an IPC$ connection to test the existing Administrator password. When a connection is made, the username and password information are correct. If a connection isn't made, the script drops into a section that handles errors.
Code that handles errors that occur while attempting the various operations. As you've seen, some parts of PWcheck-change already handle a few types of errors. However, the script also includes code that handles two special types of errors: incorrect username and incorrect server permissions.
Sometimes, a well-meaning but absent-minded administrator might change the password or rename the administrator account. To discover this "password drift," the script uses local.exe to determine whether the username is in the local Administrators group, as callout E shows. If the username isn't in the local Administrators group, either an incorrect name is specified in the input file or the correct name is specified but the server no longer has the correct permissions (e.g., someone erroneously changed the Administrator account name on the server). In either case, the script logs an error message that notes the account wasn't found. If the name in the input file and the server permissions are both correct but there's some other type of problem, the script considers the error a general failure and logs the error message PW Check/Change Failure.
Code that sends information to a file or the console. In almost every section, PWcheck-change uses the Echo command to send information (e.g., current date and time, a success or failure message, a command's output) to either the output file or the console. If the Echo command is followed by the code
the script writes the information to the comma-separated value (CSV) output file specified in the header area. If the Echo command isn't followed by the >> redirect symbol, information goes to the console.
There are other ways you can send information to a file or another medium, including:
- You can copy information to a file in a shared folder.
- You can append information to an existing file.
- You can create an HTML file and upload that file to a Web server.
- You can use SMTP mail and Blat (a freeware utility) to send an email or pager message.
Code that performs cleanup operations. Scripts often include cleanup operations, which usually consist of deleting temporary files the script created and closing out local variables that the script used. As the code at callout C shows, PWcheck-change uses the Endlocal command to close out the local variables. The script didn't use any temporary files, so it doesn't include any cleanup code to delete them.
I often joke that the thousands of scripts I've written are really all the same script with just a couple of changes. Indeed, there's an element of truth to that quip. However many scripts you want to write, you just need to include these basic sections of code and follow a few recommended practices. The sidebar "Scripting Best Practices," page 66, discusses those practices.
How to Customize and Use the Script
I tested PWcheck-change on systems running Windows Server 2003, Windows 2000 Server, and Windows NT Server 4.0. To use PWcheck-change in your environment, follow these steps:
1.Download PWcheck-change from the Windows IT Pro Web site. (Column widths in the printed publication force us to wrap code lines, which might cause the printed code to run incorrectly.) Go to http://www.windowsitpro.com, enter InstantDoc ID 43591 in the InstantDoc ID text box, then click the 43591.zip hotlink.
2.Download PsPasswd from Sysinternals (http://www.sysinternals.com/ntw2k/
3.If you don't already have local.exe, obtain this tool from your Windows resource kit.
4.Create the input file. Include the information for each server (i.e., server name, Administrator account name, and password) on a separate line. Separate the server name, Administrator account name, and password with commas.
5.In the script's header area, configure the paths to the input file, output file, PsPasswd, and local.exe.
6.Test the script on a few servers. In the input file, you might want to intentionally add a server that's offline or an incorrect Administrator account name to become familiar with how the script handles and logs these errors. Run the script in the default check mode. After you're sure that the script works correctly in this mode, add the -Change argument to test the password-change operation. Review the log file for success and failure information, and log on to the server to verify that the passwords were changed correctly.
7.After thoroughly testing the script, use it in your production environment. If you run the script as a scheduled task, you must schedule the task under a user account that has Administrator group membership on the targeted servers.
8.After you have completed a password check or change run, secure the input and output files in a locked-down area. Be sure that server administrators have access to this area in case the Administrator account names and passwords are needed for an emergency local logon.
In the real world, unusual situations do arise, such as being locked out of a server. By adapting and using PWcheck-change, you'll not only be prepared to solve any unexpected password problems but also be able to quickly and easily maintain, manage, and verify your servers' Administrator account passwords. Plus, by adapting and using this script, you'll become more familiar and more comfortable with a powerful tool: Windows shell scripting.