Creating Users that Work with System.DirectoryServices.AccountManagement

| | Comments (4) | TrackBacks (0)

My friend and former coworker, Cassiano Durand, went to Code Camp Portland last month and told me about a session he attended where the speaker, Steve Evans, mentioned a new directory-related API that was introduced in .NET 3.5. After reviewing the slides and code from the presentation, my colleague and I started using this new API this week. We got snared by a serious gotcha that cost us our coffee breaks.

   

I'll cut to the chase: If you create a user with ADSI Edit, you have to set the object's msDS-UserAccountDisabled property to false and userPrincipalName to match the name of the new account. If you do not do this, the ValidateCredentials method on PrincipalContext objects will always inexplicably return false. To see what I mean, perform the following steps and run the code below.

 

  1. Start by opening ADSI Edit and connecting to an AD LDS instance using settings such as these:

       

       

  2. Once connected, drill down into the OU where you want to create the user objects.

       

       

  3. Create a new object of type user and specify a name.

       

       

       

  4. After finishing the wizard, reset the new user's password via the Reset Password option in the object's context menu.

       

    OK. This is the important bit. Stop skimming and read the following!

       

  5. Open the user object's Properties dialog box.
  6. In this windows, scroll down to msDS-UserAccountDisabled and double click it. In the Boolean Attribute Editor, set the value to False and click OK.

       

       

  7. Scroll down to userPrincipalName and double click it. In the resulting String Attribute Editor dialog box, type the name that you used in step 3 above.

       

       

If you don't perform the last two steps, the new .NET 3.5 System.DirectoryServices.AccountManagement API will not work. Every time you call the ValidateCredentials method on a PrincipalContext object, it will return false. This method seems to check against the User Principal Name (UPN) rather than the name entered when creating the account in ADSI Edit. To see this in action, you can create a simple console application with the following code:

static void Main()

{

    var host = "localhost";

    var port = 50000;

    var userName = "test1";

    var password = "Password1";

    var machineName = string.Format("{0}:{1}", host, port);

    var container = "OU=Users,C=Tenant";

 

    var context = new PrincipalContext(ContextType.ApplicationDirectory, machineName, container);

 

    var authenticated = context.ValidateCredentials(userName, password, ContextOptions.SimpleBind);

 

    if (!authenticated)

    {

        authenticated = context.ValidateCredentials(userName, password, ContextOptions.Negotiate);

 

        if (!authenticated)

            throw new AuthenticationException();

    }

}

Try running it with a user that hasn't had these properties set, an account that has just one of them set, and one that has both the msDS-UserAccountDisabled and userPrincipalName properties updated.  You'll see that creating a user account by simply following the steps on the GUI's wizard will result in an inoperable user account.

If only we had the time back that we wasted figuring this out.  We all could have enjoyed a nice coffee.  As it was, however, we had to work right through our breaks.  Very lame :-(