Use Set-LocalPassword to ensure a more secure environment
Although Active Directory (AD) provides convenient centralized account management for complex domains, it doesn't handle one account management problem: updating passwords for local Administrator accounts that reside on a member server that isn't a domain controller (DC). The more solid a network is, the easier it is to forget that local Administrator accounts also have local passwords. These accounts can be helpful when you need to get into a computer that can't access a DC and the cached credentials of a domain administrator are absent from the system. However, local Administrator accounts are security vulnerabilities if their passwords are weak or aren't updated regularly. (For more information about cached credentials, see the Microsoft article "How Interactive Logon Works," http://www.microsoft.com/technet/prodtechnol/windowsserver2003/library/TechRef/779885d9-e5e9-4f27-9c14-5bbe77b056ba.mspx.)
If you have a solid network infrastructure with user profiles stored centrally, one security option is simply to disable the local Administrator account, or deny access to it if you're on Windows 2000. (For more information about disabling the local Administrator accounts, see the Microsoft article "How to disable the Local Administrator account in Windows," http://support.microsoft.com/?kbid=281140.) Disabling the local Administrator account means you'll no longer have administrative concerns about it, and you can enable the account temporarily for planned offline work. If you should have a workstation OS failure, the worst problem you're likely to face is automatically rebuilding the workstation using the user's data, which should be safely stored on the network.
However, organizations without a neatly centralized network infrastructure need more options for changing passwords. Some organizations might not have the option of disabling the local Administrator account. For example, they might need to keep that account enabled so that they can still get into the system if the computer loses its connection with the DC.
Other organizations might need to deal with regular offline use, so they must disable cached credentials as part of their security policies. For these organizations, one option is to automate password updates with the Microsoft Active Directory Service Interfaces (ADSI) WinNT provider. Designed for compatibility with Windows NT 4.0 networks, this provider lets you access individual workstation security databases. The WinNT provider is available on Win2K and later systems; you can install it on NT 4.0 by adding the ADSI client extensions from the Windows Server 2003 or Win2K Server CD-ROM.
Using the WinNT provider to change local Administrator account passwords looks extremely easy in the ADSI documentation because the demonstration script consists of only a few lines of code. However, problems often arise when this demonstration script is used in the real world. Let's look at the problems that occur as well as at other considerations to keep in mind when you create a script to update local Administrator account passwords.
Welcome to the Real World
As the demonstration script in the ADSI documentation shows, you can remotely change local Administrator passwords using only a few lines of Windows Script Host (WSH) VBScript code. Let's say you have a computer named PC903 with a local account named la whose password you want to set to tw1nkl3. PC903 is a member of a domain, and you're logged on to that domain as a domain Administrator, which by default has sufficient privileges to remotely administer PC903. The following script is all you'll need:
Set user = GetObject( _ "WinNT://ws903b/la,user") user.SetPassword "tw1nkl3" user.SetInfo
This demonstration script is concise, as are most of the sample scripts you'll find in the ADSI documentation. But you have a bit more to do to turn this code into a robust, reliable real-world tool.
One difficulty with this demonstration script is that it doesn't perform any authentication. It simply uses your current credentials to confirm that you have the right to connect to the remote system and change a password. Theoretically, you could stay logged on as a non-administrative user and specify an administrative username and password in the script for when it connects to AD through ADSI on the remote computer. However, after trying this approach, I discovered it produces too many secondary problems while providing minimal extra value.
The first problem I encountered involved maddeningly erratic logon failures on a small domain. After some research, I found that this is a known problem documented in the Microsoft article "User authentication issues with the Active Directory Service Interfaces WinNT provider" (http://support.microsoft.com/?id=218497). If a connection is already established to the computer you want to administer, specifying other credentials will fail.
After I reconciled myself to using the RunAs command or logging on directly as an administrative user, I realized that doing so made this demonstration script simpler and more secure. If you use workstation administration privileges to log on to an account and run the script, you no longer need to worry about how to handle and pass administrator credentials. Windows handles everything automatically.
Demonstration scripts written by programmers generally seem to use one of two error-handling strategies: completely suppress errors, or simply fail and exit. Those strategies are useful for demonstrating a technology, but neither approach is an option when your goal is a robust script.
Ideally, a password-update script should notify you when a password change fails and tell you why it failed. However, you don't want the script to exit just because the first of 200 machines whose Administrator passwords need to be changed is offline. Instead, for each password change attempt, the script should return the machine name, whether the attempt succeeded or failed, and if it failed, the best short explanation possible. Before I go into details about how the script displays output, let's look at the common causes of error.
Using connection strings (e.g., WinNT://ws903b/la,user) makes WinNT calls simple. However, if the call fails, the WinNT provider returns only a numeric error code that requires research to understand. Fortunately, only three errors are common in LAN-based remote connections. All three errors have clear causes, so if you have the script test for those errors and return useful information, the script can be very robust.
The first error occurs when the remote system isn't available. In this case, you usually have to wait a few seconds before the script errors out. In a network situation, this error typically occurs when some machines are inaccessible, usually because they're offline or because they're laptop units that aren't currently connected to the network.
The second error occurs when the user doesn't have sufficient privileges to connect, typically because the user's account doesn't inherit permissions to manage the remote workstation. This situation can arise when the user doesn't log on as a user with domain administration privileges or when the remote system doesn't participate in domain security.
The third error occurs when the specified username doesn't exist on the workstation. This error is a common side effect of irregular naming practices.
As I mentioned previously, these are the three errors most often encountered, but they don't constitute an exhaustive list. You need to account for other possible errors—whatever their causes—in your script. Although your script doesn't need to provide definitive descriptions for them, it should at least return the hexadecimal-formatted error code and a failure message. If you have the hex-formatted error code, you can research a particular problem in the ADSI Error Codes Web page at http://msdn.microsoft.com/library/en-us/adsi/adsi/adsi_error_codes.asp.
Changing a local Administrator password on a remote system requires only three pieces of information: an account name, a computer name, and the new password. (You don't need to know the old or current password.) However, how you provide this information in a script requires some thought.
Local Administrator account name. You could hard-code the name of the local Administrator account in the script or have the script use a SID to obtain the account name. However, neither approach will yield a flexible tool that you can use with more than one account. Designing the script to handle any account name makes it more universally useful. The simplest solution is to require the user to supply the account name as the script's first—and possibly only—command-line argument.
Computer name. When you have only a handful of computers, one technique is to let the user specify the computer names as a parameter on the command line. If you have many computers, though, you'll probably want to have the script take advantage of the shell's standard input, which lets the script read computer names from a file or obtain them from another command's output.
New password. Obscured, confirmed entry of the new password is the most desirable approach. You can achieve this type of entry using the ScriptPW component (scriptpw.dll) on a Windows 2003 or Windows XP system. ScriptPW's Password object prompts the user who's launching the script for a password, then masks the input so that it isn't revealed to someone looking over the user's shoulder. However, using ScriptPW.Password creates a problem for scripts run from systems that don't have the ScriptPW.Password object, such as Win2K and NT 4.0 systems. If the ability to use the script on legacy networks outweighs the risk of not obscuring the password, you might want to have users enter the new password as a command-line parameter.
A Sample Real-World Solution
After having discovered the problems with the demonstration script and deciding how to provide the local Administrator account name, computer name, and new password, I created the Set-LocalPassword tool. This tool consists of two files: Set-LocalPassword.wsf, which changes the passwords, and Set-LocalPassword.cmd, which forces WSH to handle command-line input and output correctly.
The Set-LocalPassword tool is designed to handle authentication as well as notify you whether each password change was successful and, if not, why it failed. The Set-LocalPassword tool is also designed so that you specify the local Administrator account name on the command line. To make the tool user-friendly, you can provide the names of the computers on which to run the script from an input file (if you have many computers) or from the command line (if you have only a few computers). To accommodate using the tool on a variety of Windows OSs, the script obtains the new password one of two ways. For Win2K and NT 4.0 systems, the script uses the password you enter on the command line. For Windows 2003 and XP systems, the script uses the ScriptPW.Password object to get the password. The GetConfirmed-Password function, which Listing 1 shows, makes confirmed hidden password entry possible. You can use this function in any VBScript code running on an XP or later system.
You can download the Set-LocalPassword tool from the Windows IT Security Web site. Go to http://www.windowsitpro.com/windowssecurity, enter 48956 in the InstantDoc ID text box, then click the 48956.zip hotlink. Installing and removing the Set-LocalPassword tool is as simple as copying the files to your system and deleting them when you no longer want them.
Before you can use the Set-LocalPassword tool, you need to get a list of names for those computers on which you'll be changing the local Administrator account passwords. In theory, the names can be NetBIOS-style names, DNS names (fully qualified or not), or even IP addresses. I don't recommend IP addresses, however, because they might change quickly and won't help you track down problem machines. For a complex network, you might have to use fully qualified DNS names. If you have many computers, you can enter the names in an input .txt file. Place each name on a separate line.
If you don't already have a way to generate the list of machines on a network, you can export the names from the Microsoft Management Console (MMC) Active Directory Users and Computers snap-in or use the Dsquery utility from the Windows Server 2003 Administration Tools Pack. Dsquery works on Win2K domains as long as they're running at least Win2K Service Pack 3 (SP3). If you use Dsquery, however, you need to make sure you don't have any DCs in the list unless you intend to change the password for a domain account that has the same name as the workstation accounts you'll be changing. Dsquery also returns computer names within quotation marks. Set-LocalPassword.wsf automatically strips out quotation marks, but they generally cause problems for other tools that accept computer names as input.
How to Use the Tool
How you launch the Set-LocalPassword tool depends on your Windows OSs and whether you have many or only a few computers. To use the Set-LocalPassword tool on a Windows 2003 or XP system that has many computers, log on to the domain using an account that has workstation administration rights, open a command-shell window, and enter the command to run the tool. For example, suppose your input file is C:\ computers.txt, the workstation administrator accounts are all named admin, and you want to set the account passwords to tw1nkl3. On a Windows 2003 or XP system, you'd execute the command
Set-LocalPassword will prompt you to enter and confirm the new password, then begin changing the passwords. When a password change succeeds, the output shows the machine name followed by the word SUCCESS. If you have a noncatastrophic failure, the script displays the machine name followed by the word FAILURE and a brief description of the cause. If you don't want to display the success messages, you can suppress them with the /Q switch to display only the machines that couldn't be updated and the possible reasons for the failures.
You can redirect the script's output to a file. Because the failures use the console's standard error stream, the redirection must specify the error stream, which has an ID of 2. The following command sends success messages to the console and redirects failure messages to the error.log file:Set-LocalPassword admin error.log
If you want to change passwords on just a few machines, you can specify each machine name on the command line after the name of the account to change:Set-LocalPassword admin WS01 WS02 WS03 WS04
To use the Set-LocalPassword tool on a Win2K or NT 4.0 system on which the ADSI extensions are installed, you need to provide the password on the command line. Log on to the domain using an account that has workstation administration rights, open a command-shell window, and run the commandSet-LocalPassword admin /P: tw1nkl3
Changing Local Administrator Passwords Made Easy
Organizations without centralized network infrastructures need a way to regularly change their local Administrator account passwords to avoid security vulnerabilities. The Set-LocalPassword tool lets those organizations easily change these important passwords, no matter whether their Windows networks are large or small.
Alex K.Angelopoulos (alexangelopoulos@ hotmail.com) is a senior network engineer who does IT consulting work in Indiana. He is an MCSE, an MCP+I, and an MVP.