In "Script User Account and Mailbox Creation" (September 2002, http://www.exchangeadmin.com, InstantDoc ID 25843), I demonstrate how to use scripts to create Active Directory (AD) user accounts and Exchange 2000 Server mailboxes. Several readers asked how to delete Exchange 2000 mailboxes and how to delete AD user accounts and mailboxes simultaneously; this article addresses both tasks.
I won't repeat the relevant background information that I cover in "Script User Account and Mailbox Creation," but I will follow the structure of that article so that you can more clearly see the consistency inherent in email-enabled user-account management tasks.
This article presents a script that performs email-enabled user-account deletion tasks. Like the script for creating user accounts and mailboxes, this script relies on Active Directory Service Interfaces (ADSI), Collaborative Data Objects for Exchange Management (CDOEXM), VBScript, and Windows Script Host (WSH) 5.6.
Before you use the script to automate user-account and mailbox deletion, your network must run Exchange 2000 and AD. Exchange 2000 updates the AD schema to create email-enabled AD user-account objects. The script also assumes that you're logged on to the domain that contains the user account you want to delete and that you have permission to delete user accounts and mailboxes.
The client computer on which you intend to run this script must have WSH 5.6 and CDOEXM installed. You need WSH 5.6 because the script uses an improved method of managing command-line arguments and an XML file format, both of which I describe in "Script User Account and Mailbox Creation."
The IMailboxStore COM interface in the cdoexm.dll file is installed and registered when you install the Exchange Management Components on a client computer. This interface contains mailbox properties and methods that the script relies on to complete the mailbox-deletion task. These components are available from any computer that's running Exchange 2000, so another option is to start a terminal services session with an Exchange 2000 server and run the script from there.
Running the Script
The script that deletes a mailbox and, optionally, a user account is called Remove User and Mailbox (RUM.wsf); you can download the file from the Code Library at http://www.exchangeadmin.com, InstantDoc ID 38529. The .wsf extension designates the file type as a Windows Script File. The content of a .wsf file is structured as XML. (I explain why I use the XML format of a .wsf file in "Script User Account and Mailbox Creation.")
You can run RUM.wsf from the WSH UI, wscript.exe, or the command-line interface, cscript.exe. For command-line help with RUM.wsf, type
and press Enter.
When you run the .wsf file from wscript.exe, RUM.wsf displays help and status information in a message box, as Figure 1 shows. When you run the .wsf file from cscript.exe, RUM.wsf displays the same information in a command window, as Figure 2 shows. See "Script User Account and Mailbox Creation" if you need to know why two script hosts exist, which is the default script host, and how to change the default script host.
As Figure 1 and Figure 2 show, the script requires three parameters to delete a mailbox or to delete both a user account and a mailbox. The /U parameter specifies the user account to delete. The /C parameter specifies the container location of the user account. The /RU parameter specifies whether to delete only the mailbox or both the mailbox and the user account.
The /C parameter is the distinguished name (DN) of the container hosting the user account. You can specify the following values for this parameter:
- A default AD container (e.g., cn=Users, cn=Builtin)
- An organizational unit (OU—e.g., ou=Management) or child OU (e.g., ou=Division1,ou=Management)
The /RU parameter is a Boolean value and must be either /RU- to delete only the mailbox of the specified user account or /RU+ to delete both the mailbox and the user account. For example, to delete only the mailbox for a user account named EthanW in an OU named TechWriters, type
rum.wsf /RU- /C:ou=TechWriters /U:EthanW
and press Enter. To delete both the mailbox and the user account, change /RU- to /RU+.
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. The parameters and their values aren't case sensitive, and parameter order isn't important. If any of the parameter values contain spaces, you must enclose the value in quotation marks. For example, /C:"ou=Accounting Dept" targets the user account deletion task at an OU named Accounting Dept.
The Step-by-Step Details
Using a .wsf file format provides additional features that aren't available in .vbs or .js files, such as the XML declarations that enclose the code. See "Script User Account and Mailbox Creation" for a detailed explanation of why the XML declarations are important to running this script.
Line 23 of RUM.wsf starts the file's VBScript code section. This part of the script begins with two statements: Option Explicit and On Error Resume Next. The Option Explicit statement requires that all variables 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 added code to the script to deal with the most likely error conditions, such as specifying a user-account name that doesn't exist 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 three constants to represent Lightweight Directory Access Protocol (LDAP) error codes that an AD domain controller (DC) can return. The first constant (LDAP_NO_SUCH_OBJECT) indicates that the specified user account doesn't exist. The user account might exist, but not in the location you provided in the /C container parameter. The second constant (LDAP_INVALID_DN_SYNTAX) indicates that the syntax in the binding string is incorrect. The third constant (LDAP_SERVER_DOWN) indicates that the script was unable to establish a connection with a DC to complete the deletion task.
RUM.wsf uses the WshArguments object's Named property to retrieve the command-line parameters. The Named property returns the WshNamed collection object, which contains the parameters that have names—in this case, /C, /U, and /RU. XML declarations in the beginning of RUM.wsf define the /C and /U parameters as string values and the /RU parameter as a Boolean value.
The code at callout A in Listing 1 assigns each named item in the WshNamed collection to a variable. Callout B in Listing 1 shows how the script determines whether you included all three required command-line parameters when you launched the script. If the Count method of the WshNamed object returns a value less than 3, the ShowUsage method of the WshArguments object displays the information contained in the
Up to this point, the script has done little to complete the task of deleting a mailbox or both a mailbox and a user account. 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 user account targeted for the deletion task. The GetObject function, ADSI's IADsContainer interface, and two functions I created—sUserPath() 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 perform the deletion task. The sUserPath() function calls the sADsPath() function to build the LDAP path to the targeted 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() function, contains the default domain.
Serverless binding means that a DC name isn't hard-coded in the connection string, so you aren't limited to a specific DC when performing the deletion task. However, using serverless binding presents an important caveat to using this script. RUM.wsf assumes that you're logged on to the domain in which you want to perform the deletion task. If you aren't logged on to that domain, you must add code to specify the domain. You might also need to specify credentials if the user account isn't trusted to perform the task in that domain.
Although you don't include the server name in a serverless binding operation, binding to AD still relies on an available DC. If a DC isn't available, the script returns an LDAP_SERVER_DOWN error, displays a message explaining that a DC couldn't be located to service the request, then terminates. If a DC is available, the script determines whether the DN specified in the binding string was valid and, if so, whether the DC servicing the request found the user account specified in the binding string. If the DN is invalid, the script returns an LDAP_INVALID_DN_SYNTAX error; if the user account isn't found, the script returns an LDAP_NO_SUCH_OBJECT error. In both cases, the script terminates after displaying the error message.
If the DC finds the user account, the script determines what you want to do—delete only the mailbox or delete both the user account and mailbox. The script also determines whether the specified user account has an associated mailbox. This step is necessary because you can create an AD user account that doesn't contain a mailbox. The code in Listing 2, page 14, shows the four tests that the script performs to determine the following:
- The user account has an associated mailbox, and the operator specifies /RU+.
- The user account doesn't have an associated mailbox, and the operator specifies /RU+.
- The user account has an associated mailbox, and the operator specifies /RU-.
- The user account doesn't have an associated mailbox, and the operator specifies /RU-.
These four tests and their accompanying actions are the core of this script. The script first determines whether the user account has been assigned a mailbox. The script needs this information so that it doesn't attempt to delete a nonexistent mailbox. To determine mailbox assignment, the script relies on the HomeMDB property of the IMailboxStore interface. HomeMDB is empty for a user account without an assigned mailbox. For a user account with an assigned mailbox, HomeMDB returns the DN of the mailbox store object for the assigned mailbox. For example, the default mailbox store for a user account in the Contoso.com domain, on a server named W2KS1, in the Network Design Group Exchange organization is CN=Mailbox Store (W2KS1),CN=First Storage Group,CN=InformationStore,CN=W2KS1,CN=Servers,CN=First Administrative Group,CN=Administrative Groups,CN=Network Design Group,CN=Microsoft Exchange,CN=Services,CN=Configuration,DC=contoso,DC=com. The strHomeMDB variable is set equal to the value of HomeMDB. If HomeMDB is empty, strHomeMDB is also empty.
After determining whether a user account has an assigned mailbox, the script determines whether you want to delete both the user account and mailbox or only the assigned mailbox. The value of the blnRUM variable is true (/RU+) if you want to delete both the user account and the mailbox and is false (/RU-) if you want to delete only the assigned mailbox.
If blnRUM is true, the script calls the DeleteUser subroutine. This routine uses the Parent property of ADSI's IADs interface to determine the ADsPath to the parent OU of the user account. For example, the EthanW user account in the TechWriters OU of the Contoso.com domain has the ADsPath LDAP://ou=TechWriters, DC=contoso,DC=com. The IADs interface is part of the objUser user object in the script. You can use string manipulation to build the ADsPath to the parent OU, but reading the user object's Parent property is much easier.
After the ADsPath to the OU is known, DeleteUser calls the GetObject function to bind to the OU and set the objOU variable to the value of the returned object. Finally, the Delete method of the IADsContainer interface deletes the user account.
After the user account is deleted, the script displays a message stating that the user account and mailbox were deleted or that only the user account was deleted. The former message appears if strHomeMDB had a value, and the latter message appears if strHomeMDB was empty. You don't need to specifically delete the assigned mailbox because deleting the user account orphans and eventually deletes the mailbox.
If blnRUM is false (i.e., you want to delete the assigned mailbox only), the script checks for a value in the strHomeMDB variable. If strHomeMDB contains a value, the DeleteMailbox method of the IMailboxStore interface takes care of deleting the mailbox. This method reconfigures several user-account attributes so that the user account is no longer connected to a mailbox. After the attributes are reconfigured, the script calls the SetInfo method of the IADs interface to save the changes to the user-account object. After saving the change, a message informs the operator that the assigned mailbox was deleted.
If blnRUM is false and strHomeMDB variable is empty, the user account doesn't have a mailbox but the operator requested that an assigned mailbox be deleted. When this condition occurs, the script simply informs the operator that nothing was done because the user account didn't have an assigned mailbox.
Why the Mailbox Is Still There
If you open the Microsoft Management Console (MMC) Exchange System Manager (ESM) snap-in and look for the mailbox after running the script, you're likely to still see the mailbox. In reality, the mailbox isn't immediately deleted. Instead, the mailbox is orphaned so that you can reconnect it to a user account if necessary.
To see which mailboxes are orphaned or reconnected, run the Cleanup Agent from ESM. In the ESM console tree, expand the Mailbox Store container of the server that contains the mailbox, then right-click the Mailboxes node to run the Cleanup Agent.
By default, orphaned mailboxes are retained for 30 days. However, you can change this behavior and other deletion settings from the Properties page of the Mailbox Store container. In the ESM console tree, right-click the Mailbox Store container and click Properties. The deletion settings appear in the bottom section of the Limits tab.
RUM.wsf Is Running
RUM.wsf contains some error-handling code for the most common errors and is enveloped by XML elements to take advantage of the self-documenting features of named arguments. The script's core mission is to make it easy for you to delete a user account and mailbox or only a mailbox. Everything else in the script makes it practical for daily use. This script can provide you with a useful tool for email-enabled account management.