title: Getting all direct Reports from Active Directory
body: <p>I'm trying to get all the direct reports of a User through Active Directory, recursively.
  So given a user, i will end up with a list of all users who have this person as manager or who have a person as manager who has a person as manager ... who eventually has the input user as manager.</p>
  
  <p>My current attempt is rather slow:</p>
  
  <pre><code>private static Collection&lt;string&gt; GetDirectReportsInternal(string userDN, out long elapsedTime)
  {
      Collection&lt;string&gt; result = new Collection&lt;string&gt;();
      Collection&lt;string&gt; reports = new Collection&lt;string&gt;();
  
      Stopwatch sw = new Stopwatch();
      sw.Start();
  
      long allSubElapsed = 0;
      string principalname = string.Empty;
  
      using (DirectoryEntry directoryEntry = new DirectoryEntry(string.Format("LDAP://{0}",userDN)))
      {
          using (DirectorySearcher ds = new DirectorySearcher(directoryEntry))
          {
              ds.SearchScope = SearchScope.Subtree;
              ds.PropertiesToLoad.Clear();
              ds.PropertiesToLoad.Add("directReports");
              ds.PropertiesToLoad.Add("userPrincipalName");
              ds.PageSize = 10;
              ds.ServerPageTimeLimit = TimeSpan.FromSeconds(2);
              SearchResult sr = ds.FindOne();
              if (sr != null)
              {
                  principalname = (string)sr.Properties["userPrincipalName"][0];
                  foreach (string s in sr.Properties["directReports"])
                  {
                      reports.Add(s);
                  }
              }
          }
      }
  
      if (!string.IsNullOrEmpty(principalname))
      {
          result.Add(principalname);
      }
  
      foreach (string s in reports)
      {
          long subElapsed = 0;
          Collection&lt;string&gt; subResult = GetDirectReportsInternal(s, out subElapsed);
          allSubElapsed += subElapsed;
  
          foreach (string s2 in subResult)
          {
          result.Add(s2);
          }
      }
  
  
  
      sw.Stop();
      elapsedTime = sw.ElapsedMilliseconds + allSubElapsed;
      return result;
  }
  </code></pre>
  
  <p>Essentially, this function takes a distinguished Name as input (CN=Michael Stum, OU=test, DC=sub, DC=domain, DC=com), and with that, the call to ds.FindOne() is slow.</p>
  
  <p>I found that it is a lot faster to search for the userPrincipalName. My Problem: sr.Properties["directReports"] is just a list of strings, and that is the distinguishedName, which seems slow to search for.</p>
  
  <p>I wonder, is there a fast way to convert between distinguishedName and userPrincipalName? Or is there a faster way to search for a user if I only have the distinguishedName to work with?</p>
  
  <p><strong>Edit:</strong> Thanks to the answer! Searching the Manager-Field improved the function from 90 Seconds to 4 Seconds. Here is the new and improved code, which is faster and more readable (note that there is most likely a bug in the elapsedTime functionality, but the actual core of the function works):</p>
  
  <pre><code>private static Collection&lt;string&gt; GetDirectReportsInternal(string ldapBase, string userDN, out long elapsedTime)
  {
      Collection&lt;string&gt; result = new Collection&lt;string&gt;();
  
      Stopwatch sw = new Stopwatch();
      sw.Start();
      string principalname = string.Empty;
  
      using (DirectoryEntry directoryEntry = new DirectoryEntry(ldapBase))
      {
          using (DirectorySearcher ds = new DirectorySearcher(directoryEntry))
          {
              ds.SearchScope = SearchScope.Subtree;
              ds.PropertiesToLoad.Clear();
              ds.PropertiesToLoad.Add("userPrincipalName");
              ds.PropertiesToLoad.Add("distinguishedName");
              ds.PageSize = 10;
              ds.ServerPageTimeLimit = TimeSpan.FromSeconds(2);
              ds.Filter = string.Format("(&amp;(objectCategory=user)(manager={0}))",userDN);
  
              using (SearchResultCollection src = ds.FindAll())
              {
                  Collection&lt;string&gt; tmp = null;
                  long subElapsed = 0;
                  foreach (SearchResult sr in src)
                  {
                      result.Add((string)sr.Properties["userPrincipalName"][0]);
                      tmp = GetDirectReportsInternal(ldapBase, (string)sr.Properties["distinguishedName"][0], out subElapsed);
                      foreach (string s in tmp)
                      {
                      result.Add(s);
                      }
                  }
              }
            }
          }
      sw.Stop();
      elapsedTime = sw.ElapsedMilliseconds;
      return result;
  }
  </code></pre>