If you administer a network running Windows 2000 Server and Exchange 2000 Server, you or someone you know must create user accounts and mailboxes. Using the Microsoft Management Console (MMC) Active Directory Users and Computers snap-in to perform these tasks manually is tedious, and, like most manual-entry tasks, fraught with opportunities to make data-entry errors. If you currently create users and mailboxes manually, don't despair. In this article, I show you how to use Active Directory Service Interfaces (ADSI), Collaboration Data Objects for Exchange Management (CDOEXM), VBScript, and Windows Script Host (WSH) 5.6 to write a script that automates this process. I review what's required to use the script, describe how to run it, examine what the XML does, and discuss the script details.
The System Requirements
Before you use the script to automatically create user accounts and mailboxes, your network must run Active Directory (AD) and Exchange 2000. Exchange 2000 relies on the AD schema to define mail-enabled AD objects and mailbox-enabled AD user objects. Before or during the Exchange 2000 installation, you must extend the AD schema to support Exchange-specific classes and to update existing classes with Exchange-specific attributes. (Exchange 2000 documentation outlines how to extend the AD schema.)
The client computer on which you intend to run this script must have WSH 5.6 installed. You need WSH 5.6 because the script uses a new and improved method of managing command-line arguments and an XML file format, which earlier versions of WSH don't support. Windows XP comes with WSH 5.6. You can download the WSH 5.6 upgrade for Win2K, Windows NT, Windows Me, and Windows 98 at http://msdn.microsoft.com/scripting. The cdoexm.dll file, which contains the CDOEXM objects, isn't part of the Windows OS. This DLL is installed automatically on computers that run Exchange 2000, and it's also installed automatically with the Exchange Management Components on a client computer. (If you install the Exchange Management UI, you've also installed the Management Components.)
The Script Basics
The script that creates a user account and mailbox is named Create User and Mailbox (CrUM.wsf), which you can download from the Code Library at http://www.exchangeadmin.com, InstantDoc ID 25843. The .wsf extension designates the file type as a Windows Script (WS) file. The XML structure of the .wsf file content provides additional capabilities not available in pure VBScript or JScript scripts. WS files not only provide XML structure but also let you run multiple scripting languages within a single file and reference type libraries. As I detail the parts of CrUM.wsf, I describe the .wsf's file's XML elements. For details about all the XML elements in a .wsf file, see the scripting section of the Microsoft Developer Network (MSDN) Web site at http://msdn.microsoft.com/scripting.
You can run CrUM.wsf from either the WSH graphical interface, wscript.exe, or from the command-line interface, cscript.exe. For command-line help with CrUM.wsf, type
When you run the .wsf file from WScript, CrUM.wsf displays Help and status information in a message box, as Figure 1 shows. When you run the .wsf file from CScript, CrUM.wsf displays the same information in a command window, as Figure 2 shows.
Whether the Help information appears in a message box or in a command window depends on which script host you've configured as the default. WScript is the default script host unless you specifically configure WSH to use CScript. To configure CScript as your default script host, at a command line, type
and press Enter.
As Figure 1 and Figure 2 show, the .wsf file requires three parameters to create a user account and a mailbox. The /S parameter specifies the Exchange 2000 system on which you want to create the mailbox for the user account. The /U parameter specifies the name of the user account. The /OU parameter specifies the organizational unit (OU) in which you want to create the user account. For example, to create a mailbox on an email server named Exch01 for a user account named EthanW in an OU named TechWriters, type
crum.wsf /s:exch01 /u:EthanW /ou:TechWriters
You must specify the three mandatory parameters. If you omit a mandatory parameter, the .wsf file displays the information that appears in Figure 1 and Figure 2. You can assign many more attributes to a user account object when you create it, but to do so, you must specify additional command-line parameters. For simplicity, I didn't include optional command-line parameters in CrUM.wsf.
The order of the mandatory parameters doesn't matter. For example, if you're creating several users in the TechWriters OU who'll have mailboxes on the Exch01 server, you can reorganize the previous code example to
crum.wsf /s:exch01 /ou:TechWriters /u:EthanW
To create a mailbox for another user on the same server and in the same OU, you simply change the user account name. If any of the parameters contains spaces, you must enclose the value in quotation marks. For example, /U: "Ethan Wilansky" creates a user account named Ethan Wilansky.
The XML Advantage
One reason that the .wsf file uses XML format is to take advantage of the <runtime> element. In this element, you can document usage information that the .wsf file displays when it runs. Callout A in Listing 1 shows CrUM.wsf's usage information. Later, the .wsf file uses the ShowUsage method to display the contents of the <runtime> element. Figure 1 and Figure 2 show the displayed information. ShowUsage is a special method that WSH 5.6 includes. If you use the <runtime> element and the ShowUsage method, you don't have to write special code to provide Help information.
The <job> element, which is the parent of the <runtime> element, encapsulates one task in a .wsf file. You could define several jobs in this file, but for simplicity, I defined creating a user account and a mailbox as one task and, therefore, as one job. The opening <job> tag appears directly above callout A in Listing 1, and the closing </job> tag appears near the end of Listing 1.
The <package> element is the parent of the <job> element and contains one or more jobs in the .wsf file. Because this .wsf file involves only one job, the <package> element is optional. However, I recommend including it so that you'll be in the habit when you create a .wsf file with multiple jobs..
The <script> element is a child of the <runtime> element. The <script> element contains the VBScript code that completes the work of creating a user account and mailbox. Note that Listing 1 shows the XML structure of CrUM.wsf, but it doesn't show the script that creates AD user accounts and Exchange 2000 mailboxes.
After the .wsf file defines the opening script tag <script language = "VBScript">, the script's CDATA section tells the XML parser (the XML engine that processes XML tags and their content) that this section doesn't contain XML and therefore shouldn't be processed. The section opens with <!\[CDATA\[ and closes with \]\]>. Everything between the opening and closing of the CDATA section is the script. If the .wsf file didn't begin with the XML declaration <?xml version="1.0" encoding="utf-8" ?>, the file wouldn't need to include this section. However, including this line instructs the XML parser to verify that the XML document is well formed. (For information about the XML declaration, see the XML Developer's Guide at http://msdn.microsoft.com.)
The Step-by-Step Details
The VBScript code begins with two statements: Option Explicit and On Error Resume Next. The Option Explicit statement requires that all variables used in the script be declared (using the DIM statement) before they're used. The On Error Resume Next statement lets the script continue if it encounters an error.
I've added code to the script to deal with the most likely error conditions, such as specifying a user account name that already exists in AD. Because VBScript returns error numbers, not error names, you can define constants in the script to make it easier to understand and debug error conditions.
I use constants to represent various LDAP error codes that an AD domain controller (DC) can return. The first constant (LDAP_ALREADY_EXISTS) indicates that the user account the script specifies already exists. The second constant (LDAP_NO_SUCH_OBJECT) indicates that the OU the script specifies doesn't exist. The third constant (LDAP_SERVER_DOWN) indicates that the server running AD that the script specifies isn't available or doesn't exist.
As I explained earlier in this article, the <runtime> element documents command-line parameters. The <runtime> element and its contents, however, don't dictate the format and content of the command-line parameters. Instead, the WshArguments object determines them. The VBScript code uses the Named property of the WshArguments object to define the three command-line parameters: S, U, and OU.
The Named property of the WshArguments object returns the WshNamed child object. Web Listing 1 (http://www.exchangeadmin.com, InstantDoc ID 25843) shows how the WshArguments and WshNamed objects are created in memory by setting an object name to the named arguments collection and how named items are specified by using the Item property of the named collection. Listing 2 shows how the script determines whether you've specified all required command-line parameters. If the Count method of the WshNamed object is less than 3, the total number of command-line parameters, the ShowUsage method of the WshArguments object displays the information the <runtime> element contains, then the script terminates.
Up to this point, the script has done little to complete the task of creating a user account and mailbox. If you've specified all the necessary named command-line parameters (in any order), the script goes to work.
The script must first bind to the OU in which you want the user account to reside. The GetObject function, the IADsContainer ADSI interface, and two functions I created—sContainerPath() and sADsPath()—complete this task. By using GetObject, the script runs in the security context of the current user. Therefore, you must have enough privileges in AD to create a mailbox-enabled user account in the specified OU. The sContainerPath() function calls the sADsPath() function to build the Lightweight Directory Access Protocol (LDAP) path to the OU in which the script will create the user account. The sADsPath() function uses an ADSI feature called serverless binding to return the name of the default domain of the currently logged-on user. The defaultNamingContext attribute, which is specified when the script calls the sADsPath attribute, contains the default domain.
The sADsPath() function doesn't specify a DC, which is good because you shouldn't tie the operator to a specific server for creating user accounts. However, using serverless binding brings up an important caveat regarding this script. The script assumes that the operator who creates a user account is logged on to the domain in which the user account will be created. If the operator won't be logged on to that domain, you must add code to specify the domain in which you want to create the user account. You might also have to specify credentials if the user account isn't trusted to perform user account and mailbox creation in that domain.
The script then determines whether the DC servicing the script has returned an error code matching the value of LDAP_NO_SUCH_OBJECT because the GetObject method can't find the OU. If the DC returns this error code, the script informs the operator that the OU doesn't exist and the script terminates. If not, the script uses IADsUser's Create method to create the user account in the OU. IADsUser is an ADSI interface for a limited but important set of user account management tasks, such as creating a user account. (For more information about the IADsUser interface, see Platform SDK: Directory Services IADsUser at http://msdn.microsoft.com/library/en-us/netdir/adsi/iadsuser.asp.)
Before it continues, the script determines whether the DC servicing the script has returned an error code matching the value of LDAP_ALREADY_EXISTS. If the DC returns this error code, the script informs the operator that the user account already exists and that the operator should check the directory to determine whether a mailbox for this user account also exists. Then, the script terminates. The user account name specified must be unique in the domain because the account's sAMAccountName attribute uses the same name, and the latter must also be unique in the domain. For example, if the script attempts to create the user account EthanW in the TechWriters OU but a user account with a sAMAccountName of EthanW exists in a different OU in the domain, the script terminates and informs you that it can't create the user account. To avoid this error, you can modify the script to accept an additional parameter for the sAMAccountName attribute and use this parameter when you create the user accounts.
If the user account doesn't already exist, the script sets additional attributes for the account. Specifically, the script assigns the user account a password of password, enables the account, and selects the User must change password at next logon check box.
After the script creates the user account, it calls CDOEXM to create the user account's mailbox. CDOEXM is an extension of ADSI that contains the CreateMailbox method that you run against the user object just created. Specifically, the CreateMailBox method of the IMailBoxStore interface configures AD to create the mailbox, as Listing 3 shows. The mailbox isn't actually created until someone sends mail to or attempts to connect to the mailbox. CreateMailBox expects one parameter, which is HomeMDBURL—the path to the location in which the mailbox will be created. Unfortunately, this path is usually long and difficult to interpret. Web Figure 1 shows an example path as it appears in the MMC ADSI Edit snap-in according to the code specified in Listing 3. (I removed the line continuation script syntax for brevity in Web Figure 1.) The callouts that appear in the ADSI Edit console tree in Web Figure 1 correspond to the callouts that appear in the script syntax, also in Web Figure 1. Note that callouts A and B in Listing 3 aren't part of the console tree path.
Callout A specifies the LDAP provider and the name of the directory server. The strDS() function determines the directory server name. The strDS() function relies on the sADSPath() function, which relies in turn on ADSI to find and connect to a DC within the AD site in which your computer is located. If ADSI can't find a DC within the AD site, ADSI uses the first DC it can find. Callout B specifies that the container is a mailbox store on the mail server. The strMailServer variable contains the server name you specified when you called CrUM.wsf. Two other variables in the HomeMDBURL path, strOrgName and strStorageGroup, appear in callout F and callout C, respectively. The script specifies the default storage group as First Storage Group and the organization name as Network Design Group. You must change these names to the names that appear in your Microsoft Exchange Server directory. You can use the ADSI Edit snap-in or Exchange System Manager (ESM) to find the organization name defined for your mail system. (The organization name appears at the top of the console tree in the ESM console, in the Microsoft Exchange program group.)
The final steps in the VBScript code perform a number of error-checking routines. The code checks for returned error codes: first, for the LDAP_SERVER_DOWN error code; second, for the LDAP_NO_SUCH_OBJECT error code; and third, for any other error code. If the code finds the LDAP_SERVER_DOWN error code, the script tells the operator that it can't find the directory server and terminates. This event is extremely unlikely because if a DC isn't available, you can't create a user account. The code finds this error only if the script successfully creates the user account, then a DC becomes unavailable. If the code finds the LDAP_NO_SUCH_OBJECT error code, the script tells the operator that a problem with the specified mailbox path exists. The script displays the current values of the strMailServer, strOrgName, and strStorageGroup variables, then deletes the user account and terminates. If the DC servicing the script returns any other error code, the script displays the error number and error description, deletes the user account, and terminates. If the DC returns no error codes, the script displays a message to inform the operator that it has successfully created the user account and mailbox.
As you can see, the CrUM.wsf file accomplishes several tasks. The only required portions of the CrUM.wsf file are those that create a user account and configure the user account for a mailbox. Adding helpful WSH 5.6 features, such as named arguments, and other features, such as error checking, introduces some complexity but should make CrUM.wsf more useful. Ultimately, CrUM.wsf should save you time and reduce errors as you create AD user accounts and mailboxes.