Throughout the past 2 years, Windows Scripting Solutions (formerly Win32 Scripting Journal) has presented articles about how to use Active Directory Service Interfaces (ADSI) in scripts to manage most elements of Windows 2000 and Windows NT enterprise administration. With these basic elements mastered, you now have the foundation to use ADSI and your favorite scripting language to create robust administrative applications.
In this column, I'll show you how to build on the techniques you've learned (and show you some new ones) so that you can create scripts that solve particularly difficult problems administrators face regularly. This month, I show you how to use ADSI and VBScript to tackle two password-related tasks: finding and removing unused machine accounts in a domain and changing the local Administrator account password on workstations and member servers.
Finding and removing unused machine accounts. When it comes to namespace housekeeping, most enterprises do a great job keeping user accounts current and accurate because of the risks associated with failing to do so. However, the same can't always be said for machine accounts. Although these accounts don't pose any significant risk to an enterprise's security, keeping the SAM up-to-date is important. You can often decrease the amount of time it takes to run scripts by simply reducing the number of invalid machine accounts in the SAM, especially if those scripts use enumeration functions at the domain level.
Changing the local Administrator account passwords. The biggest problem with changing the local Administrator account password is the sheer number of machines on which you need to change it. Because this tedious task is time-consuming, often the same password that was assigned to the Administrator account when the machine was built remains for the life of that build. Worse yet, many enterprises use a single password for the Administrator account on all machines. This practice yields a tremendous security risk because someone just needs to compromise one machine's account database to obtain unauthorized, untraceable access to all workstations and member servers. In addition, regularly checking and changing passwords can flag instances in which central administrators unknowingly lose their access to a machine because someone modified the Administrators or Users group membership.
To solve both problems simultaneously, you can write a script that will
- Enumerate all domains in an enterprise
- Enumerate all machine accounts in each domain
- Identify inactive machine accounts (i.e., accounts that have been off the network for the past 180 days)
- Change the Administrator account password on each remaining machine to a random value
- Report results back to a text file, ODBC data source, or Microsoft Excel workbook
Enumerating All Domains
To enumerate all domains in the enterprise, you bind to the ADSI service provider and step through the returned collection, as Listing 1 shows. When you run this code, it returns a list specifying all the domains in the WinNT: namespace. If you want to add to the enumeration's effectiveness, you can use additional For Each...Next statements within the enumeration to return a list of every computer in each domain, as the next section shows.
Enumerating All Machine Accounts
If you want to get a complete list of all machines in a given domain, you can use one of two approaches. You can either bind directly to the domain or nest another For Each...Next loop within the enumeration function that returns the list of domains.
First, let's look at the direct domain-binding method. Listing 2 shows this technique. This code is useful when you want to view the contents of only one domain and aren't concerned with any other domains that might exist in the enterprise.
Alternatively, you can add to the code used to enumerate all domains in the enterprise (i.e., Listing 1) to return the name of every machine defined in each domain in the WinNT: namespace. Listing 3 shows this code. Note that there is only one GetObject statement. During enumeration, binding to each child object is automatically established and need not be done explicitly. Unnecessary bindings cause slower performance of your application or script, so you should avoid them when possible.
Identifying Inactive Accounts
By default, all machines in a domain change their secure channel password every 7 days. If you query machine accounts for the age of this password, you can identify inactive accounts so that you can remove them. A good age to test for is 180 days to accommodate any laptop users who might be on extended travel.
Listing 4 contains code that lists the machine accounts that have been inactive for more than 180 days. As callout A in Listing 4 shows, an If...Then...Else statement examines the account to see whether the ADS_UF_WORKSTATION_TRUST_ACCOUNT user flag (&H1000) has been set, which prevents the removal of any domain controller (DC) machine accounts. Because a domain's PDC never changes its secure channel password, this code ensures that you don't inadvertently remove the PDC's machine account.
Changing the Administrator Account Password
As you enumerate the machines in each domain, you can try to connect to each workstation's local SAM to change the local Administrator account password. Listing 5 contains the code that you can use to accomplish this task. As callout A in Listing 5 shows, you first try to connect to each machine. If you make the connection and have the necessary permissions to change the local Administrator account password, you can set a new random 8-character, mixed-case password.
The code at callout A uses the IADsUser::SetPassword method to set the new password. The code at callout B in Listing 5 generates a random value for that password. If you need a stronger password, you can easily modify the code at callout B to generate a different value.
Reporting the Results
Writing results to a message box or console window is the easiest way to report results. However, you'll likely want to capture and store the generated random passwords in a more permanent medium. In addition, you'll also likely want to capture the names of the machines you couldn't connect with and the machines on which you didn't have the necessary permissions to change the Administrator account password. Not having the necessary permissions could potentially indicate that someone modified the Administrators or Users group membership.
Capturing the results in an ODBC datasource is best. However, this solution might not be practical for smaller, single-run tasks, in which case you can effectively use a delimited text file. As the excerpt of code in Listing 6 shows, you can use the FileSystemObject object to create the delimited text file, which you can later import into Microsoft Access or Excel for query and further manipulation. You can find the entire listing in the Code Library on the Windows Scripting Solutions Web site (http://www.winscriptingsolutions.com).
Combining Forces: Excel Password Management Tool
I created a fully functional application that performs the password administration tasks I've discussed here and generates a report detailing the results. You can download this application from the Code Library on the Windows Scripting Solutions Web site. This application uses Excel 2000 as a front end. To view the application's code, open the application in Excel 2000, then press Alt+F11 to access the Visual Basic Editor. To use the Excel application, follow the directions in the workbook's Instructions sheet.
A Powerful Tool to Tackle Complex Tasks
ADSI is a powerful set of interfaces that you can use to tackle complex Win2K and NT administrative tasks in your enterprise. By adapting the code I've presented here, you can generate robust password-management applications for your enterprise. Whether you want to simply run reports or change Administrator account passwords, ADSI can help.
Next month, I'll show you how to automate more password-management tasks, specifically how to manage passwords for service accounts in an enterprise.