I encounter questions like this on a fairly regular basis. Just a few minutes ago I hit it in the newsgroups and I responded, I thought, you know, that would make a good blog entry… So here it is….
The question was
Does anyone have a set of queries that I can use to re-construct the GAL in
my application
This was responded to by one of the good Exchange MVPs with a link to his website showing how to do this.
http://www.infinitec.de/exchange/howtos/getgal.aspx
The mechanism outlined has you pulling the purportedSearch attribute from the Address List (AL) that you are interested in and performing that same query against AD.
While there are times you want to do that, like when comparing what the GAL is versus what it should be (you DO check that occasionally right?) but other than that, it is a waste of processor cycles.
Here is my response…
-=-=-=-=-=-=-=-=-=-=-=-
While that mechanism would work to show objects that should be in the GAL, I see two issues with it.
1. It is who should be in it, not what is actually in it. More on that below.
2. Those queries are horrendous.
The purportedSearch query is NEVER used against Active Directory as an LDAP query by the RUS to produce the ALs. The only time the filter is used as an actual LDAP filter against AD is when you click on the preview button in the ESM.
The query is actually processed against every object the RUS looks at via logic internal to the RUS. This means that it is possible, and I have encountered it in real life, where AD would return a different set of matching values than the RUS does. Most people never figure out that the GAL or any other AL doesn’t match with the true listing because they never do a comprehensive scan of what is in the GAL listing versus what should be in the listing.
The people that figure it out are the ones that make a custom filter and hit some issue with how the RUS handles the comparisons[1] and the list gets populated with nearly nothing. Some people will wait and wait thinking it is a timing or replication thing, finally after a week they realize it isn’t going to happen and play with it until it seems to work right. Alternatively, people figure it out if someone important is noticed missing and someone has to dig into it.
Anyway, the purportedSearch is used by the RUS to stamp the mail objects with values in their showInAddressBook attribute. The simplest thing to do to get the GAL list, is to use that attribute which the RUS has already populated. The AL DNs for any ALs the object is a member of is listed in the multi-value attribute. For instance, a normal user in my test lab Exchange environment has these values set:
>showInAddressBook: CN=Default Global Address List,CN=All Global Address Lists,CN=Address Lists Container,CN=joeware,CN=Microsoft Exchange,CN=Services,CN=Configuration,DC=joe,DC=com
>showInAddressBook: CN=All Users,CN=All Address Lists,CN=Address Lists Container,CN=joeware,CN=Microsoft Exchange,CN=Services,CN=Configuration,DC=joe,DC=com
This means the user is in the default GAL and the all users AL.
So looking at the queries, you have the option of using this Default GAL query
(& (mailnickname=*) (| (&(objectCategory=person)(objectClass=user)(!(homeMDB=*))(!(msExchHomeServerName=*)))(&(objectCategory=person)(objectClass=user)(|(homeMDB=*)(msExchHomeServerName=*)))(&(objectCategory=person)(objectClass=contact))(objectCategory=group)(objectCategory=publicFolder)(objectCategory=msExchDynamicDistributionList) ))
which breaks down to this easier to read filter
(& (mailNickname=*) (| (& (objectCategory=CN=Person,CN=Schema,CN=Configuration,DC=joe,DC=com) (objectClass=user) (! (homeMDB=*) ) (! (msExchHomeServerName=*) ) ) (& (objectCategory=CN=Person,CN=Schema,CN=Configuration,DC=joe,DC=com) (objectClass=user) (| (homeMDB=*) (msExchHomeServerName=*) ) ) (& (objectCategory=CN=Person,CN=Schema,CN=Configuration,DC=joe,DC=com) (objectClass=contact) ) (objectCategory=CN=Group,CN=Schema,CN=Configuration,DC=joe,DC=com) (objectCategory=CN=ms-Exch-Public-Folder,CN=Schema,CN=Configuration,DC=joe,DC=com) (objectCategory=CN=ms-Exch-Dynamic-Distribution-List,CN=Schema,CN=Configuration,DC=joe,DC=com) ) )
or you can use this much better query
&(mailnickname=*)(showinaddressbook=CN=Default Global Address List,CN=All Global Address Lists,CN=Address Lists Cont ainer,CN=joeware,CN=Microsoft Exchange,CN=Services,CN=Configuration,DC=joe,DC=com)
Which breaks down to be
(& (mailNickname=*) (showInAddressBook=CN=Default Global Address List,CN=All Global Address Lists,CN=Address Lists Container,CN=joeware,CN=Microsoft Exchange,CN=Services,CN=Configuration,DC=joe,DC=com) )
The statistics from the DC tell the whole tale here.
Using the query I propose you get something like this:
Elapsed Time: 10 (ms)
Returned 5059 entries of 5086 visited – (99.47%)Index Name : idx_mailNickname
Record Count: 4416 (estimate)
Index Type : Normal Attribute Index
Using the purportedSearch query you get something like this:
Elapsed Time: 220 (ms)
Returned 5082 entries of 5086 visited – (99.92%)
Used Indices:
idx_mailNickname:4416:NIndex Name : idx_mailNickname
Record Count: 4416 (estimate)
Index Type : Normal Attribute Index
Note that even here, you can see that there is a delta in the query above and what the RUS actually stamped….
The main thing to notice though is the elapsed time differences. The purportedSearch filter was 22 times slower. Running each query multiple times resulted in the purportedSearch speeding up to about 200ms at best due to caching. The other query stayed at 10ms.
joe
[1] One good example is using the NOT (!) operator which I have seen push the RUS off the deep end when building ALs if you do it like (!attr=val) instead of (!(attrib=val)).