Search code examples
c#active-directorydirectoryservices

Active Directory Query Users with contains ids from an array using C#


I am trying to perform a query to Active Directory to obtain all users where id is contained in the input array using a single query.

Is it possible? It

public List<Principal> Get(IEnumerable<string> ids)
{
    var context = new PrincipalContext(ContextType.Domain);

    var userPrincipal = new UserPrincipal(context)
    {
        // SOME logic with input array
    };

    var searcher = new PrincipalSearcher(userPrincipal);

    return searcher.FindAll().ToList();
}

It is not a problem to query single user by id:

public static UserPrincipal? Get(string id)
{
    var context = new PrincipalContext(ContextType.Domain);

    var userPrincipal = UserPrincipal.FindByIdentity(context, id);

    return userPrincipal;
}

Also, I can not find any information in docs


Solution

  • You can't do it with PrincipalSearcher. You'll have to use DirectorySearcher (which is what PrincipalSearcher uses behind the scenes anyway).

    I actually already wrote a method that does exactly this in an article I wrote about getting better performance when programming withActive Directory. That method takes usernames and returns a list of email addresses:

    public IEnumerable<string> GetEmailAddresses(IEnumerable<string> usernames) {
        var filter = new StringBuilder();
        var numUsernames = 0;
        
        var e = usernames.GetEnumerator();
        var hasMore = e.MoveNext();
        
        while (hasMore) {
            var username = e.Current;
            filter.Append($"(sAMAccountName={username})");
            numUsernames++;
            
            hasMore = e.MoveNext();
            if (numUsernames == 50 || !hasMore) {
                var search = new DirectorySearcher(new DirectoryEntry()) {
                    PageSize = 1000,
                    Filter = $"(&(objectClass=user)(objectCategory=person)(|{filter}))"
                };
                search.PropertiesToLoad.Add("mail");
                
                using (var results = search.FindAll()) {
                    foreach (SearchResult result in results) {
                        yield return (string) result.Properties["mail"][0];
                    }
                }
                filter.Clear();
                numUsernames = 0;
            }
        }
    }
    

    This method builds an LDAP query in the format:

    (&(objectClass=user)(objectCategory=person)(|(sAMAccountName={username1})(sAMAccountName={username2})))
    

    It does them in batches of 50 just to make sure you never hit the size limit on LDAP requests, although you could probably push that number higher.

    You can adapt it to return whatever you'd like by changing the method signature and the yield return line.

    In that same article I discuss the benefits of using DirectoryEntry/DirectorySearcher instead of the AccountManagement namespace. You can get far better performance.