I had this problem I needed to solve earlier today which involved going through about 115,000 users and determining which ones existed in the source of record (some tables in an Oracle database). There's a bunch of ways to go about this, but, I settled on a solution that involved storing the state information in Active Directory.
What I decided to do was task extensionAttribute10 from Exchange as a temporary counter variable on the user objects. Exchange conveniently indexes this attribute as of Exchange 2007 as well which made it further conducive to the task. Thus, the first thing I did is use adfind and admod to set extensionAttribute10 to 0 on all my users in question:
adfind.exe -f "(&(objectCategory=person)(objectClass=user))" -rb "ou=people" -adcsv | admod.exe -unsafe extensionAttribute10::0
In this case I only wanted to find all the users in the People OU under the root of my default domain.
adfind.exe -f "(&(objectCategory=person)(objectClass=user)(extensionAttribute10=*))" -rb "ou=people" extensionAttribute10
Now that I have an initial baseline state I was able to use some pre-existing code which queries this Oracle data and each time I found a user in Oracle, I set their extensionAttribute10 equal to 1:
adfind.exe -f "(&(objectCategory=person)(objectClass=user)(samAccountName={0})" -rb "ou=people" -adcsv | admod.exe –upto 1 extensionAttribute10::1
My code replaces {0} with the user's samAccountName as defined in the source data. Obviously you could use another attribute depending on how your users are provisioned in Active Directory. Finally, I used adfind once more to find the users who do not have a matching record in the source data:
adfind -f "(&(objectCategory=person)(objectClass=user)(extensionAttribute10=0))" -rb "ou=people" -c
adfind -f "(&(objectCategory=person)(objectClass=user)(extensionAttribute10=0))" -rb "ou=people" -list samAccountName > "RogueUsers1.txt"



Very cool use of adfind/admod Brian. Do you ever work in environments with less than 100k users?
Talk to you later
Mike