broadband
broadband

Reputation: 3498

Get additional Active directory attributes from Principal instead of DirectorySearcher's SearchResult

I would like to get some additional AD attributes from the AD user. I want to use PrincipalSearcher instead of DirectorySearcher.

What I did was get the underlyingSearcher of the PrincipalSearcher and called FindOne() method. I don't like to use GetUnderlyingSearcher from PrincipalSearcher, but apparently it works.

The code works, but I'm wondering if there is a way to read additional properties/Ad attributes from Principal

Principal match = principalSearcher.FindOne();

Additionally could below code have some problems because we are using the underlyingSearcher (DirectorySearcher) methods.

I find PrincipalSearcher more higher level and also if I would use DirectorySearcher the ldapPath must be defined e.g. var ldapPath = "DC=corp,DC=ad,DC=example,DC=com"; It's just less lines of code.

public AdSimpleObject GetUser(string userName, string domainName)
{
  PrincipalContext principalContext = new PrincipalContext(ContextType.Domain, domainName);

  UserPrincipal userPrincipal = new UserPrincipal(principalContext);
  userPrincipal.SamAccountName = userName; // where condition

  using (PrincipalSearcher principalSearcher = new PrincipalSearcher(userPrincipal))
  {
    using (DirectorySearcher ds = (DirectorySearcher)principalSearcher.GetUnderlyingSearcher())
    {
      // get only properties we need, therefore search performance is increased
      ds.PropertiesToLoad.Clear();
      ds.PropertiesToLoad.AddRange(new string[]
      {
        "sAMAccountType", 
        "sAMAccountName", 
        "userPrincipalName", 
        "msDS-PrincipalName"
      });

      SearchResult match = ds.FindOne();

      if (match != null)
      {
          AdSimpleObject ado = this.CreateAdSimpleObject(match);
          return ado;
      }  
      return null;
    }
  }
}

public class AdSimpleObject
{
  public string AdsPath { get; set; }
  public int SamAccountType { get; set; }
  public string SamAccountName { get; set; }
  public string MsDsPrincipalName { get; set; }
  public string UserPrincipalName { get; set; }
}

private AdSimpleObject CreateAdSimpleObject(SearchResult searchItem)
{
  ResultPropertyCollection props = searchItem.Properties;

  string adsPath = props["adspath"]?.OfType<string>().ToList().FirstOrDefault() ?? string.Empty;
  int samAccountType = props["sAMAccountType"]?.OfType<int>().ToList().FirstOrDefault() ?? 0;
  string samAccountName = props["sAMAccountName"]?.OfType<string>().ToList().FirstOrDefault() ?? string.Empty;
  string userPrincipalName = props["userPrincipalName"]?.OfType<string>().ToList().FirstOrDefault() ?? string.Empty;
  string msDsPrincipalName = props["msDS-PrincipalName"]?.OfType<string>().ToList().FirstOrDefault() ?? string.Empty;

  return new AdSimpleObject
  {
      AdsPath = adsPath,
      SamAccountType = samAccountType,
      SamAccountName = samAccountName,
      UserPrincipalName = userPrincipalName,
      MsDsPrincipalName = msDsPrincipalName
  };
}

Upvotes: 1

Views: 1714

Answers (1)

Gabriel Luci
Gabriel Luci

Reputation: 40988

I'm wondering if there is a way to read additional properties/Ad attributes from Principal

You would use the DirectoryEntry object returned from Principal.GetUnderlyingObject:

Principal match = principalSearcher.FindOne();
var de = (DirectoryEntry) match.GetUnderlyingObject();
de.RefreshCache(new string[] {"someAttribute"});
var someAttribute = de.Properties["someAttribute"].Value;

Doing this will go back out to AD to get the attributes, instead of using the data that was retrieved in the search. The use of RefreshCache is to tell it to only get the attributes you are interested in. Otherwsie, if you use DirectoryEntry.Properties right away, it will go out to AD and get all attributes that have a value, which is unlikely what you need, and will just take extra time for no reason.

Additionally could below code have some problems because we are using the underlyingSearcher (DirectorySearcher) methods.

Not at all. That code is no different than just making your own DirectorySearcher and using it. In fact, with the way you've written this code, there is no real point to using UserPrincipal/PrincipalSearcher at all. The only thing it's done for you is build the query string.

The whole AccountManagement namespace is just a wrapper around DirectoryEntry/DirectorySearcher. It makes things easier for the programmer (in some cases, not all), but it does so at the cost of performance.

If you use DirectoryEntry/DirectorySearcher yourself directly, you have far more control over how much data is retrieved from AD, and how often network requests are made. Those translate into less time taken. If you're just searching for one account, then it won't make much of a difference. But if you're searching for large groups of users, or looping over a large list of users, it can make a huge difference.

I wrote an article about optimizing performance when talking to AD (specifically with DirectoryEntry/DirectorySearcher): Active Directory: Better performance

Upvotes: 3

Related Questions