Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / Win32

Using System.DirectoryServices.AccountManagement

4.88/5 (27 votes)
21 Jul 2009CPOL4 min read 303.8K   8.7K  
In this article, we will be focusing on creating, editing, and deleting both user accounts or groups on the Active Directory or Machine SAM by using the System.DirectoryServices.AccountManagement namespace that .NET Framework 3.5 includes.

Introduction

In this article, we will be focusing on creating, editing, and deleting both user accounts or groups on Active Directory or Machine SAM by using the System.DirectoryServices.AccountManagement namespace that .NET Framework 3.5 includes. We were able to develop these functionalities by using the Invoke method of the DirectoryEntry object which is returned by the DirectorySearcher component in .NET Framework 2.0, but this new namespace works faster and is easy to develop. It also is in a more object oriented structure.

Before starting, let's just have a sneak peek at the objects we will be using during this article.

PrincipalContextPrincipalContext is the object that provides directory services such as AD or Machine SAM. In other words, it holds the connection to a directory.
PrincipalPrincipal is the base class of principal objects that we will be describing below. It includes common methods such as Save, Delete.
GroupPrincipalAs can be understood from the name, the GroupPrincipal object provides functionality for Group objects at directory level and is derived from the Principal object.
AuthenticablePricipalThis class is also derived from the Principal class, and besides that, it includes methods for authentication mechanisms.
UserPricipalUserPrincipal is the object that represents users on both the Active Directory or the Local SAM, it provides functionality for user items.

Using the code

I believe that the best learning method in programming is writing code. So, let's start writing our code. Instead of pasting all the code here, I will give some examples of critical points, and try to explain the idea of working with these classes.

For further information, you can download the source from the link above. Here are some functionalities this program includes.

  • Groups: Listing, Filtering, Creating, Editing, Deleting, Viewing users of a group.
  • Users: Listing, Filtering, Creating, Editing, Deleting, Changing password, Viewing user groups, Adding user to a group, Removing user from a group.

Let's start with creating an instance of a PrincipalContext object.

C#
PrincipalContext insPrincipalContext = 
  new PrincipalContext(ContextType.Machine);//Connecting to local computer.
PrincipalContext insPrincipalContext = new PrincipalContext(ContextType.Domain, "MyDomain", 
                                       "DC=MyDomain,DC=com");
                                       //Connecting to Active Directory
PrincipalContext insPrincipalContext = new PrincipalContext(ContextType.Machine,"TAMERO", 
                                       "administrator","password");
                                       //Connecting to local computer 
                                       //with credentials of an user

As you see, there is no difference between connecting to a Machine SAM and Active Directory. I will continue with my article giving examples from Machine SAM, but as I said, there is no difference. Also, with this object, you can validate a user's credentials. I will provide an example of using this method at the end of the article.

Now, let's look at how to list or filter users or groups.

C#
private void SearchGroups(GroupPrincipal parGroupPrincipal)
{
    lbGroups.Items.Clear();
    PrincipalSearcher insPrincipalSearcher = new PrincipalSearcher();
    insPrincipalSearcher.QueryFilter = parGroupPrincipal;
    PrincipalSearchResult<principal> results = insPrincipalSearcher.FindAll();
    foreach (Principal p in results)
    {
        lbGroups.Items.Add(p);
    }
}
private void SearchUsers(UserPrincipal parUserPrincipal)
{
    lbUsers.Items.Clear();
    PrincipalSearcher insPrincipalSearcher = new PrincipalSearcher();
    insPrincipalSearcher.QueryFilter = parUserPrincipal;
    PrincipalSearchResult<principal> results = insPrincipalSearcher.FindAll();
    foreach (Principal p in results)
    {
        lbUsers.Items.Add(p);
    }
}
private void ListGroups()
{
    GroupPrincipal insGroupPrincipal = new GroupPrincipal(insPrincipalContext);
    insGroupPrincipal.Name = "*";
    SearchGroups(insGroupPrincipal);
}
private void ListUsers()
{
    UserPrincipal insUserPrincipal = new UserPrincipal(insPrincipalContext);
    insUserPrincipal.Name = "*";
    SearchUsers(insUserPrincipal);
}

