This script gives your Help desk an easy way to add user accounts to AD

To help your Help desk staff members create user accounts in Windows 2000's Active Directory (AD), you can write a user-creation script. As I described in "Handy Web Script Creates User Accounts, Part 1," October 2001, this script displays a form in which the Help desk staff members enter the data needed to create an account for a user. When they submit the form, the script creates the user account if one doesn't already exist. Now, let's look at how the script works and how you can customize the script for your network.

Building and Calling the Form
The first step in scripting user-account creation is to build a simple form that asks for all the information that the operator must type in to create a user account. Listing 1, page 2, contains the user-creation script, UserCreate.asp. The Code Library on the Windows Scripting Solutions Web site (http://www.winscripting contains a version of this script that includes comments that describe the code in detail.

Callout C in Listing 1 highlights the Form procedure that builds the form. You use a mixture of VBScript and HTML code to create the Active Server Pages (ASP) form, enclosing the VBScript code between . Notice that the VBScript end marker (%>) appears right before the HTML form begins. You could use the ASP syntax to write the form, but it's simpler to exit the scripting language, use simple HTML syntax to build the form, then return to VBScript to end the procedure. You can link a style sheet to a form to further enhance its appearance.

Your form should request as little information as possible from the operator; the script should fill in the rest. For example, the user-creation form requests just the four-digit extension of the user's phone number. Using this information and the organizational unit (OU) that the operator selects, the script can enter the entire user phone number in the AD store user account.

Before calling the form, the user-creation script determines whether an operator has entered anything in the User account name, First name, or Last name text box. At callout F, you can see that if one or more of these three text boxes are empty, the script calls the Form procedure; otherwise, the script retrieves the user data and initializes the corresponding script variables.

Validating the Data
After the operator fills in the text boxes, selects or clears the Account Enabled check box, and selects an OU, he or she clicks Submit. The Submit action kicks off the client-side data-validation procedure, which appears at callout E. This validation verifies that all text boxes, except for the optional User's job title text box, are filled in. It also verifies that the 4 digit extension # field actually contains numeric data. If one of the four required text fields is empty when the operator clicks Submit, the procedure displays an error message that prompts the operator for the missing information. After the operator clicks OK in the message box, the validation procedure highlights the empty text box so that the operator can enter the necessary information.

You could enhance client-side data validation in many ways. For example, you could add a validation procedure that checks for four digits in the 4 digit extension # text box. You could add string manipulation so that the first letter of the user account name, first name, and last name is capitalized. If you have a naming convention for user account names, you could remove the User account name text box from the form and create a simple routine to create the user account name from other information (e.g., first name, last name) that the operator enters. Finally, you could use Dynamic HTML (DHTML) to write a procedure to further enhance data validation. For example, you could use DHTML so that the Web page verifies entries in required fields before the operator clicks Submit. To keep the sample code as simple as possible, I haven't included these features.

Connecting to the Provider
After data validation, the next step is to establish a connection to the AD store through the binding process. The two methods for binding to the AD store are server binding and serverless binding. For server binding, you hard-code a domain controller (DC) computer name in your binding string. In serverless binding, the Microsoft Active Directory Service Interfaces (ADSI) provider consults Win2K locator services to determine the best DC for the client. The best DC is one in the domain of the currently logged-on user and preferably at the same site. The BindContainer function at callout A uses serverless binding to bind to the AD store's root (LDAP://rootDSE), obtain the domain name (defaultNamingContext), then initialize a variable that holds the container path (sContainerPath) to the OU in which the script will create the user account.

For example, if the script connects to a DC named server1.domain01.local and the container name is OrgUnit01, the default naming context is DC= domain01,DC=local. This value initializes the variable sADsPath. Notice that the DC's computer name doesn't appear in the default naming context; any DC in the site can answer the binding request. The value of sContainer- Path is then LDAP://OU=OrgUnit 01, DC=domain01,DC=local. You don't need to modify this function for your environment because it will find the proper value for sADsPath. For more information about AD binding, see Bob Wells' Windows 2000 Magazine article "Easy Active Directory Scripting for Systems Administrators, Part 1,", InstantDoc ID 9168.

Maintaining hard-coded DC names in a script is difficult. If you or someone else changes the server name or domain name and you forget to change the name in the script, the script won't work. One disadvantage of serverless binding is that user accounts created on a DC at the current site aren't available immediately to other sites—they must first be replicated to DCs at those sites. Intrasite replication happens frequently, but intersite replication occurs less often (the default intersite replication interval is 180 minutes). Increasing the intersite replication frequency is a better solution than hard-coding DC server names. If increasing the frequency of all intersite replication isn't an option, consider increasing the frequency of replication between sites where operators create many user accounts or increasing replication frequency for short periods during the day.

Checking for an Existing User Account
Before you create a user account in the AD store, you should make sure the user doesn't already have one. Binding to the AD store initialized the sADsPath variable, and entering user account information into the form initialized the associated variables. The script's CheckForUser function uses all this information to query the AD store for an existing user account.

Using the ActiveX Data Objects (ADO) API is an easy way to query the AD store. ADO provides the Connection, Command, and RecordSet objects for accessing data from the OLE DB provider for Microsoft Active Directory Service (aka the Active Directory Provider). Callout B shows the CheckForUser function that performs the AD store query.

The Connection object authenticates the user, the Command object builds the Select statement, and the RecordSet object executes and returns the results of the query. After declaring local variables (oConnection, oCommand, and oRecordSet), the CheckForUser function creates a Connection object that uses the Active Directory Provider. Because the script doesn't specify user credentials, the Connection object uses the credentials of the logged-on user to establish an ADO connection.

CheckForUser then creates a Command object, sets it to the active ADO connection, and initializes the Command object's CommandText property with a Select statement. The Select statement queries the AD store for a SAM account name and a full name. (All Win2K accounts require a unique downlevel SAM account name—i.e., a user account that Win2K can read.) Notice that I limited the query to the Person object category to return results more quickly.

The RecordSet object then executes the query. If the RecordSet object returns any records, CheckForUser informs the operator and the script exits. If RecordSet doesn't find any objects, the script can create a user account.

Connecting to the OU and Adding the Account
Connecting to the OU and adding the user are the user-creation script's final tasks. In these steps, the script uses all the information the operator entered in the form and additional information the script provides to create the user account in the proper OU, set user account properties, and provide feedback to the operator.

The script is already connected to the AD store; the next step is to bind to the OU, or container, in which you want to create the user account. The line at

callout G accomplishes this task. At callout A, you saw the BindContainer function initialize the sContainerPath variable. Now that the script is connected to the proper OU, the final step is to create the user account by using the AddUser procedure at callout D.

The AddUser procedure first declares and initializes several local variables. Then, the Create method generates (instantiates) a User object from the AD schema. Next, the Put method assigns a slew of mandatory and optional properties to the User object. The SetInfo method saves the User object and its properties to the AD store. Then, AddUser sets the account-enabled flag and the user password on the User object. Next, AddUser again uses the SetInfo method to save the User object. You must save the User object before and after you enable the account and set the password.

After AddUser creates the user account, the procedure displays a message that states the account was created and shows the account information. Although the script could query the AD store for this information, it takes the faster approach of using values from the form's text fields.

Customizing and Running the Script
To make the create-user script work in your environment, you must make a few minor adjustments to it. Unless your OU names are OrgUnit01 and OrgUnit02, you need to modify the Form procedure (at callout C) so that it uses your OU names.

In the code at callout F, change the container names, telephone prefixes, domain suffixes, and locations as necessary. Note that the sDomainSuffix variable is the unique domain suffix that you define in the Microsoft Management Console (MMC) Active Directory Domains and Trusts snap-in. To find any unique domain suffixes, open the snap-in. In the console tree, right-click Active Directory Domains and Trusts, then select Properties. You'll see all unique domain suffixes listed in the Alternative UPN suffixes box. If nothing is listed there, use the domain suffix as defined for your domain.

The other changes that you need to make to the script are associated with the properties that the AddUser procedure (at callout D) assigns to the User object. For example, the HomeDirectory property is \\filesrv01\users\ in the sample script. You should change this value to the root of your home directory. This value can be a domain DFS link instead of a Universal Naming Convention (UNC) path that contains a server name. To learn about additional properties that you can set and how to set them, see Bob Wells' Windows 2000 Magazine article "Easy Active Directory Scripting for Systems Administrators, Part 2,", InstantDoc ID 15734.

After you've made the necessary modifications, you can run the script. Start Microsoft Internet Explorer (IE), and in the Address text box, type http:// computer_name/virtual_directory_name/main/usercreate.asp, where computer_name is the name of the computer running Microsoft IIS and virtual_directory_name is the name of the virtual directory that contains the user-creation script. I run IIS on server2 with the virtual directory ua, so the URL in my case is http://server2/ua/main/usercreate.asp.

Where to Go from Here
You can improve the script in many ways. Currently, the only error-checking that the script performs is to determine whether a user already has an account in the AD store. You could add a routine that displays a friendly error message if no Win2K domain is available or a routine that checks whether IE is present before making the script available to the operator. You could also add a string-manipulation routine to capitalize the first letter in the user account name and the job title. And now that the .NET Framework is in beta, you could rewrite the form in the context of an XML framework to improve document management and use Microsoft Visual Basic (VB) 7.0 or C# instead of VBScript to enhance the script's programming capabilities.

Now that you understand how to create a Web-based user-creation routine, you're on your way to creating additional Web-based Win2K administrative tools. For example, you could combine user creation with creating home directories and setting NTFS permissions from a Web page, as I'll show you in an upcoming issue.