The best way to learn about using ADSI to program with the Exchange Server directory is to examine a sample application. I developed one that demonstrates how to create mailboxes, custom recipients, distribution lists, and recipient containers, and how to query for recipient directory information such as a user's name, address, and phone number. There is one caveat with this application—since it uses Dynamic HTML (DHTML) to mimic the Exchange Administrator program, the portion of the application that queries the attributes of a specific user will not work with Netscape browsers and will work only in Microsoft Internet Explorer 4.0 or later.
Before you can install the application, you must have a Windows NT 4.0 Server and a client with certain software installed. Table 15-1 describes the required software for setting up the application.
Table 15-1. Installation requirements for the ADSI application.
Minimum Software Requirements | Installation Notes |
---|---|
Exchange Server 5.5 Service Pack 1 with Outlook Web Access | Exchange Server SP3 is recommended. |
Internet Information Server (IIS) 3.0 or later with Active Server Pages | Internet Information Services (IIS) 4.0 is recommended. |
CDO library (cdo.dll) CDO Rendering library (cdohtml.dll) | Exchange Server 5.5 Service Pack 1 installs CDO library 1.21 and CDO Rendering library 1.21. Outlook installs CDO library 1.21. |
ADSI 2.0 | ADSI 2.5 is available as a free download from http://www.microsoft.com/adsi. Windows 2000 installs ADSI 2.5 by default. |
ActiveX Data Objects | Internet Information Services 4.0 installs ADO 1.5. Visual Basic 6.0 installs ADO 2.0. For more information on ADO, consult http://www.microsoft.com/data/. |
For the client: | |
Internet Explorer 4.0, Outlook 98 | You can run the client software on the same machine or on a separate machine. |
To install the ADSI application, first copy the ADSI folder from the companion CD to the Web server where you want to run the application. Start the IIS administration program. Create a virtual directory that points to the location where you copied the ADSI files, and name the virtual directory adsi. Make sure you enable the Execute permissions option for the virtual directory. You will be able to use the following URL to access your ADSI application: http://yourservername/adsi.
Included with the ADSI files on the CD is a dynamic link library (DLL) named AcctCrt.dll. Use the Regsvr32 utility to register it:
regsvr32 acctcrt.dll |
The first page displayed in the ADSI application is the Logon page, as shown in Figure 15-3. Once a user enters logon information and verifies the dynamically generated Exchange Server information, the application presents a menu of available administrative options for the user, as shown in Figure 15-4.
Figure 15-3. The Logon page for the ADSI application. The Exchange Server name, organization, and site information are pulled dynamically using the CDO Rendering library.
Figure 15-4. The main menu of the ADSI application. Users can create or modify objects in the Exchange Server directory as long as they have the proper permissions on the object.
Now let's step through the actual code that makes up these different menu items and see how to use the ADSI object library with an Exchange Server directory.
The most common operation in the code for the menu items is the object binding code for ADSI. This binding code is dispersed throughout all the code modules in the application, rather than being centralized and performed only once, to make it easier for you to browse the code and understand exactly what is happening.
To bind successfully to an object in the Exchange Server directory using ADSI, you must use the OpenDSObject method after using the GetObject method to set an object variable to the ADSI library. The OpenDSObject method takes four parameters:
Depending on the provider used, these flags specifying the binding option might or might not be supported. On the LDAP provider, if you set both flags and pass in a user name and a password, ADSI will perform a simple bind over Secure Sockets Layer (SSL) sessions, which is a secure authentication over a secure channel. The sample application does not use either flag, so a 0 is passed in as the value for the final parameter to indicate that no encryption and no secure authentication should be used.
The following code example shows how to set an object variable to the LDAP provider and log on using the OpenDSObject method:
Set oIADs = GetObject("LDAP:") oIADs.OpenDSObject(AdsPath, UserName, Password, 0) |
Using ADSI, you can create mailboxes easily in the Exchange Server directory, but they are not fully functional. ADSI does not provide a way for you to specify a Windows NT account as the primary owner of the mailbox, nor does it allow you to change the permissions on the mailbox so that the primary owner has permission to open it. You must perform these operations by using a separate program or the Exchange Administrator program. To specify a Windows NT account and to change user permissions on the mailbox in the sample application, I used a DLL named AcctCrt.dll. This COM component is discussed in detail in Chapter 16.
Figure 15-5 shows the interface in the ADSI application that prompts the user for information about the mailbox she wants to create. This information is needed by the ADSI code to set specific properties in the Exchange Server directory. The application also asks for Windows NT account information so that a corresponding Windows NT user account can be created and identified as the primary account for the Exchange Server mailbox. Be sure to enter information in all text boxes; otherwise, an error will be displayed.
Figure 15-5. The page where the user can enter information about the mailbox she wants to create using ADSI.
After the mailbox information is submitted, the ASP page called by the application sets a reference to the ADSI library. The ASP page also retrieves the Recipients container, where it will create the mailbox and the private mailbox store so that it can parse out the correct domain name for the user's SMTP address. The application creates proxy addresses for the user just in case the Exchange server has a Lotus cc:Mail or Microsoft Mail connector installed. The application also sets other properties as shown in this code:
<% bstrAT = Request.ServerVariables("AUTH_TYPE") If InStr(1, "_BasicNTLM", bstrAT, vbTextCompare) < 2 Then Response.Buffer = TRUE Response.Status = ("401 Unauthorized") Response.AddHeader "WWW.Authenticate", "Basic" Response.End end if dim arrOtherAddresses(1) 'Make sure to set error checking on error resume next err.clear 'Retrieve the items from the previous form fn = Session("FN") ln = Session("LN") dn = Session("DN") al = Session("AL") dir = Session("DIR") NTDomain = Session("NTDomain") NTDescrip = Session("NTDescrip") NTPassword = Session("NTPassword") 'Create the Windows NT account. 'Use the new AcctCrt component. set oNTContainer = CreateObject("MSExchange.AcctMgmt") oNTContainer.NtAccountCreate NTDomain, al, NTPassword, "", "" 'Get the server ID and descriptor call oNTContainer.GetSidFromName(NTDomain, al, SecurityID) call oNTContainer.GenerateSecDescriptor(NTDomain, al, _ SecurityDescriptor) 'Create the Exchange Server mailbox 'Get a reference to the ADSI library Set oIADS = GetObject("LDAP:") 'Query to the Exchange server bstr1 = "LDAP://" + Session("Server") + "/cn=" + Session("CN") + _ ",ou=" + Session("OU") + ",o=" + Session("O") bstr2 = Session("bstr2") bstr3 = Session("bstr3") 'Query to the private MDB file on the Exchange server bstrMDB = "LDAP://" + Session("Server") + _ "/cn=Microsoft Private MDB,cn=" + Session("Server") + _ ",cn=Servers ,cn=Configuration,ou=" + Session("OU") + ",o=" + _ Session("O") Set oContainer = oIADS.OpenDSObject(bstr1, bstr2, bstr3, 0) Set oMDB = oIADS.OpenDSObject(bstrMDB, bstr2, bstr3, 0) 'Create a new mailbox (class = organizationalPerson) with the 'correct directory name Set oIADS = oContainer.Create("organizationalPerson", "cn=" + _ CStr(dir)) oIADS.Put "NT-Security-Descriptor", (SecurityDescriptor) oIADS.Put "Assoc-NT-Account", (SecurityID) 'Retrieve the information from the private .mdb oMDB.GetInfo 'Retrieve the SMTP address for the private .mdb so that it can be parsed 'for the domain name strSMTPAddr = oMDB.Get("mail") 'Get up to the @ sign Pos = InStr(strSMTPAddr, "@") 'Parse out the domain name SMTPExt = Mid(strSMTPAddr, Pos, Len(strSMTPAddr)) strUserSMTPAddr = replace(al, " ", "") + SMTPExt 'Create the domain name for the Message Transfer Agent of the user strDNMTA = "cn=Microsoft MTA,cn=" + Session("Server") + _ ",cn=Servers,cn=Configuration,ou=" + Session("OU") + ",o=" + _ Session("O") 'Set the array of other addresses such as CCMAIL, MSMAIL, 'Profs, and so on arrOtherAddresses(0) = "MS$" + Session("O") + "/" + _ Session("OU") + "/" + al arrOtherAddresses(1) = "CCMAIL$" + al + " at " + Session("OU") 'arrOtherAddresses(2) = "Your other addresses" 'Use the Put command to have ADSI write all of these values to 'the new mailbox. 'mailPreferenceOption must always be 0. oIADS.Put "mailPreferenceOption", 0 oIADS.Put "givenName", CStr(fn) oIADS.Put "sn", CStr(ln) oIADS.Put "cn", CStr(dn) oIADS.Put "uid", CStr(al) oIADS.Put "Home-MTA", CStr(strDNMTA) oIADS.Put "Home-MDB", "cn=Microsoft Private MDB,cn=" + _ Session("Server") + ",cn=Servers,cn=Configuration,ou=" + oIADS.Put "mail", CStr(strUserSMTPAddr) oIADS.Put "MAPI-Recipient", True oIADS.Put "MDB-Use-Defaults", True oIADS.PutEx 2, "otherMailbox", (arrOtherAddresses) oIADS.Put "rfc822Mailbox", CStr(strUserSMTPAddr) oIADS.Put "textEncodedORaddress", CStr("c=US;a= ;p=" + _ Session("O") + ";o=" + Session("OU") + ";s=" + ln + ";g=" + _ fn + ";") oIADS.SetInfo if err.number = 0 then 'Success! %> <SCRIPT LANGUAGE="JavaScript"> alert("Successfully created mailbox and account! Please click OK to continue."); window.location="menu.asp"; </SCRIPT> <% else 'Failure! %> <SCRIPT LANGUAGE="JavaScript"> alert("Error! Error Number: <%=err.number %> Description: <%=err.Description%>"); window.location="menu.asp"; </SCRIPT> <% end if %> |
As you can see in the code, the main properties set on the mailbox include mailPrefenceOption, givenName (first name), sn (last name), cn (display name), uid (alias name), Home-MTA, Home-MDB, mail (SMTP address), MAPI-Recipient, MDB-Use-Defaults, otherMailbox (other addresses for the mailbox such as CCMAIL and MSMAIL), rfc822Mailbox (SMTP address), and textEncodedORaddress (X.400 address).
The example shows how to use the ADSI method PutEx to enter a multivalue property into the Exchange Server directory. To create the value for a multivalue property, you must use an array in VBScript. When passing this array as an argument to the PutEx call, you must place parentheses around the array to de-reference it. Recall that the Put and PutEx methods will modify the copy of the attributes in the property cache but not in the actual directory service. For this reason, the last statement in the code calls SetInfo to take all the changes in the cache and commit them to the directory service.
The ADSI application also shows you how to query for information from an existing Exchange Server mailbox. The user interface, shown in Figure 15-6, allows you to type in the first name of the user to find the mailbox.
Figure 15-6. The page allows you to type the first name of the user in the directory to locate the mailbox.
After the user types in a name, the application uses ADO to query the directory service using LDAP. You might be wondering why ADO is used rather than the OpenDSObject method we saw in the code for creating a mailbox. The reason is that to use the OpenDSObject method, the user must know the exact name of the desired object in the directory. ADO is more forgiving. When the user is looking for an existing mailbox, the user is not typing in the exact name of the user he is looking for but rather some portion of the first name. Also, since many times users do not know the alias name of users they are querying, forcing users to type in aliases does not make sense. The code to create the ADO object and perform the query is shown here:
bstrSearchCriteria = Request.Form("UserName") bstrServer = Session("Server") 'Create an ADO object Set ADOconn = CreateObject("ADODB.Connection") If Err.Number = 0 Then ADOconn.Provider = "ADSDSOObject" ADOconn.Open "ADs Provider" 'Create a query using ADO to find all users across all containers bstrADOQueryString = "<LDAP://" + bstrServer + _ ">;(&(objectClass=organizationalPerson)(cn=" + _ bstrSearchCriteria + "*));cn,adspath;subtree" Set objRS = ADOconn.Execute(bstrADOQueryString) If Err.Number = 0 Then If objRS.RecordCount > 0 Then %> <p>Please select one of the following names from the list of names.</p> <p><em><strong>Returned Names:</strong></em></p> <SELECT NAME='MailboxPath'> <% 'Builds the select control of the queried records While Not objRS.EOF bstrSelectStatement = bstrSelectStatement & _ "<OPTION VALUE='" & objRS.Fields(iCN).Value & _ "'>" & objRS.Fields(iADSPATH) objRS.MoveNext Wend Response.Write bstrSelectStatement & "</SELECT>" Else %> <B><I>No entries match your search criteria. Try again using a different value.</I></B> <% End If Else If Hex(Err.Number) = 80070044 Then Response.Write "<FONT FACE='Arial, Helvetica' " + _ "SIZE=2>Error " + Hex(Err.Number) + _ ": Too many entries match your search " + _ "criteria!</FONT>" Err.Clear Else %> <SCRIPT LANGUAGE="JavaScript"> alert("Error Number: <%=Hex(Err.Number)%> \n Error Description: <%=Err.Description%>") history.back() </SCRIPT> <% Err.Clear End If End If Else %> <SCRIPT LANGUAGE="JavaScript"> alert("Error Number: <%=Hex(Err.Number)%> \nError Description: <%=Err.Description%>") history.back() </SCRIPT> <% Err.Clear End If %> |
This code creates an ADO Connection object and sets the Provider property to ADSDSOObject, which specifies the LDAP provider for ADSI. You can specify any string for the connection string argument to the Open method of the ADO Connection object. In this case, the application specifies ADs Provider as the argument. The code then creates an LDAP query, which consists of four elements separated by semicolons. This is the format:
<LDAP://server/adsidn>;ldapfilter;attributescsv;scope. |
The server argument specifies the name or the IP address of the server where the directory is hosted. The adsidn argument specifies the distinguished name in the directory where we want to start our query. You should pass in a correctly formed path, which we saw how to create earlier in the chapter. The filter parameter specifies the LDAP filter string to use. In this case, the LDAP filter states that the object class must be organizationalPerson and the name of the object must match the letters typed in by the user. The next argument, attributescsv, is a list of attribute names, separated by commas, that you want returned for each row in the recordset. In our example application, we want the name of the person and the AdsPath to the object that corresponds to that person returned so that we can place this information in the HTML form, as shown in Figure 15-7. The final argument, scope, informs the directory service how deeply in the hierarchy to search for the information being queried. The scope argument can be one of three values: base, onelevel, or subtree. Since we want to query for all mailboxes that match our specified criteria across all recipient containers in the directory, subtree is specified for this argument. The subtree argument causes the directory service to search for the information in every subtree under the starting object. The base argument searches only the currently specified object, and onelevel searches one level below the current object in the hierarchy.
If the query successfully returns records that match the filter, the code uses the standard ADO methods to scroll through the recordset and place the records in the HTML form.
Figure 15-7. After performing the query, the HTML form is populated with the corresponding recordsets so that a user can pick the person she is interested in finding more information about.
After the user selects the name of the person she wants to find more information about in the HTML form, the application opens the directory object for this person and retrieves information such as the address, phone number, manager, and direct reports. This information is then represented using a DHTML tabbed dialog box in the browser, as shown in Figure 15-8.
Figure 15-8. The tabbed dialog box that shows the directory information for a specific user.
The next section of code retrieves the user information from the directory. I intentionally left out some of the DHTML code from the listing to highlight how ADSI is used in the code. Also, only a portion of the ADSI code is listed here because the structure of the code throughout this part of the application is very similar. Only the specific properties retrieved from the directory using ADSI are different. The full code is included on the companion CD.
On Error Resume Next if Request.QueryString("Path") = "" then bstrMailboxPath = Request.Form("MailboxPath") else bstrMailboxPath = Request.Querystring("Path") end if bstrServer = Session("Server") Set objIADs = GetObject(bstrMailboxPath) strCustomAttributes = "LDAP://" & Session("Server") & _ "/cn=Attribnum" & ",cn=" & "Microsoft DMD" & ",ou=" & _ Session("ou") & ",o=" & Session("o") Function GetAttribName(AttribName) strADsPath = Replace(strCustomAttributes,"Attribnum", _ AttribName,1,1) set objAttributeName = GetObject(strADsPath) strAttributeName = objAttributeName.Get("Admin-Display-Name") GetAttribName = strAttributeName & ":" end Function … <TR> <TD VALIGN=MIDDLE ALIGN=RIGHT> <FONT FACE="Arial, Helvetica" SIZE=2>First Name:</TD> <TD VALIGN=TOP><FONT FACE="Arial, Helvetica" SIZE=2> <B><%=Server.HTMLEncode(objIADs.Get("givenName"))%></B></FONT></TD> <TD VALIGN=MIDDLE ALIGN=RIGHT><FONT FACE="Arial, Helvetica" SIZE=2> Initials:</TD> <TD VALIGN=TOP><FONT FACE="Arial, Helvetica" SIZE=2> <B><%=Server.HTMLEncode(objIADs.Get("initials"))%></B></FONT></TD> <TD VALIGN=MIDDLE ALIGN=RIGHT><FONT FACE="Arial, Helvetica" SIZE=2> Last Name:</TD> <TD VALIGN=TOP><FONT FACE="Arial, Helvetica" SIZE=2> <B><%=Server.HTMLEncode(objIADs.Get("sn"))%></B></FONT></TD> </TR> <TR> <TD VALIGN=MIDDLE ALIGN=RIGHT><FONT FACE="Arial, Helvetica" SIZE=2> Display Name:</TD> <TD VALIGN=TOP><FONT FACE="Arial, Helvetica" SIZE=2> <B><%=Server.HTMLEncode(objIADs.Get("cn"))%></B></FONT></TD> <TD> </TD> <TD> </TD> <TD VALIGN=MIDDLE ALIGN=RIGHT><FONT FACE="Arial, Helvetica" SIZE=2> Alias:</TD> <TD VALIGN=TOP><FONT FACE="Arial, Helvetica" SIZE=2> <B><%=Server.HTMLEncode(objIADs.Get("uid"))%></B></FONT></TD> </TR> <TR> <TD WIDTH="100%" COLSPAN="10"> <HR> </TD> </TR> <TR> <TD VALIGN=TOP ALIGN=RIGHT><FONT FACE="Arial, Helvetica" SIZE=2> Address:</FONT></TD> <TD VALIGN=TOP><FONT FACE="Arial, Helvetica" SIZE=2> <B><%=Server.HTMLEncode(objIADs.Get("postalAddress"))%></B> </FONT></TD> <TD> </TD> <TD> </TD> <TD ALIGN=RIGHT><FONT FACE="Arial, Helvetica" SIZE=2> Title:</FONT></TD> <TD ALIGN=LEFT><FONT FACE="Arial, Helvetica" SIZE=2> <B><%=Server.HTMLEncode(objIADs.Get("title"))%></B></FONT></TD> </TR> <TR> <TD> </TD> <TD> </TD> <TD> </TD> <TD> </TD> <TD ALIGN=RIGHT><FONT FACE="Arial, Helvetica" SIZE=2> Company:</FONT></TD> <TD><FONT FACE="Arial, Helvetica" SIZE=2> <B><%=Server.HTMLEncode(objIADs.Get("Company"))%></B></FONT></TD> </TR> <TR><TD ALIGN=RIGHT><FONT FACE="Arial, Helvetica" SIZE=2> City:</FONT></TD> <TD VALIGN=TOP><FONT FACE="Arial, Helvetica" SIZE=2> <B><%=Server.HTMLEncode(objIADs.Get("l"))%></B></FONT></TD> <TD> </TD> <TD> </TD> <TD ALIGN=RIGHT><FONT FACE="Arial, Helvetica" SIZE=2> Department:</FONT></TD> <TD><FONT FACE="Arial, Helvetica" SIZE=2> <B><%=Server.HTMLEncode(objIADs.Get("department"))%></B></FONT></TD> </TR> <TR> <TD ALIGN=RIGHT><FONT FACE="Arial, Helvetica" SIZE=2> State:</FONT></TD> <TD><FONT FACE="Arial, Helvetica" SIZE=2> <B><%=Server.HTMLEncode(objIADs.Get("st"))%></B></FONT></TD> <TD> </TD> <TD> </TD> <TD ALIGN=RIGHT><FONT FACE="Arial, Helvetica" SIZE=2> Office:</FONT></TD> <TD><FONT FACE="Arial, Helvetica" SIZE=2> <B><%=Server.HTMLEncode(objIADs.Get("physicalDeliveryOfficeName"))%> </B></FONT></TD> </TR> <TR> <TD ALIGN=RIGHT><FONT FACE="Arial, Helvetica" SIZE=2> Zip Code:</FONT></TD> <TD><FONT FACE="Arial, Helvetica" SIZE=2> <B><%=Server.HTMLEncode(objIADs.Get("postalCode"))%></B> </FONT></TD> <TD> </TD> <TD> </TD> <TD ALIGN=RIGHT><FONT FACE="Arial, Helvetica" SIZE=2> Assistant:</FONT></TD> <TD><FONT FACE="Arial, Helvetica" SIZE=2> <B><%=Server.HTMLEncode(objIADs.Get("secretary"))%></B></FONT></TD> </TR> <TR><TD ALIGN=RIGHT><FONT FACE="Arial, Helvetica" SIZE=2> Country:</FONT></TD> <TD><FONT FACE="Arial, Helvetica" SIZE=2> <B><%=Server.HTMLEncode(objIADs.Get("co"))%></B></FONT></TD> <TD> </TD> <TD> </TD> <TD ALIGN=RIGHT><FONT FACE="Arial, Helvetica" SIZE=2> Phone:</FONT></TD> <TD><FONT FACE="Arial, Helvetica" SIZE=2><B> <%=Server.HTMLEncode(objIADs.Get("telephoneNumber"))%> </B></FONT></TD> </TR> </TABLE> </DIV> <DIV CLASS=conts ID=t2Contents> <!-- Draw out the tab for Organization --> <Table width=600> <TR> <TD width=100% colspan="5"><img src="mailbox.jpg" align="middle"> <FONT FACE="Arial, Helvetica" SIZE=5> <B><%=Server.HTMLEncode(objIADs.Get("cn"))%></B></FONT></TD> </TR> <TR> </TR></TABLE> <Table border=0> <TR> <TD ALIGN=LEFT NoWrap><FONT FACE="Arial, Helvetica" SIZE=2> Manager Name:</FONT></TD> <TR> </TR> <% strManager = objIADs.Get("manager") strManagerPath = "LDAP://" & Session("Server") & "/" & strManager set oIADsManager = GetObject(strManagerPath) strManagercn = Server.HTMLEncode(oIADsManager.Get("cn")) %> <TR> <TD><FONT FACE="Arial, Helvetica" SIZE=2><B> <A Href='MBINFOTABS.ASP?Path=<%=strManagerPath%>'> <%=strManagercn%></a></B></FONT></TD> </TR> <TR><TD> </TD></TR> <TR><TD ALIGN=LEFT><FONT FACE="Arial, Helvetica" SIZE=2> Direct Reports:</FONT></TD></TR> <% err.clear strReports = objIADs.GetEx("Reports") for i = LBound(strReports) to UBound(strReports) 'Get each Directory Service object to return the friendly name strDirectPath = "LDAP://" & Session("Server") & _ "/" & strReports(i) set oIADsReports = GetObject(strDirectPath) strReportscn = _ Server.HTMLEncode(oIADsReports.Get("cn")) %> <TR><TD><FONT FACE="Arial, Helvetica" SIZE=2> <B><img src="mailboxs.jpg" ALIGN="Middle"> <A Href='MBINFOTABS.ASP?Path=<%=strDirectPath%>'> <%=strReportscn%></a> </B></TD></TR> <% next %> </TR></TABLE> </DIV> <DIV CLASS=conts ID=t4Contents> <!-- Draw out the tab for Custom Attributes --> <Table width=600> <TR> <TD width=100% colspan="5"><img src="mailbox.jpg" align="middle"> <FONT FACE="Arial, Helvetica" SIZE=5> <B><%=Server.HTMLEncode(objIADs.Get("cn"))%></B></FONT></TD> </TR> <TR> </TR> </TABLE> <Table> <TR><TD ALIGN=RIGHT NoWrap><FONT FACE="Arial, Helvetica" SIZE=2> <%=GetAttribName("Extension-Attribute-1")%></FONT></TD> <TD><FONT FACE="Arial, Helvetica" SIZE=2> <B><%=Server.HTMLEncode(objIADs.Get("Extension-Attribute-1"))%> </B></FONT></TD> </TR> <TR><TD ALIGN=RIGHT NoWrap><FONT FACE="Arial, Helvetica" SIZE=2> <%=GetAttribName("Extension-Attribute-2")%></FONT></TD> <TD><FONT FACE="Arial, Helvetica" SIZE=2> <B><%=Server.HTMLEncode(objIADs.Get("Extension-Attribute-2"))%> </B></FONT></TD> </TR> <TR><TD ALIGN=RIGHT NoWrap><FONT FACE="Arial, Helvetica" SIZE=2> <%=GetAttribName("Extension-Attribute-3")%></FONT></TD> <TD><FONT FACE="Arial, Helvetica" SIZE=2> <B><%=Server.HTMLEncode(objIADs.Get("Extension-Attribute-3"))%></B> </FONT></TD> </TR> <TR><TD ALIGN=RIGHT NoWrap><FONT FACE="Arial, Helvetica" SIZE=2> <%=GetAttribName("Extension-Attribute-4")%></FONT></TD> <TD><FONT FACE="Arial, Helvetica" SIZE=2> <B><%=Server.HTMLEncode(objIADs.Get("Extension-Attribute-4"))%> </B></FONT></TD> </TR> <TR><TD ALIGN=RIGHT NoWrap><FONT FACE="Arial, Helvetica" SIZE=2> <%=GetAttribName("Extension-Attribute-5")%></FONT></TD> <TD><FONT FACE="Arial, Helvetica" SIZE=2><B> <%=Server.HTMLEncode(objIADs.Get("Extension-Attribute-5"))%> </B></FONT></TD> </TR> <TR><TD ALIGN=RIGHT NoWrap><FONT FACE="Arial, Helvetica" SIZE=2> <%=GetAttribName("Extension-Attribute-6")%></FONT></TD> <TD><FONT FACE="Arial, Helvetica" SIZE=2> <B><%=Server.HTMLEncode(objIADs.Get("Extension-Attribute-6"))%> </B></FONT></TD> </TR> <TR><TD ALIGN=RIGHT NoWrap><FONT FACE="Arial, Helvetica" SIZE=2> <%=GetAttribName("Extension-Attribute-7")%></FONT></TD> <TD><FONT FACE="Arial, Helvetica" SIZE=2><B> <%=Server.HTMLEncode(objIADs.Get("Extension-Attribute-7"))%> </B></FONT></TD> </TR> <TR><TD ALIGN=RIGHT NoWrap><FONT FACE="Arial, Helvetica" SIZE=2> <%=GetAttribName("Extension-Attribute-8")%></FONT></TD> <TD><FONT FACE="Arial, Helvetica" SIZE=2><B> <%=Server.HTMLEncode(objIADs.Get("Extension-Attribute-8"))%> </B></FONT></TD> </TR> <TR><TD ALIGN=RIGHT NoWrap><FONT FACE="Arial, Helvetica" SIZE=2> <%=GetAttribName("Extension-Attribute-9")%></FONT></TD> <TD><FONT FACE="Arial, Helvetica" SIZE=2> <B><%=Server.HTMLEncode(objIADs.Get("Extension-Attribute-9"))%> </B></FONT></TD> </TR> </TABLE> </FORM> </DIV> |
The mailbox that the user wants to query is passed to the ASP page. Using the GetObject method, the code opens that mailbox in the Exchange Server directory and sets an object variable, objIADs, to that mailbox. Throughout much of the remaining code, the Get method of the objIADs object is used to retrieve specific attributes on the mailbox.
The most interesting pieces of code besides the code for retrieving attributes include those that retrieve the user's manager and direct reports from the directory. In the application, the manager's name is displayed as a hyperlink on the Organization tab so that users can quickly look up the manager's directory information. The direct reports of the current user are also displayed as hyperlinks on the Organization tab so that users can look at the direct report's directory information as well. Figure 15-9 shows a sample of these hyperlinks.
Figure 15-9. The Organization tab for a queried mailbox displays the manger and direct reports as hyperlinks.
The following code implements the hyperlink functionality:
<Table border=0> <TR> <TD ALIGN=LEFT NoWrap><FONT FACE="Arial, Helvetica" SIZE=2> Manager Name:</FONT></TD> <TR> </TR> <% strManager = objIADs.Get("manager") strManagerPath = "LDAP://" & Session("Server") & "/" & strManager set oIADsManager = GetObject(strManagerPath) strManagercn = Server.HTMLEncode(oIADsManager.Get("cn")) %> <TR> <TD><FONT FACE="Arial, Helvetica" SIZE=2><B> <A Href='MBINFOTABS.ASP?Path=<%=strManagerPath%>'> <%=strManagercn%></a></B></FONT></TD> </TR> <TR><TD> </TD></TR> <TR><TD ALIGN=LEFT><FONT FACE="Arial, Helvetica" SIZE=2> Direct Reports:</FONT></TD></TR> <% err.clear strReports = objIADs.GetEx("Reports") for i = LBound(strReports) to UBound(strReports) 'Get each DS object to return the friendly name strDirectPath = "LDAP://" & Session("Server") & "/" & _ strReports(i) set oIADsReports = GetObject(strDirectPath) strReportscn = Server.HTMLEncode(oIADsReports.Get("cn")) %> <TR><TD><FONT FACE="Arial, Helvetica" SIZE=2> <B><img src="mailboxs.jpg" ALIGN="Middle"> <A Href='MBINFOTABS.ASP?Path=<%=strDirectPath%>'> <%=strReportscn%></a> </B></FONT></TD></TR> <% next %> </TR></TABLE> |
When you retrieve the manager property from the Exchange Server directory, the directory returns the distinguished name of the manager. To retrieve the display name of the manager, the code uses the distinguished name to create a full AdsPath to the directory object that corresponds to the manager. Then the code opens that object and retrieves the display name of the manager.
To retrieve the direct reports, the code uses the GetEx method in ADSI. (Recall that the reports attribute is a multivalued property. You must use GetEx when retrieving multivalued properties from the Exchange Server directory.) The GetEx code returns an array of distinguished names for all direct reports of the current user. The code scrolls through each direct report in the array, and it displays as a hyperlink an image and the full name of each direct report.
The next code snippet includes some interesting code that retrieves the custom attribute names and the values for these custom attributes. The Exchange Server directory contains 15 customizable attributes that developers or administrators can use to specify custom properties for each entry in the directory. Because you can customize the attribute names so that they correspond to the value types you store in the attribute, such as cost center or social security number, the application queries the Exchange Server directory for the names of the custom attributes. The application also queries the directory for the actual values in the attributes. All of this functionality is implemented in the following section of code:
strCustomAttributes = "LDAP://" & Session("Server") & _ "/cn=Attribnum" & ",cn=" & "Microsoft DMD" & ",ou=" & _ Session("ou") & ",o=" & Session("o") Function GetAttribName(AttribName) strADsPath = Replace(strCustomAttributes,"Attribnum", _ AttribName,1,1) set objAttributeName = GetObject(strADsPath) strAttributeName = objAttributeName.Get("Admin-Display-Name") GetAttribName = strAttributeName & ":" end Function … <Table> <TR><TD ALIGN=RIGHT NoWrap><FONT FACE="Arial, Helvetica" SIZE=2> <%=GetAttribName("Extension-Attribute-1")%></FONT></TD> <TD><FONT FACE="Arial, Helvetica" SIZE=2> <B><%=Server.HTMLEncode(objIADs.Get("Extension-Attribute-1"))%> </B></FONT></TD> </TR> <TR><TD ALIGN=RIGHT NoWrap><FONT FACE="Arial, Helvetica" SIZE=2> <%=GetAttribName("Extension-Attribute-2")%></FONT></TD> <TD><FONT FACE="Arial, Helvetica" SIZE=2> <B><%=Server.HTMLEncode(objIADs.Get("Extension-Attribute-2"))%> </B></FONT></TD> </TR> <TR><TD ALIGN=RIGHT NoWrap><FONT FACE="Arial, Helvetica" SIZE=2> <%=GetAttribName("Extension-Attribute-3")%></FONT></TD> <TD><FONT FACE="Arial, Helvetica" SIZE=2> <B><%=Server.HTMLEncode(objIADs.Get("Extension-Attribute-3"))%> </B></FONT></TD> </TR> <TR><TD ALIGN=RIGHT NoWrap><FONT FACE="Arial, Helvetica" SIZE=2> <%=GetAttribName("Extension-Attribute-4")%></FONT></TD> <TD><FONT FACE="Arial, Helvetica" SIZE=2> <B><%=Server.HTMLEncode(objIADs.Get("Extension-Attribute-4"))%> </B></FONT></TD> </TR> <TR><TD ALIGN=RIGHT NoWrap><FONT FACE="Arial, Helvetica" SIZE=2> <%=GetAttribName("Extension-Attribute-5")%></FONT></TD> <TD><FONT FACE="Arial, Helvetica" SIZE=2> <B><%=Server.HTMLEncode(objIADs.Get("Extension-Attribute-5"))%> </B></FONT></TD> </TR> <TR><TD ALIGN=RIGHT NoWrap><FONT FACE="Arial, Helvetica" SIZE=2> <%=GetAttribName("Extension-Attribute-6")%></FONT></TD> <TD><FONT FACE="Arial, Helvetica" SIZE=2> <B><%=Server.HTMLEncode(objIADs.Get("Extension-Attribute-6"))%> </B></FONT></TD> </TR> <TR><TD ALIGN=RIGHT NoWrap><FONT FACE="Arial, Helvetica" SIZE=2> <%=GetAttribName("Extension-Attribute-7")%></FONT></TD> <TD><FONT FACE="Arial, Helvetica" SIZE=2> <B><%=Server.HTMLEncode(objIADs.Get("Extension-Attribute-7"))%> </B></FONT></TD> </TR> <TR><TD ALIGN=RIGHT NoWrap><FONT FACE="Arial, Helvetica" SIZE=2> <%=GetAttribName("Extension-Attribute-8")%></FONT></TD> <TD><FONT FACE="Arial, Helvetica" SIZE=2> <B><%=Server.HTMLEncode(objIADs.Get("Extension-Attribute-8"))%> </B></FONT></TD> </TR> <TR><TD ALIGN=RIGHT NoWrap><FONT FACE="Arial, Helvetica" SIZE=2> <%=GetAttribName("Extension-Attribute-9")%></FONT></TD> <TD><FONT FACE="Arial, Helvetica" SIZE=2> <B><%=Server.HTMLEncode(objIADs.Get("Extension-Attribute-9"))%> </B></FONT></TD> </TR> </TABLE> |
Notice that in the strCustomAttributes string, the container Microsoft DMD is specified. This container corresponds to the actual schema definitions for the Exchange Server directory. To retrieve the names of any custom attributes, we must query the schema. To make the querying easier, I created a function that takes a string that specifies the name of the custom attribute you want to query. The function in turn grabs that attribute in the schema and figures out the corresponding custom name of the attribute—for example, cost center. The code then uses the standard Get method on the user's mailbox to retrieve the attribute value for that particular user and attribute, as shown in Figure 15-10.
Figure 15-10. The Custom Attributes tab that shows the custom attributes for the queried mailbox and the custom names of those attributes from the Exchange Server schema.
The code for creating a custom recipient in the Exchange Server directory is similar to the code for creating a mailbox, as you would expect. The main differences are these:
The following code creates a custom recipient using ADSI:
Dim arrOtherAddresses(1) on error resume next err.clear smtp = Request.Form("SMTP") fn = Request.Form("FN") ln = Request.Form("LN") dn = Request.Form("DN") al = Request.Form("AL") dir = Request.Form("DIR") Set oIADs = GetObject("LDAP:") bstr1 = "LDAP://" + Session("Server") + "/cn=" + Session("CN") + _ ",ou=" + Session("OU") + ",o=" + Session("O") bstr2 = Session("bstr2") bstr3 = Session("bstr3") Set oContainer = oIADs.OpenDSObject(bstr1, bstr2, bstr3, 0) Set oIADs = oContainer.Create("remote-address", "cn=" + CStr(dir)) arrOtherAddresses(0) = "MS$" + Session("O") + "/" + _ Session("OU") + "/" + al arrOtherAddresses(1) = "CCMAIL$" + al + " at " + Session("OU") oIADs.Put "target-address", "SMTP:" + CStr(smtp) oIADs.Put "givenName", CStr(fn) oIADs.Put "sn", CStr(ln) oIADs.Put "cn", CStr(dn) oIADs.Put "uid", CStr(al) oIADs.Put "MAPI-Recipient", False oIADs.Put "mail", CStr(smtp) oIADs.PutEx 2, "otherMailbox", (arrOtherAddresses) oIADs.Put "rfc822Mailbox", CStr(smtp) oIADs.Put "textEncodedORaddress", CStr("c=US;a= ;p=" + _ Session("O") + ";o=" + Session("OU") + ";s=" + ln + _ ";g=" + fn + ";") oIADs.SetInfo |
Creating a distribution list, like creating a custom recipient, is again very similar to creating a mailbox. The object class for a distribution list is groupOfNames, and the properties you need to set for the distribution list are a little different from the properties you set for a mailbox. For example, for a distribution list, you can set the reportto-owner and the report-to-originator properties, which specify whether reports should be sent to the distribution list owner or to the message originator, respectively. You can also set the distribution list owner property, as shown in the next snippet of code, by placing the distinguished name of a user into the owner property. This code creates a distribution list:
<% dim arrOtherAddress(1) on error resume next err.clear cn = Request.Form("cn") uid = Request.Form("uid") owner = Request.Form("owner") Set oIADs = GetObject("LDAP:") bstr1 = "LDAP://" + Session("Server") + "/cn=" + Session("CN") + _ ",ou=" + Session("OU") + ",o=" + Session("O") bstr2 = "cn=" + Session("UserName") + ", cn=" + Session("Domain") bstr3 = Session("Password") bstrMDB = "LDAP://" + Session("Server") + _ "/cn=Microsoft Private MDB,cn=" + Session("Server") + _ ",cn=Servers ,cn=Configuration,ou=" + Session("OU") + _ ",o=" + Session("O") bstrOwner = "LDAP://" + Session("Server") + "/cn=" + owner + _ ",cn=" + Session("CN") + ",ou=" + Session("OU") + _ ",o=" + Session("O") Set oContainer = oIADs.OpenDSObject(bstr1, bstr2, bstr3, 0) Set oObject = oIADs.OpenDSObject(bstrMDB, bstr2, bstr3, 0) set oOwner = oIADs.OpenDSObject(bstrOwner,bstr2,bstr3,0) Set oIADs = oContainer.Create("groupOfNames", "cn=" + uid) oIADs.Put "cn", Cstr(cn) oIADs.Put "uid", CStr(uid) oIADs.Put "owner", oOwner.distinguishedName oObject.GetInfo Mail = oObject.Get("mail") Pos = InStr(Mail, "@") SMTPExt = Mid(Mail, Pos, Len(Mail)) iaddr = replace(uid, " ", "") + SMTPExt arrOtherAddress(0) = CStr("MS$" + Session("O") + "/" + _ Session("OU") + "/" + uid) arrOtherAddress(1) = Cstr("CCMAIL$" + uid + " at " + _ Session("OU")) oIADs.Put "distinguishedName", CStr("cn=" + uid + ",cn=" + _ Session("CN") + ",ou=" + Session("OU") + ",o=" + Session("O")) oIADs.Put "mail", CStr(iaddr) oIADs.PutEx 2, "otherMailbox", (arrOtherAddress) oIADs.Put "Report-To-Originator", True oIADs.Put "Report-to-Owner", False oIADs.Put "Replication-Sensitivity", CInt(20) oIADs.Put "rfc822Mailbox", CStr(iaddr) oIADs.Put "textEncodedORaddress", CStr("c=US;a= ;p=" + _ Session("O") + ";o=" + Session("OU") + ";s=" + uid + ";") oIADs.SetInfo if err.number = 0 then 'Success! %> <SCRIPT LANGUAGE="JavaScript"> alert("Distribution List successfully created. Please click OK to continue."); window.location="menu.asp"; </SCRIPT> <% else 'Failure! %> <SCRIPT LANGUAGE="JavaScript"> alert("Error! Error Number: <%=err.number%> Description: <%=err.Description%>"); window.location="menu.asp"; </SCRIPT> <% end if %> |
ADSI provides another interface that you can take advantage of when working with directory objects that manage group memberships such as a distribution list: the IADsGroup interface. It provides methods such as Add and Remove that make it easy to add and remove members from the group. All you need to specify to these methods is the AdsPath that corresponds to the object you will either add or remove. To remove a user from a distribution list, you would just replace the Add method call with the Remove method.
This interface also provides a Members method that returns a collection of the current members of the group. The following code checks to see whether a user is already a member of a distribution list and, if the user is not, the code adds the user to the list:
<% Set oIADs = GetObject("LDAP:") bstr1 = Session("strDLName") bstr2 = Session("bstr2") bstr3 = Session("bstr3") 'Open the Exchange Server DS object Set objDL = oIADs.OpenDSObject(bstr1, bstr2, bstr3, 0) strAlias = Request.Form("USERSELECT") bstr1 = strAlias bstr2 = Session("bstr2") bstr3 = Session("bstr3") 'Open the object in the Exchange Server DS that corresponds to 'the user set objUser = oIADs.OpenDSObject(bstr1, bstr2, bstr3, 0) booluserdoesnotexist=true for each item in objDL.Members if item.ADSPath = strAlias then booluserdoesnotexist = false end if next if booluserdoesnotexist then 'User does not exist in the Distribution List already, so add the user objDL.Add objUser.ADSPath if err.number = 0 then %> <SCRIPT LANGUAGE="JavaScript"> alert("Successfully added user to DL. Please click OK to continue."); window.location="menu.asp"; </SCRIPT> <% else 'Failure! %> <SCRIPT LANGUAGE="JavaScript"> alert("Error! Error Number: <%=err.number %> Description: <%=err.Description%>"); window.location="menu.asp"; </SCRIPT> <% end if else 'User does exist! %> <SCRIPT LANGUAGE="JavaScript"> alert("User already is a member of this DL. Please click OK to continue."); window.location="menu.asp"; </SCRIPT> <% end if %> |
The sample application allows you to display the users contained in a distribution list in an HTML table, as shown in Figure 15-11. This table is generated by using a For…Each construct to scroll through the collection returned by the Members method of the IADsGroup interface. After retrieving the object that corresponds to each member, the code checks the object class and displays the correct identifier for each member, such as mailbox, distribution list, or custom recipient. Remember that distribution lists can hold different types of objects. The code that displays users in a distribution list is shown here:
<HTML> <HEAD> <TITLE>Display users in Distribution List</TITLE> </HEAD> <BODY> <h1>The members of this Distribution List are:</font></h1> <hr> <form METHOD="POST" NAME="INFO" ACTION=""> <input TYPE="button" VALUE="Back to Main Menu" OnClick='window.location="menu.asp";'> <input TYPE="button" VALUE="Select different container" OnClick='window.location="logon.asp?diffcont=1";'> </FORM> <P> <TABLE BORDER=1 bgcolor="#79AA86"> <% dim oIADs dim MyContainer dim objRecipients dim item on error resume next err.clear strDLName = Request.Form ("DLSELECT") Set oIADs = GetObject("LDAP:") bstr1 = strDLName bstr2 = Session("bstr2") bstr3 = Session("bstr3") Set objDL = oIADs.OpenDSObject(bstr1, bstr2, bstr3, 0) Response.Write "<TR><TD><B>Class</TD><TD><B>Display Name</TD> <TD><B>Alias</TD><TD><B>Directory Name</TD></TR>" for each item in objDL.Members set objitem = oIADs.OpenDSObject(item.ADSPath, bstr2, bstr3, 0) select case item.class case "organizationalPerson" Response.Write "<TD>MailBox</TD><TD>" & _ objitem.get("cn") & "</TD><TD>" &objitem.get("uid") & _ "</TD><TD>" & item.name & "</TD></TR>" case "Remote-Address" Response.Write "<TD>Custom Recipient</TD><TD>" & _ objitem.get("cn") & "</TD><TD>" & objitem.get("uid") & _ "</TD><TD>" & item.name & "</TD></TR>" case "groupOfNames" Response.Write "<TD>Distribution List</TD><TD>" & _ objitem.get("cn") & "</TD><TD>" & objitem.get("uid") & _ "</TD><TD>" & item.name & "</TD></TR>" case else Response.Write "<TD>" & item.class & "</TD><TD>" & _ objitem.get("cn") & "</TD><TD>" & objitem.get("uid") & _ "</TD><TD>" & item.name & "</TD></TR>" end select next %> </TABLE> </FONT> |
Figure 15-11. An HTML table of the users in a specific distribution list. The table is generated by parsing the return value of the Members method of the IADsGroup interface.
The code that creates a Recipients container is probably the easiest to write in ADSI because you don't need to set many properties on it. Just remember to specify Container as the object class when creating the new Recipients container. The only gotcha when creating a Recipients container is that if you want the container to appear in the address book, you must set the Container-Info attribute to -2147483647 or &H80000001. The following code creates a Recipients container based on the values specified by the user:
<% on error resume next err.clear strDisplayName = Request.Form("display") strDirectoryName = Request.Form("dir") Set oIADs = GetObject("LDAP:") bstr1 = "LDAP://" + Session("Server") + "/ou=" + Session("OU") + _ ",o=" + Session("O") bstr2 = Session("bstr2") bstr3 = Session("bstr3") Set oContainer = oIADs.OpenDSObject(bstr1, bstr2, bstr3, 0) Set oIADs = oContainer.Create("Container", "cn=" + strDirectoryName) oIADs.Put "Container-Info", -2147483647 oIADs.Put "Admin-Display-Name", Cstr(strDisplayName) oIADs.Put "rdn", CStr(strDirectoryName) oIADs.SetInfo if err.number = 0 then 'Success! %> <SCRIPT LANGUAGE="JavaScript"> alert("Recipients Container successfully created. Please click OK to continue."); window.location="menu.asp"; </SCRIPT> <% else 'Failure! %> <SCRIPT LANGUAGE="JavaScript"> alert("Error! Error Number: <%=err.number %> Description: <%=err.Description%>"); window.location="menu.asp"; </SCRIPT> <% end if %> |
The final code we'll look at in this section is similar to the code that displays the members of a distribution list. This code, however, displays the objects contained in a specific Recipients container in the directory. The first part of the code displays the list of available Recipients containers in the directory by scrolling through the available objects below the Organizational Unit (OU), or Exchange Server site, and then parses out only the objects whose class name is Container. Since the configuration portion of Exchange Server, which contains settings for connectors, protocol settings, and monitor settings, also has an object class of Container, and you do not want users trying to display all the objects in the Configuration container, the code skips this Configuration container. The code then displays all the remaining Recipients containers so that the user can select the container whose contents they want to display.
on error resume next err.clear Set oIADs = GetObject("LDAP:") bstr1 = "LDAP://" + Session("Server") + "/ou=" + Session("OU") + _ ",o=" + Session("O") bstr2 = Session("bstr2") bstr3 = Session("bstr3") Set oRecips = oIADs.OpenDSObject(bstr1, bstr2, bstr3, 0) for each child in oRecips select case child.class case "Container" 'Block out the Configuration container if instr(child.name,"Configuration") = 0 then Response.Write "<OPTION VALUE='" & child.Name & _ "'>" & Replace(child.name,"cn=", "") end if end select next |
Next the code scrolls through all the objects in the selected container and displays the class, display name, alias, and directory name of the object by using a For Each …Next loop. The result of this code is shown in Figure 15-12.
<% on error resume next err.clear Set oIADs = GetObject("LDAP:") bstr1 = "LDAP://" + Session("Server") + "/" + Request.Form("cn") + _ ",ou=" + Session("OU") + ",o=" + Session("O") bstr2 = Session("bstr2") bstr3 = Session("bstr3") Set oRecipObjects = oIADs.OpenDSObject(bstr1, bstr2, bstr3, 0) Response.Write "<TR><TD><B>Type</TD><TD><B>Display Name</TD> <TD><B>Alias</TD><TD><B>Directory Name</TD></TR>" for each item in oRecipObjects set objitem = oIADs.OpenDSObject(item.ADSPath, bstr2, bstr3, 0) select case item.class case "organizationalPerson" Response.Write "<TD>MailBox</TD><TD>" + _ objitem.get("cn") + "</TD><TD>" + objitem.get("uid") + _ "</TD><TD>" + item.name + "</TD></TR>" case "Remote-Address" Response.Write "<TD>Custom Recipient</TD><TD>" + _ objitem.get("cn") + "</TD><TD>" + objitem.get("uid") + _ "</TD><TD>" + item.name + "</TD></TR>" case "groupOfNames" Response.Write "<TD>Distribution List</TD><TD>" + _ objitem.get("cn") + "</TD><TD>" + objitem.get("uid") + _ "</TD><TD>" + item.name + "</TD></TR>" case "Container" Response.Write "<TD>Container</TD><TD>" + _ objitem.get("rdn") + "</TD><TD>" + + "</TD><TD>" + _ item.name + "</TD></TR>" case else Response.Write "<TD>" + item.class + "</TD><TD>" + _ objitem.get("cn") + "</TD><TD>" + objitem.get("uid") + _ "</TD><TD>" + item.name + "</TD></TR>" end select next %> |
Figure 15-12. The HTML table, which is dynamically generated from the object in a Recipients container.
Raising the Number of Results Returned for LDAP QueriesBy default, Exchange Server will return only 100 results, so you might want to raise the number of results the Exchange server returns for LDAP queries. To do this, launch the Exchange Administrator program, and under the Configuration object for your site, select the Protocols icon. Double-click the LDAP (Directory) Site Defaults icon in the right pane. In the displayed Properties dialog box, click on the Search tab. Increase the number in the text box named Maximum Number Of Search Results Returned to the number of results you want to return from an LDAP query. If you do not raise this number and your LDAP query has more than the default 100 results, Exchange Server will not return any results.