Now, let's look at the logic behind listing and filtering users or groups. You can easily see that both the SearchGroups and SearchUsers methods are similar. The most important point is using GroupPrincipal for Group, and UserPrincipal for User functions. And, principal searcher is the common class that provides the search functionality. After assigning an object (GroupPrincipal or UserPrincipal) that contains filters to the QueryFilter property of this object, the FindAll method returns all the records, and the FindOne method returns the first record. And, another point is how to set search criteria, for example, for a property that contains a "b" character, you can assign "*b*" to that property, or for a property that starts with a "b" character, you can assign "b*" to that property. I will also tell how to set filters that depend on comparison (e.g., PasswordExpirationDate is in the next 5 days) at the end of this article.

Now, let's look at the code that creates a new user:

C#
UserPrincipal insUserPrincipal = new UserPrincipal(insPrincipalContext);

//method that assign properties to insUserPrincipal object
insUserPrincipal.Save();
insUserPrincipal.Dispose();
MessageBox.Show("User created.");
GroupPrincipal insGroupPrincipal = new GroupPrincipal(insPrincipalContext);

//method that assign properties to insGroupPrincipal object
insGroupPrincipal.Save();
insGroupPrincipal.Dispose();
MessageBox.Show("Group created.");

It is very easy to create an entry. As you can see, after creating a principal object (User or Group), invoking its Save method saves the record. Editing and deleting works the same. Instead of creating a Principal object, if you get a Principal object reference, then change the properties and invoke the Save method, and your object is updated. For deleting an entry, the only thing you should do is call the Delete method of the Principal object that references to the entry.

And now, let's talk about changing the password of a user:

C#
UserPrincipal insUserPrincipal = (UserPrincipal)lbUsers.SelectedItem;
insUserPrincipal.SetPassword("12345678");
MessageBox.Show("Password changed.");

You can get the members of a group from the Members property of the GroupPrincipal object. You can use this property as an enumeration. To add a user to a group, you should add the UserPrincipal object that holds the reference of a user to this enumeration. And the same logic for removing a user from a group: you must remove that user from the enumeration. But, do not forget to execute the Save method of GroupPrincipal after making changes. You can list the user groups by executing the method GetGroups() of the UserPrincipal object. Here are some examples:

C#
//Listing groups members
GroupPrincipal insGroupPrincipal = (GroupPrincipal)lbGroups.SelectedItem;
List<principal> insListPrincipal = new List<principal>();
foreach (Principal p in insGroupPrincipal.Members)
{
    insListPrincipal.Add(p);
}

//Listing users groups
UserPrincipal insUserPrincipal = (UserPrincipal)lbUsers.SelectedItem;
List<principal> insListPrincipal = new List<principal>();
foreach (Principal p in insUserPrincipal.GetGroups())
{
    insListPrincipal.Add(p);
}

//Adding user to a group
UserPrincipal insUserPrincipal = (UserPrincipal)lbUsers.SelectedItem;
GroupPrincipal groupPrincipal = 
  GroupPrincipal.FindByIdentity(insPrincipalContext, group.GroupName);
if (groupPrincipal.Members.Contains(insPrincipalContext, 
    IdentityType.SamAccountName, insUserPrincipal.SamAccountName))
{
    MessageBox.Show(insUserPrincipal.Name + 
      " is already a member of group " + group.GroupName);
    return;
}
groupPrincipal.Members.Add(insUserPrincipal);
groupPrincipal.Save();

//Removing user from a group
UserPrincipal insUserPrincipal = (UserPrincipal)lbUsers.SelectedItem;
GroupPrincipal groupPrincipal = 
  GroupPrincipal.FindByIdentity(insPrincipalContext, group.GroupName);
groupPrincipal.Members.Remove(insUserPrincipal);
groupPrincipal.Save();

And, here is the code for validating user credentials and filtering comparison values:

C#
insPrincipalContext.ValidateCredentials("Tamer", "12345678");
parUserPrincipal.AdvancedSearchFilter.AccountExpirationDate(
                 DateTime.Now.Date.AddDays(10), MatchType.Equals);
parUserPrincipal.AdvancedSearchFilter.BadLogonCount(5, MatchType/span>.GreaterThanOrEquals);

For bug reports and suggestions, feel free to contact me at oztamer@hotmail.com.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)