Downloads
50543.zip

As a company grows, so does its number of personnel. And, as any systems administrator can tell you, as the number of personnel grows, so does the number of user accounts that the administrator needs to manage. For the most part, Active Directory (AD) scales up well so that managing large numbers of users isn't typically difficult. Because each user has a unique user account, locating and modifying a user's profile settings is easy. However, there's one exception to this rule: the vast sea of email addresses that can plague the network administrator.

Administrators often create multiple email accounts for one user. As a prime example, even the administrator probably has multiple addresses, such as admin@yourcompany.com, administrator@yourcompany.com, hostmaster@yourcompany.com, and webadmin@yourcompany.com. The existence of multiple addresses (aka proxy addresses) can make it difficult to determine which user account receives email for any given email address.

In this article, I offer a script that leverages Perl and some Windows technologies to query AD and determine which user account receives mail for a given email address. From any machine on the network, you can simply open a command-line window, run this script, and achieve the desired results.

Managing Email Addresses
You can use an AD account's E-Mail property to identify a particular user's email address. Because AD doesn't offer native email support, this email address can be pretty much anything—jon.doe@gmail.com, 12345@ yourcompany.com, and so on. I equate the usefulness of this address with some of the other AD account properties, such as Telephone Number and Web Page URL. The primary value of these properties is that some programs can query AD for them and display them onscreen for the user.

However, if you have Microsoft Exchange Server installed in your AD network, giving your accounts email addresses can be particularly useful: Users can actually send email to these accounts. One interesting Exchange property associated with a user account's mailbox is called proxyAddresses. This property is a list of all email accounts that the mailbox can receive. This list of addresses lets a given user receive mail that's sent to separate addresses— for example, a sales rep can receive mail for the addresses sales@my company.com, customer_questions@ mycompany.com, and leads@my company.com.

Querying AD
In a network of thousands of user accounts, with potential multitudes of unusually named email addresses for each user account, determining the sole recipient of several email addresses can be challenging. There are many ways to query AD for information (e.g., the proxyAddresses lists)—for example, you can use Lightweight Directory Access Protocol (LDAP), among other technologies. However, by using ADO databases (ADODB) and Active Directory Service Interfaces (ADSI), the FindEmail.pl script— which you'll find in Web Listing 1—turns a monumental task into an easy one.

One of the main reasons for using ADODB and ADSI is that they both use Windows' user credentials to query AD so that your script doesn't have to specify user IDs and passwords to gain access to the database. And ADODB is a quick and efficient way to perform database queries into ADSI.

Generally speaking, querying AD is pretty simple. You use ADSI to locate an AD server on your network, then submit a query looking for email addresses. Finally, you walk through the resulting list of accounts that match your query criteria. Of course, the actual execution is a bit more complicated, as you'll see in the next section. The most complicated aspect of querying AD is constructing the query criteria. For information about how to do it, see the Microsoft article "Microsoft OLE DB Provider for Microsoft Active Directory Service" (http://msdn.microsoft.com/library/en-us/ ado270/ htm/ mdrefad sprovspec.asp).

To query AD, the FindEmail.pl script uses the following criteria:

<LDAP://$Config\{address_path\}
  $DomainContext>
  (| (mail=*$EmailAddress*)
  (proxyAddresses=*:*
  $EmailAddress*));ADsPath;
  subtree

This criteria specifies that the query will start at the tree level of $Config \{address_path\}$DomainContext. When the script runs, the query will default to the top level of the AD domain, which is what $ DomainContext represents.

If your AD domain is large, searching for email addresses can take considerable time. Therefore, you can specify an address path—for example, only within a particular organizational unit (OU). Suppose your AD domain has separate OUs for the Sales Department, Executives, and IT. You can search only the Sales Department by specifying an address path of OU =Sales,OU=Company_Departments. The next part of the query is the filter. In this case, the filter criteria are looking for any account that has a mail or proxyAddresses property that contains the string the user specified. In the case of proxyAddresses, the email address is in the protocol:address format. Internet mail (aka SMTP) addresses would be in the smtp:john .doe@mycompany.com format. Note that the protocol is case-sensitive; an address with uppercase protocol is the primary address for that particular protocol. Exchange uses this primary address in the From field of outgoing messages from the account. Only one proxy address can be a protocol's primary address.

