10

I found How can I get a list of users from active directory?

It is helpful when I only have a few users, but I have so many users in AD, so when I run my query

if ((String)(entry.Properties["sn"].Value) == "lname"
     && (String)(entry.Properties["givenName"].Value) == "fname")
{
    return entry.Properties["samAccountName"].Value.ToString();
}

it took too long to finish.

What can I do to search one particular user logon id by first name and last name?

Community
  • 1
  • 1
user1225072
  • 335
  • 1
  • 4
  • 11

4 Answers4

12

Since you're on .NET 4, you should check out the System.DirectoryServices.AccountManagement (S.DS.AM) namespace. Read all about it here:

Basically, you can define a domain context and easily find users and/or groups in AD:

// set up domain context
PrincipalContext ctx = new PrincipalContext(ContextType.Domain);

// find a user - by e.g. his "samAccountName", or the Windows user name or something
UserPrincipal user = UserPrincipal.FindByIdentity(ctx, "SomeUserName");

if(user != null)
{
   // do something here....     
   string samAccountName = user.SamAccountName;
}

If you cannot find a user specified by a user name, you can also use the new search functionality:

// define a "query-by-example" principal - here, we search for a UserPrincipal 
// and with the first name (GivenName) and a last name (Surname) 
UserPrincipal qbeUser = new UserPrincipal(ctx);
qbeUser.GivenName = firstName;
qbeUser.Surname = lastName;

// create your principal searcher passing in the QBE principal    
PrincipalSearcher srch = new PrincipalSearcher(qbeUser);

// find all matches
foreach(var found in srch.FindAll())
{
    // do whatever here - "found" is of type "Principal" - it could be user, group, computer.....          
}

The new S.DS.AM makes it really easy to play around with users and groups in AD! And just finding a single user should be relatively quick, too.

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
  • @marc_s this was exactly what I was looking for, perfect, thanks... however, I did find out the hard way that if any field you request from the user for searching is not needed, skip the corresponding property in the `UserPrincipal`. I originally was pushing `string.empty` and then even `null` and was having **no** results returned. However, as soon as I added an `if` statement and ignored that item in the `UserPrincipal`, I finally had results returned (_ie: skipped **GivenName** when the **FirstName** was not populated, instead of setting to default_). – famousKaneis Nov 22 '16 at 23:24
4

You should be using the AD server to do the filtering. Do this by supplying an LDAP syntax filter. Also, specify only the properties you need using the propertiesToLoad argument of FindAll:

    public static SearchResultCollection FindByName(
        string domain, string firstName, string lastName, string[] properties) {
        var rootEntry = new DirectoryEntry("LDAP://" + domain);
        var filter = string.Format("(&(sn={0})(givenName={1}))", lastName, firstName);
        var searcher = new DirectorySearcher(rootEntry, filter, properties);
        return searcher.FindAll();
    }

    // Using the method:
    var result = FindByName("mydomain", "Robert", "Smith", new[] { "samAccountName" })[0];
    string uName = (string)result.Properties["samAccountName"][0];
Joshua Honig
  • 12,925
  • 8
  • 53
  • 75
1

You need to set the QueryFilter property on searcher, and then call searcher.FindOne() instead of searcher.FindAll(). The query filter can be set to a UserPrincipal object where you set the fields that you want to search on.

Microsoft has a good example of this at the Query By Example page though their example expects to find several objects matching the given criteria.

A particularly literal adaptation of the linked question to your requirement would be

using (var context = new PrincipalContext(ContextType.Domain, "mydomain.com"))
{
    using (var searcher = new PrincipalSearcher(new UserPrincipal(context) { GivenName = "fname", Surname = "lname" }))
    {
            foreach (var result in searcher.FindAll())
            {
                DirectoryEntry de = result.GetUnderlyingObject() as DirectoryEntry;
                return de.Properties["samAccountName"].Value.ToString();
            }
    }
}
dsolimano
  • 8,870
  • 3
  • 48
  • 63
0

If entry is part of an IEnumerable collection, you could do something like:

var entries = {do your population of the collection here}

var entry = entries.Where(e=>e.Properties["sn"].Value.ToString() == "lname"
    && e=>.Properties["givenName"].Value.ToString() == "fname")
    .FirstOrDefault();
Jeremy Holovacs
  • 22,480
  • 33
  • 117
  • 254