Although I'm a proponent of automating repetitive tasks with scripts, I've suspected the majority of Windows NT systems administrators prefer to use a GUI, resource kit, or third-party utility. My suspicions about scripting were confirmed recently at TechEd, where I had many interesting conversations with industry peers. The most common reason that NT systems administrators cited for preferring prebuilt tools is time.
Here's how one TechEd conversation went. One gentleman sitting next me during a session expressed the need for a command-line utility that unlocks locked NT user accounts, regardless of where the accounts reside. I mentioned that he could easily script the task. Based on his response, I don't think that was the answer he wanted to hear. So I asked for his business card and told him I'd see what I could do.
The UnlckUsr.vbs Script
You can use many different scripting languages to write a script to unlock user accounts. For example, you can use ActivePerl with the AdminMisc module, ActivePerl with Active Directory Service Interfaces (ADSI) 2.0 via Object Linking and Embedding (OLE), Advanced Systems Concepts' XLNT 2.0, FastLane Technologies' FINAL 6.21, or Windows Scripting Host (WSH) with ADSI 2.0. Based on the requirements and the person requesting the utility, I decided to use WSH (via Visual Basic Script--VBScript) with ADSI 2.0. (For an example of how to use ActivePerl with the AdminMisc module, see "How to Manage Your Enterprise's Passwords the Easy Way," August 1998.)
Listing 1, page 202, contains the script, UnlckUsr.vbs, I wrote to unlock locked user accounts. This simple script unlocks locked NT user accounts in a domain, member server, or workstation, providing that the user running the script has sufficient privileges. The script uses ADSI 2.0 to manipulate the UF_LOCKOUT bit. The UserFlags property of the WinNT provider user object defines this bit. If you're new to ADSI, the WinNT provider (adsnt.dll) is the .dll file that provides access to the NT 3.51 and NT 4.0 directories (i.e., the Security Accounts ManagerSAMdatabases). Thus, you must run this script from an NT 4.0 Service Pack 3 (SP3) workstation or server on which you have installed ADSI 2.0 Runtime and WSH. To obtain ADSI 2.0, go to http://www.microsoft.com/ntserver/basics/future/actdirinterfaces/default.asp. To get WSH, go to http://www.microsoft.com/scripting. (For more information about ADSI and WSH, see Keith Pleas, "Windows Scripting Host in Action," February 1998, and Ken Spencer, "Creating Applications with ADSI and VB," a Windows NT Magazine Web Exclusive, http://www.winntmag.com.)
Before I walk you through the script, let's take a closer look at the UserFlags property. This property is one of 18 custom properties that make up the WinNT provider's user object. The UserFlags property lets you access and set several important user attributes. Table 1, page 203, lists the constants that make up the UserFlags property and the individual bits that you can set. As Table 1 shows, UserFlags provides access to user settings, such as Account Disabled, Account Locked Out, User Cannot Change Password, and Password Never Expires.
Now let's look at how you can access and change one of the settable UserFlags bits in a script. UnlckUsr.vbs in Listing 1 begins by declaring the variables used in the script and the UF_LOCKOUT constant. ADSI defines the constant value of UF_LOCKOUT as 0x0010, so the script's UF_LOCKOUT constant is &H0010, with &H representing hexadecimal numbers in VBScript. This value represents a specific bit position within the UserFlags property. You'll later use this value to test whether the UF_LOCKOUT bit is on or off, which, in turn, tells you whether the account is locked or unlocked. You need to know that you can only unlock locked user accounts; you can't lock an unlocked account.
Next, the script checks the number of command-line arguments. UnlckUsr.vbs requires two command-line arguments in the following order: the domain or server name containing the target user account followed by the user account name. If you don't include these arguments, the script calls the Usage subroutine, which displays usage instructions via the WScript.Echo method and then exits. Assuming that you supply the two required command-line arguments in the right order, the script assigns their values to strDomainOrHost and strUsername, respectively.
At callout A in Listing 1, the script instantiates an ADSI user object for the specified user. This user object gives the script access to the user properties. You use the GetObject method for this purpose, passing the method the name of the target namespace ("WinNT://" for NT 3.5x and NT 4.0 SAM databases), domain or server name (strDomainOrHost), and user account name (strUserName). The last value (",User") in the call to GetObject is important. It specifies the class of object, telling ADSI Runtime that it needs to look for a user object. This value results in much more efficient code because ADSI Runtime knows the type of object to fetch. If you don't include this optional value, ADSI Runtime browses every object in the namespace (e.g., computers, groups, and users) until it locates the named object. The amount of time this value saves in large domains is astounding.
After initializing objUser, the script uses the Get method to fetch the user properties. The script retrieves two properties, FullName and UserFlags, and assigns them to strName and cUserFlags, respectively. ADSI Runtime retrieves all the user properties and stores the values in a local, dynamic cache. Because ADSI Runtime caches the values, it reduces the number of network trips required to fetch subsequent data--another important timesaver when you're using ADSI Runtime in large domains.
The script then uses the logical AND operator to compare the bit position defined by the UF_LOCKOUT constant to the corresponding bit value in cUserFlags to determine whether the account is locked. If the result is false, the script displays a message stating the account is not locked, destroys any previously created objects, and exits. If the result is true, the account is locked, in which case the script proceeds to unlock it.
At B, the script uses the Put method to unlock the locked account. Before you can use the Put method for the UserFlags property, you must turn off the LOCKOUT bit. You turn off this bit with XOR, the exclusive OR operator. XOR behaves as follows: If both bits are true (1 and 1) or both bits are false (0 and 0), the result is false (0). If one bit is true (1) and the other bit is false (0), the result is true (1). With locked accounts, both bits are true. Thus, when the script encounters Xor, the result is false, which changes the UF_LOCKOUT bit in the UserFlags property from 1 to 0. However, the Put method updates the cache, not the underlying object in the directory, because ADSI Runtime caches the user information locally. Therefore, the script invokes the SetInfo method to update the corresponding object in the directory and unlock the locked account.
Finally, the script uses the Get method to fetch the UserFlags property a second time to check whether the change was successful. The script uses the same logical AND operation performed earlier and, based on the result, displays an appropriate message to the user.
If You're Still Uneasy ...
If you like the results you can achieve with scripting but still believe using scripts is too time-intensive, here's good news: When NT 5.0 becomes reality, this type of solution will be no different from using a batch file. I'm excited about the path Microsoft has laid for reusable, scriptable components. Technologies such as ActiveX Data Object (ADO), ADSI, and Collaboration Data Object (CDO) combined with hundreds of third-party components offer NT systems administrators flexibility--a feature that's hard to find in prebuilt tools and utilities. However, a huge market still exists for those vendors that eventually figure out that the NT tools and utilities market remains largely untapped. Although the NT resource kits are useful, they are far from being an end-all NT utility suite.