Blog Home  Home Feed your aggregator (RSS 2.0)  
.Net Jonesie - Using System.DirectoryServices.AccountManagement
A simple programmers blog
 
# Friday, August 26, 2011

This week I’ve been creating some integration code between our web product (an ASP.Net web forms app) and LDAP – or more specifically, Active Directory.  This involves loading principals (groups and users) and OU’s from AD into our custom database and updating them on a schedule. 

In the past I have used System.DirectoryServices to do any work with AD, and this has been, to say the least, not one of my favorite things in life to do. It seems like the people who invented LDAP have brains that don’t work the same way as ‘normal’ geeks – at least, not the same way as me.

So, I was very pleased to discover System.DirectoryServices.AccountManagent.  This provides a higher level of abstraction over LDAP’s and makes the simple tasks much simpler. 

Firstly, you create a context.  I have some properties and a helper method for this:

private ContextType ContextType { get; set; }
private string OU { get; set; }
private string DomainOrMachineName { get; set; }
private string UserDName { get; set; }
private string Password { get; set; }
 
private PrincipalContext CreateContext()
{
  if (this.UserDName != null && this.Password != null)
  {
    return new PrincipalContext(this.ContextType, this.DomainOrMachineName, this.OU, ContextOptions.SimpleBind, this.UserDName, this.Password);
  }
  else
  {
    return new PrincipalContext(this.ContextType, this.DomainOrMachineName, this.OU);
  }
}

ContextType is an enum of Machine, Domain or ApplicationDirectory.  For a simple domain AD you want Domain.  ApplicationDirectory is for AD LDS (what was once called ADAM).

The OU is the root OU you want to work with.

UserDName is a user account’s distinguished name, eg: CN=Administrator,DC=domain,DC=com.

The helper method will use the UserDName and Password if specified, otherwise it will do a normal binding – using Negotiated credentials if available.  If you are talking to a remote AD not in your local domain then you will probably need to use the SimpleBind method – I found this the easiest.

 

To get a list of groups from a domain via this context is now trivial:

using (var pc = this.CreateContext())
{
    var gp = new GroupPrincipal(pc, "*");
    var ps = new PrincipalSearcher(gp);

    foreach (GroupPrincipal g in ps.FindAll())
    {
        Console.WriteLine(g.Name);
    }
}

 

This little snippet will output the names of the groups under the specified OU.  However, it will only list the top level groups – not groups within groups.  To do that, we need to get recursive.

using (var pc = this.CreateContext())
{
    var gp = new GroupPrincipal(pc, "*");
    var ps = new PrincipalSearcher(gp);
 
    foreach (GroupPrincipal g in ps.FindAll())
    {
        Console.WriteLine(g.Name);
        listNestedGroups(g, "-");
 
    }
}
 
void listNestedGroups(GroupPrincipal gp, string indent) 
{
   foreach(var p in gp.members)
   {
      if(p is GroupPrincipal) //  could also be a user
     {
          var g = p as GroupPrincipal;
          Console.WriteLine(indent + g.Name);
          listNestedGroups(g, indent + "-");
      }
   }
}

 

You can use the same objects to create and manipulate LDAP entities similarly easily.

Like all good things though, there’s a couple of issues.  When dealing with users, I needed the middle name (Initials in AD).  The UserPrincipal includes a property for this, but it comes through empty.  Luckily, it’s easy enough to fall back to the old way of getting the extra properties.  The GetUnderlyingObject() method returns the DirectoryEntry instance:

var de = userPrincipal.GetUnderlyingObject() as DirectoryEntry;
 
if (de != null)
{
  var lastChangeDates = de.Properties["whenChanged"];
  if (lastChangeDates != null && lastChangeDates.Count == 1)
  {
    var lastChangeDate = (DateTime)lastChangeDates[0];
    var initials = u.MiddleName;
    if (initials == null)
    {
      var initialProps = de.Properties["initials"];
      if (initialProps != null && initialProps.Count == 1)
      {
        initials = (string)initialProps[0];
      }
    }
  }
}

There’s a bunch of documentation on MSDN for this but I also found some code snippets on StackOverflow.

Another useful tip, get the SysInternals AD Explorer.  It shows you a lot more information that the User and Group tool in AD.

Enjoy!

Friday, August 26, 2011 9:02:33 AM (New Zealand Standard Time, UTC+12:00)  #    Comments [1]   General  | 
Monday, October 24, 2011 7:48:03 PM (New Zealand Daylight Time, UTC+13:00)
nice post
Comments are closed.
Copyright © 2013 Peter G Jones. All rights reserved.
DasBlog 'Portal' theme by Johnny Hughes.
Pick a theme: