Sunday, 5 July 2015

AD Fast Querying

The other day at work I went through again a issue I had already experienced in previous occasions. Querying the Windows AD for some basic need can be terribly slow. I have no idea about why, but I've suffered this while querying different AD's (from different companies). So this time I've gone through an alternative route.

As far as I know the recommended way to query the AD from modern versios of .Net is using the classes in the System.DirectoryServices.AccountManagement namespace, like PrincipalContext and UserPrincipal, so if you want to get the AD groups a user is member of you would do something like this:

private static List<string> GetGroupNamesBasedOnPC(string userName, string domainName)
 {
  List<string> result;

  using (PrincipalContext pc = new PrincipalContext(ContextType.Domain, domainName))
  {
   using (PrincipalSearchResult<.Principal> src = UserPrincipal.FindByIdentity(pc, userName).GetGroups(pc))
   {
    result = src.Select(sr => sr.SamAccountName).ToList();
   }
  }
  return result;
 }

The above code is terribly sloooooowwwwww.

Hopefully, there are alternatives (don't ask me why these alternatives are way faster, I have no clue).

  • You can use the very fast net user command like this:
    net user userName /domain
    notice that it will search the current domain, so the "/domain" string is right that, you don't have to replace it by the domain name, I mean:
    net user francoise12 /domain
    The problem with that approach is that the returned string comes formatted for the Windows Console and some characters can be trimmed. So large group names will be trimmed and this command is not valid for general cases.

  • Another option is using the gpresult command, as along with information about the installed gpo's it will also return the list a groups for the current user, and this time with no odd formatting causing trimming.
    gpresult /user ng3419a /r
    The problem with this is that it only works for the current user, you can't pass the user name as parameter

  • And finally the good one:
    net group groupName /domain
    Here you'll get a listing with just the group names, one per row, so no trimming problems. The windows commandline is not that primitive as many people use to think (and yes, of course with PowerShell is really powerful), so if you just want to verify if a user belongs to a certain group you can pipe the output to the find command like this:
    net group groupName /domain | find /I /C userName
    If you want to run it from a .Net application you just do this (notice that net group is a command, not a program, so the process that you have to start is cmd.exe)
    string command = "/c net group /domain " + group + " | find /I /C \"" + user + "\"";
    Process process = new Process();
    process.StartInfo.FileName = "cmd.exe";
    process.StartInfo.Arguments = command;
    process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
    //need these 2 lines in order to read from the stdout
    process.StartInfo.UseShellExecute = false;
    process.StartInfo.RedirectStandardOutput = true;
    process.Start();
    process.WaitForExit();// Waits here for the process to exit.
    string output = process.StandardOutput.ReadToEnd(); 
    bool belongsToGroup = output[0] == '1';
    

No comments:

Post a Comment