Recently, a colleague said to me, "I rely on the Microsoft Exchange 2000 Server Global Address List (GAL). All I need is a simple GUI that lets my account operators update user accounts and common attributes that appear in the GAL. The Active Directory Users and Computers tool is great, but it's more than my account administrators need." While listening to my friend's discourse, I considered the possibility of using Dynamic HTML (DHTML), Active Directory Service Interfaces (ADSI), and VBScript to create such a GUI tool. By hosting the application as an HTML Application (HTA), I could provide a lightweight, powerful, and simple interface for the script. After a few days of coding, I completed the User Update Utility (3U).
3U consists of the UserUpdate.hta file, two .htm files (Results.htm and PropPage.htm), and the iso3166.xml file. You can download 3U from http://www.exchangeadmin.com, InstantDoc ID 39802.
UserUpdate.hta, the application's main entry point, displays the Search and Modify Utility window, which Figure 1, page 2, shows. This interface looks similar to the address book tool in Exchange's Microsoft Outlook Web Access (OWA) utility. I chose this interface design because it's an intuitive display for performing search requests. The interface will also be instantly familiar to anyone who has used OWA's address book feature.
The Results.htm file contains the results table that appears at the bottom of the Search and Modify Utility window. When you enter search criteria in at least one of the interface's fields and click Find, 3U updates the results table with matching user account records.
PropPage.htm, which Figure 2, page 2, shows, is the Properties page that appears when you select a user account from the results table and click Properties. From the User Account Properties Page, an operator with sufficient privileges (e.g., an account operator or administrator) can update the properties of the selected user account.
The iso3166.xml file contains three country names and their country codes. The country names contained in this file appear in the Country drop-down list on the Properties page. Obviously, this list doesn't contain all country names and codes. Because of copyright restrictions, I can't include the full list of country names and codes with this article download. You can legally download a full list from the International Organization for Standardization (ISO) Web site at http://www.iso.ch/iso/en/prods-services/iso3166ma/02iso-3166-code-lists/index.html. Make sure to download the XML version of the English country names and code elements. Then, rename the downloaded file iso3166.xml and replace the version of the file you downloaded from the Exchange & Outlook Administrator Web site.
Preparing for 3U
To use 3U, you must be running Active Directory (AD). Your network doesn't necessarily need to be running Exchange 2000; however, 3U's primary purpose is to provide an easy way for you to update user accounts in an Exchange-enabled domain, and many of the changes made through 3U will be reflected in the Exchange GAL. Even in non-Exchange domains, the application provides a simple, intuitive interface for updating user accounts in AD.
After you download 3U, copy the files to a local computer that's a member of an AD domain and that has Microsoft Internet Explorer (IE) 5.0 or later installed. The 3U tool doesn't come with a fancy installation program because it uses COM objects that are present on AD-enabled clients running IE 5.0 or later. After you copy the 3U files locally, double-click UserUpdate.hta to run the utility.
3U will run from a network share, but IE will display two ActiveX Data Objects (ADO)/Remote Data Service (RDS) warning messages, depending on your IE security settings. The warning messages state that the page is accessing a data source on another domain. IE generates these messages when the ADSI OLE DB provider attempts a search operation from a mapped drive or Universal Naming Convention (UNC) path. If you must run the application from a share and you want to avoid the ADO/RDS warning message, follow these steps on each computer that will run 3U:
- From the IE menu, click Tools, Internet Options.
- On the Security tab, click Local intranet, then click Custom Level.
- In the Miscellaneous section, click Enable for the Access data sources across domain option.
If you're using Group Policy, you can also use the User Configuration\Windows Settings\Internet ExplorerMaintenance\Security node to adjust this IE security setting, as follows:
- Open a Group Policy Object (GPO) that targets the users who will run 3U from a network share.
- Click the User Configuration\Windows Settings\Internet Explorer Maintenance\Security node.
- In the details pane, double-click Security Zones and Content Ratings (Preference Mode).
- Select the Import the current security zones and privacy settings radio button, then click OK.
After you apply the GPO to the targeted user accounts, those users will be able to run 3U over a network, and the system won't display ADO/RDS warning messages.
When 3U begins to load, it verifies that you're logged on to an AD domain. After you enter search criteria and click Find, the utility uses the default domain to perform a search for user accounts that match the criteria you entered in the Search and Modify Utility window. You can use wildcards when you specify user account criteria. For example, by typing
in the Title field, you can search for all user accounts that contain the word Manager in their title attribute. After you specify values for one or more fields, click Find to perform the search.
After one or more user accounts appear in the results table, you can double-click a user account to access the User Account Properties Page. You can make any changes that you're authorized to make to the values that appear on this page, then click OK to commit the changes to AD.
3U does a good job of catching changes that AD considers invalid. For example, if you set the User name field to a value that's already in use in the domain, the utility will inform you that the change is forbidden and will give you an opportunity to change the value.
Under the Covers
The 3U interface is simple and intuitive, but the code behind it is somewhat more complicated. UserUpdate.hta is constructed like an HTML file, but an additional line designates the file as an HTA. Listing 1 shows the element that designates the file as an HTA. You must give HTAs an .hta extension so that they load properly.
HTAs are applications that rely on IE APIs but don't come with the overhead of the IE interface elements. HTAs are powerful because they have access to all the COM objects available to IE, such as the rich variety of DHTML objects, as well as the COM objects in ADSI. IE's support of scripting languages (e.g., VBScript) makes these COM objects available.
The main body of UserUpdate.hta loads the interface elements. Each element below the body tag contains a unique id attribute, which makes the element available as a DHTML object in the script. Listing 2 shows a subset of the HTA's elements. Callout A shows the structure of the page's Display Name field. Each field contains a div element for positioning, a label element for associating a label with a field, and an input element, which specifies the field in the interface. Callout B shows how the HTA positions the Find and Reset buttons and assigns subroutines to the buttons' onClick events. The onClick event—one of many events you can capture while a program is running—monitors mouse clicks. In this case, 3U raises the onClick event when the operator clicks buttons in the HTA. Callout C shows an iframe element. The primary purpose of the iframe element is to create a frame and call the table that the Results.htm file specifies. When the result table exceeds the size of the frame, the iframe element automatically adds a scroll bar to the frame to simplify scrolling through the table.
The Script Details
The tags in Listing 2 are static and don't change as the program runs. However, each element's id attribute provides a convenient way to reference the element in script code and, through the script code, bring the element to life—a job for DHTML.
UserUpdate.hta contains a script preamble, which verifies that you're logged on to an AD domain, then connects (binds) to the ADSI rootDSE object. The rootDSE object contains several properties that describe the domain; the defaultNamingContext property is the property that contains the distinguished name (DN) of the current domain. The script attempts to bind to the rootDSE object and read the object's defaultNamingContext property. If the rootDSE object returns any error number other than 0, the script first displays a message stating that a default domain couldn't be found, and UserUpdate.hta closes. Otherwise, UserUpdate.hta initializes strDomain with the value of the default domain. The script initializes this variable globally because several subroutines use it.
After the preamble, UserUpdate.hta automatically loads the Window_Onload subroutine. This subroutine uses DHTML to set the title for 3U's opening window, resize the window, and call the InitialUIState subroutine. At this point, the opening window appears, but the HTML elements on the page haven't been positioned and don't contain any data—not even field labels. Calling the InitialUIState subroutine takes care of this step.
The InitialUIState subroutine calls four subroutines—SetColumns, BuildElement, SetButtons, and SetIFrame—to prepare the interface for input. Listing 3 shows a subset of InitialUIState's subroutine calls. The SetColumns subroutine uses the Style.CssText property to position the two div columns (Col1 and Col2) on the page. Col1 and Col2 contain all the page's fields except the Display Name field. Specifically, Col1 contains the fields on the left side of the page, and Col2 contains the fields on the right side.
InitialUIState then calls the BuildElement subroutine nine times, once for each set of div, label, and input elements necessary for each field on the page. BuildElement uses DHTML to configure various attributes of each field display.
Callout A in Listing 3 shows the parameters that the BuildElement subroutine receives to configure the Display Name field. This subroutine takes eight parameters. The first parameter—DisplayName—is the prefix that identifies both the div tag object (DisplayNameDiv) and the label tag object (DisplayNameLbl). BuildElement appends div and lbl to the prefix to reference the appropriate tag. The second parameter, Display Name, is the name of the label that appears on the page. Notice that the letter D in the word Display is surrounded by the underline elements and . As a result, the D in Display Name appears underlined, as you see in Figure 1. The third parameter, DisplayNameInput, specifies the name of the Display Name field or input element. The fourth and fifth parameters (set to 70 and 70 for the Display Name field) specify the field's size and length, respectively. The size value specifies the number of visible characters in the field, and the length value specifies the maximum number of characters that can be entered in the field. The sixth parameter (set to 0 for the Display Name field) specifies the field's tab order. Pressing the Tab key moves the focus from one field to the next field in tab order. The seventh parameter (set to D in this example) configures the field's access key (aka shortcut key). Underlining the D in the word Display (which appears in the second parameter) doesn't automatically create a shortcut key to move the cursor into the field. The BuildElement subroutine sets the Display Name label's AccessKey property to D, so pressing the Alt+D combination sets the focus on the DisplayNameInput Input tag object. The eighth parameter in each BuildElement call is the AD attribute's lDAPDisplayName, which BuildElement provides for the ADSISearch subroutine. The BuildElement subroutine also positions each field-level div tag on the page and sets other element attributes. Web Listing 1 (http://www.exchange admin.com, InstantDoc ID 39802) shows the BuildElement subroutine.
The SetButtons and SetIFrame subroutines set a variety of basic properties for the button elements and iframe element that appear on the page. For example, these subroutines position elements, name buttons, and set scrolling to automatic (auto setting) on the iframe element.
The subroutines I just described now build the interface elements that appear in UserUpdate.hta, except the initial table that contains a heading row in the iframe. The SetIFrame subroutine doesn't actually create the initial table for the iframe. At callout C in Listing 2, you'll see that the iframe contains a src attribute that's set to "results.htm". This attribute instructs the iframe to load the contents of Results.htm into the iframe.
As callout C in Web Listing 2 shows, Results.htm contains a table element and thead element inside the body element. The iframe element's src attribute specifies the content to load into the frame. In this case, a table with a heading row is loaded into the iframe element. Using the iframe in this way lets you scroll the table in UserUpdate.hta without scrolling the application's main window. In Results.htm, I define actions for three events: onLoad, onMouseOver, and onClick. The onLoad event, specified for the body element, calls the BuildInitialTable subroutine that appears at callout B. BuildInitialTable creates the rows in the table head and sets initial attributes on the table. The onMouseOver and onClick events are specified for the table element. When you move the mouse over the table, the onMouseOver event fires and the cursor becomes a pointing hand (i.e., the Link Select pointer) so that you can determine when your mouse is hovering over the table's rows and cells. When you click a row, the onClick event calls the HighlightRow subroutine that appears at callout A. HighlightRow highlights a row in the table when you click anywhere in the row.
Stay Tuned for More Details
You're probably saying, "Wow, all that work just to get the interface working?" UI work is tricky and labor-intensive, but if it's done right, it's worth the effort. After you accomplish the preceding work, the benefits of 3U will be at your fingertips. In Part 2 of this series, I'll explain more coding details, including the subroutines assigned to each button's onClick event. But rest assured: You now know more than enough about 3U to begin using it.