The next part of the query— ADsPath—specifies a list of commadelimited AD properties that you want the query to return. In this example, the query is asking only for the ADsPath (e.g., LDAP://CN=Administrator, CN= Users,DC=mycompany,DC=com). The script later uses this data to obtain the actual user account ADSI object.

The final part of the query specifies the query's scope. The script's query criteria indicate the scope to be subdir, which recursively searches for matches from the address path and below. Other options are base and onelevel, about which you can find details in the aforementioned Microsoft article.

Using the Script
The simple-to-use FindEmail.pl script assumes that the computer running the script is a member of an AD domain and that the user has permissions to submit LDAP queries. Run the script, passing in either a full email address or part of an address. You can also specify multiple addresses. For example,

perl FindEmail.pl droth@
  windowsitpro.com admin

displays all user accounts that have an email or proxy address containing either droth@windowsitpro.com or admin (e.g., admin@mycompany .com,john.doe@administration.com). If you know a specific address path to use, you can speed up your search. However, unless you're a systems administrator or Exchange administrator,

you probably don't know this information. The example

perl FindEmail.pl
  -a "OU=Sales,OU=Departments"
  -s MyServer
  discounts@mycompany.com

searches for discounts@mycompany .com but only in the Sales OU and any subcontainer. The script connects to MyServer to perform the query.

How the Script Works
The FindEmail.pl script uses COM to interact with both ADODB and ADSI. Therefore, it has to use the Win32::OLE extension (which provides Windows COM functionality). Listing 1 an excerpt of the FindEmail.pl script, uses Win32::OLE to obtain a COM object that represents an ADSI connection to AD. The call to Win32::OLE-> GetObject() is interesting because it doesn't specify a COM class ID, as you would normally do when accessing a COM object. However, ADSI registers several COM monikers that take the place of a COM class ID. In this case, the LDAP:// moniker invokes ADSI. The LDAP://$Config\{server\}RootDse string tells COM to invoke ADSI and connect to AD's root Directory Systems-Agent Specific Entry (DSE). Think of DSE as the top level of your AD implementation. If the user runs the script and specifies a server to use (by using the -s switch), the string will look like LDAP://your_server/RootDse.

Listing 1 continues by calling into the FindEmailAddressAccount() subroutine, which performs an account search for each email address that the user has specified. The code then passes the results of the search into the Display() subroutine to present the data to the user.

The FindEmailAddressAccount() subroutine, which Listing 2 shows, performs the email address lookup and maps them to accounts. First, it retrieves the default naming context (i.e., a text string that ADSI uses to determine where in AD to start the search) from the AD server. Typically, the default naming context represents the top level of the AD tree. For example, a domain called MyDomain.com would result in a Default Naming Context of "DC=MyDomain,DC=com".

Next, the subroutine creates the query that it will submit to AD. Here is where the code uses the default naming context. If the user specified an address path, the path is prepended to the naming context. The script then creates a generic ADODB connection object, configures it to use the ADSI provider, and uses it to execute the query. If the query is successful, it returns an ADODB recordset object ($Record) that contains the query results. For each database record returned in the record set, the script retrieves an ADSI account object by passing the record's ADsPath value (the only field that the query requested) into Win32::OLE->

GetObject(). The account is pushed onto an array and the While loop tells the recordset to move to the next record. This process continues until the $Record object indicates that it has hit the End Of File (EOF)—that is, $Record->EOF no longer equals zero. At this point, the $Record object has no more records left to process.

The Display() subroutine loops through each ADSI account object in the $AccountList array reference passed into the routine. For each of these accounts, the Exchange proxy-Addresses property is examined. Commonly, the proxyAddresses value is an array of email addresses prefixed with the addresses' protocol. However, in some cases, the property might not contain an array of addresses but instead a single address. Listing 3 manages these cases by checking whether the property is an array. If the property is an array, the code dereferences the property, casting it into an array context and pushing array entries onto a temporary @ProxyList array. If the property is a single address, the code pushes it directly onto the @ProxyList array.

The script then processes the list of email addresses. The list consists of both the addresses in the @ProxyList array (essentially, the list of proxyAddresses) and the address from the AD mail property. Typically, the mail address is already in the proxyAddresses list, so a duplicate might occur in the resulting displayed list.

However, sometimes these addresses are mutually exclusive.

The script examines each entry in the list to determine its protocol and address. If the address is the protocol's primary address, it's prefixed with an asterisk (*). To determine the primary address, the script tests whether the protocol is all uppercase. The address is then pushed onto an array that's stored as a value of the % ProxyAddressList hash, in which the protocol is the associated key.

Next, the script displays some account information, and finally Listing 4 is executed. This block of code retrieves the address protocols from the keys of the %ProxyAddressList hash and sorts them. Then, for each protocol, the various email addresses are sorted and printed.

Email Lookups Made Simple
With this article's script, you'll find that locating email addresses is easy. Historically, you would have needed to use AD tools to log on to a machine, run an AD query tool, create a query for a specific email address, and then wait. It wasn't an impossible process, but it was cumbersome, mistakeprone,and time consuming. Once again, Perl comes to the rescue.