Search code examples
c#active-directorydirectoryservices

Active Directory not finding all users in C#


I have some code that queries Active Directory to verify user existence. I am trying to verify a long list of about 1300 ids. I've tried several methods to verify if a user account (LINQ to AD, DirectorySearcher (with and without a parent DirectoryEntry) and also a DirectoryEntry that links to the WinNT:// path). Every time it will come back and say that several users do not exist. If I hardcode their userids in the code and execute for individually, it validates existence. If I try and do it in a foreach loop, I get several false negatives.

Here's the code I am using right now..

static string[] userIDs = new string[] "user1","user2","user3","user4","user5","user6","user7","user8"...,"user1300"};

List<string> nonExistingUsers = new List<string>();
List<string> ExistingUsers = new List<string>();
foreach (string s in userIDs)
{
 DirectorySearcher search = new DirectorySearcher();
 search.Filter = String.Format("(SAMAccountName={0})", s);
 search.PropertiesToLoad.Add("cn");
 DirectorySearcher ds = new DirectorySearcher(de, "(&(objectClass=user)(cn=" + s + "))", new string[] { "Name" }, SearchScope.Subtree);
 SearchResultCollection resultCollection = ds.FindAll();
 SearchResult result = search.FindOne();
 if (result != null)
  ExistingUsers.Add(s);
 else
  nonExistingUsers.Add(s);
}

Any suggestions or reasons why I am getting the false negatives?


Solution

  • Couple of things:

    • first of all, try using the "anr=" (ambiguous name resolution) in your LDAP filter - it searches several name-related attributes and make searching easier. The UserID might not be part of the actual "common name" (CN=user1)

    • secondly, use the objectCategory instead of objectClass - the objectCategory is single-valued and indexed and thus a fair bit faster on searches

    • thirdly: why are you first calling .FindAll() and then .FindOne() on the next line? Doesn't seem really necessary at all....

    • WinNT:// really is only for backward compatibility and if you need to deal with local computer accounts - try to avoid it whenever possible, it also exposes a lot less properties than LDAP

    Here's my code I'd write:

    static string[] userIDs = new string[] "user1","user2","user3","user4","user5","user6","user7","user8"...,"user1300"};
    
    DirectoryEntry searchRoot = new DirectoryEntry("LDAP://cn=Users,dc=YourComp,dc=com");
    
    List<string> nonExistingUsers = new List<string>();
    List<string> ExistingUsers = new List<string>();
    
    foreach (string s in userIDs)
    {
       DirectorySearcher search = new DirectorySearcher(searchRoot);
    
       search.SearchScope = SearchScope.Subtree;
       search.Filter = string.Format("(&(objectCategory=person)(anr={0}))", s);
    
       SearchResultCollection resultCollection = ds.FindAll();
    
       if(resultCollection != null && resultCollection.Count > 0)
          ExistingUsers.Add(s);
       else
          nonExistingUsers.Add(s);
    }
    

    Does that work in your scenario??

    Also, if you're using .NET 3.5 or higher, things got a lot easier - see:

    Managing Directory Security Principals in the .NET Framework 3.5