Full Version: Working with the Active Directory in VB.Net
Dream.In.Code > Programming Tutorials > VB.NET Tutorials
PsychoCoder
This tutorial discusses working within the Active Directory (AD) using VB.Net, how to query the AD, query groups, members, adding users, suspending users, and changing users passwords. The Active Directory is the Windows directory service that provides a unified view of the entire network. Working with the Active Directory is a lot like working with a database, you write queries based on the information you want to retrieve.

There are 3 interfaces for accessing the Active Directory:

1) LDAP: The Lightweight Directory Access Protocol (LDAP) is the service protocol that runs on a layer above the TCP/IP layer (or stack) and provides an interface for accessing, searching and modifying Internet Directories, and is based on a client-server model.

2) ADSI: The Active Directory Services Interface (ADSI) is a set of COM components (or Interfaces) designed to access the directory services from different network providers in a network setup, it is designed to provide a single, central interface for accessing & managing network resources.

3) System.DirectoryServices: The System.DirectoryServices Namespace is built into the .Net Framework designed to provide programming access to LDAP directories (Active Directory) and is built on the ASDI API.

Before the release of VB.Net (back in the VB6 and prior years) you almost had to take a "brute force" approach when it came to working with the Active Directory, but with the System.DirectoryServices Namespace in .Net 2.0 this got much easier. In this tutorial I will show various functions that can be performed within the Active Directory, but remember, other than simply searching the Active Directory, a user has to have Administrative permissions to modify the Active Directory.

Before we get into the "cool" stuff, there are a couple of function I would like to offer, these are used later in the tutorial and come in quite handy. The first one is used for extracting the domain off of the username. Active Directory usernames are in the format "YOURDOMAIN\UserName", but when searching the directory you don't want the "DOMAINNAME\" on the username, so I wrote this ExtractUserName function:

CODE

''' <summary>
''' Function to extract just the login from the provided string (given in the format YOURDOMAIN\Username)
''' </summary>
''' <param name="path">Full AD login of the associate</param>
''' <returns>The login with the "YOURDOMAIN\" stripped</returns>
''' <remarks></remarks>
Public Shared Function ExtractUserName(ByVal path As String) As String
   'Split on the "\"
   Dim userPath As String() = path.Split(New Char() {"\"c})
   'Return the rest (username part)
   Return userPath((userPath.Length - 1))
End Function


The next function (SetADProperty) comes in handy when either modifying someone's account, creating a new user account or many other possible manipulations. This function takes your DirectoryEntry (GetDirectoryEntry function below), the property name and the property value and sets them for you:

CODE

''' <summary>
''' Helper method that sets properties for AD users.
''' </summary>
''' <param name="de">DirectoryEntry to use</param>
''' <param name="pName">Property name to set</param>
''' <param name="pValue">Value of property to set</param>
Public Shared Sub SetADProperty(ByVal de As DirectoryEntry, ByVal pName As String, ByVal pValue As String)
    'First make sure the property value isnt "nothing"
    If Not pValue Is Nothing Then
        'Check to see if the DirectoryEntry contains this property already
        If de.Properties.Contains(pName) Then   'The DE contains this property
            'Update the properties value
            de.Properties(pName)(0) = pValue
        Else    'Property doesnt exist
            'Add the property and set it's value
            de.Properties(pName).Add(pValue)
        End If
    End If
End Sub


The first thing we want to look at is actually connecting to the Active Directory, for this we need to get an Active Directory Entry. Though there are several ways to accomplish this, I prefer to use this function:

CODE

''' <summary>
''' Method used to create an entry to the AD using a secure connection.
''' Replace the path.
''' </summary>
''' <returns>DirectoryEntry</returns>
Public Shared Function GetDirectoryEntry() As DirectoryEntry
   'Of course change the information for the LDAP to your network
    Dim dirEntry As New DirectoryEntry("LDAP://192.168.1.1/CN=Users;DC=Yourdomain")
    'Setting username & password to Nothing forces
    'the connection to use your logon credentials
    dirEntry.Username = Nothing
    dirEntry.Password = Nothing
   'Always use a secure connection
    dirEntry.AuthenticationType = AuthenticationTypes.Secure
    Return dirEntry
End Function


Next, lets look at verifying that an Active Directory login is a valid login. Like I said earlier, querying the Active Directory is a lot like working with a database, there are certain "fields" you want to look for and read, then compare them with your values. Some of these values are:

SAMAccountName: This is the actual login
givenName: This is the users first name
sn: This is the users Sir Name (Last name)

Granted there are many more "fields" but these are the 3 I am worried about right now. For my valid login function I took it a step further than just simply verifying it's a valid login, I wanted to know if it's a valid login for the user specified (i.e.; First & Last name). This function looks like this:

CODE

''' <summary>
''' Function to search the Active Directory and ensure the Login provided in Agent Process is a valid one. The search is performed
''' to see if the login provided exists for the first and last name of the associate being added
''' </summary>
''' <param name="loginName">Login of the associate to search for</param>
''' <param name="givenName">First name fo the associate being added</param>
''' <param name="surName">Last name of the associate being added</param>
''' <returns>True or False depending if the login provided is a valid one</returns>
Public Function IsValidADLogin(ByVal loginName As String, ByVal givenName As String, ByVal surName As String) As Boolean
    Try
        'Create a DirectorySearcher Object (used for searching the AD)
        Dim search As New DirectorySearcher()
        'Set the filter on the searcher object to look for the SAMAccountName, givenName
        ' and the sn (Sur Name)
        search.Filter = String.Format("(&(SAMAccountName={0})(givenName={1})(sn={2}))", ExtractUserName(loginName), givenName, surName)
        'Now load these properties to the search
        search.PropertiesToLoad.Add("cn")
        search.PropertiesToLoad.Add("SAMAccountName")   'Users login name
        search.PropertiesToLoad.Add("givenName")    'Users first name
        search.PropertiesToLoad.Add("sn")   'Users last name
        'Use the .FindOne() Method to stop as soon as a match is found
        Dim result As SearchResult = search.FindOne()
        'Now check to see if a result was found
        If result Is Nothing Then
            'Login isn't valid
            Return False
        Else
            'Valid login
            Return True
        End If
    Catch ex As Exception
        MessageBox.Show(ex.Message, "Active Directory Error", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1)
    End Try
End Function


Now what if you needed to disable a users account, I'm sure there are many reasons for needing this so here is what you would do. You would have to get a directory entry (GetDirectoryEntry from above), then create a directory search object to search with. You would then need to set the search filter for the search, in the last function I gave you 3 "fields" in the Active Directory, now I'm giving you 2 more:

objectCategory: What category is the object in (Person, Computer, etc)
objectClass: What class is the object (in our case it is a user)
SAMAccount: (This one was defined earlier)

This function not only disables the users account, but it also hides the users email from all Exchange Address Lists, it looks like this:

CODE

''' <summary>
''' Method that disables a user account in the AD
''' and hides user's email from Exchange address lists.
''' </summary>
''' <param name="sLogin">Login of the user to disable</param>
Public Sub DisableAccount(ByVal sLogin As String)
    '   1. Search the Active Directory for the desired user
    Dim dirEntry As DirectoryEntry = GetDirectoryEntry()
    Dim dirSearcher As DirectorySearcher = New DirectorySearcher(dirEntry)
    dirSearcher.Filter = "(&(objectCategory=Person)(objectClass=user) _
(SAMAccountName=" & sLogin & "))"
    dirSearcher.SearchScope = SearchScope.Subtree
    Dim results As SearchResult = dirSearcher.FindOne()
    '   2. Check returned results
    If Not results Is Nothing Then
        '   2a. User was returned
        Dim dirEntryResults As DirectoryEntry = GetDirectoryEntry(results.Path)
        Dim iVal As Integer = CInt(dirEntryResults.Properties("userAccountControl").Value)
        '   3. Disable the users account
        dirEntryResults.Properties("userAccountControl").Value = iVal Or &H2
        '   4. Hide users email from all Exchange Mailing Lists
        dirEntryResults.Properties("msExchHideFromAddressLists").Value = "TRUE"
        dirEntryResults.CommitChanges()
        dirEntryResults.Close()
    End If
    dirEntry.Close()
End Sub


The next section is to show how to update/modify a users Active Directory account. The items being updated in this function can be changed to whatever fields you have in your Active Directory and what needs to be updated for a specific user. For arguments sake we're going to update a users:

Department
Title
Phone Extension

To accomplish this first we need to get a directory entry (GetDirectoryEntry from above), then create a search object and set the filter for the search object (for this we will use the same 3 "fields" we did for disabling a users account). This is where the similarities end, with this we have to create a 2nd Directory Entry only this time we pass the search results to it. We then use the SetADProperty function from the beginning of the tutorial to set the properties we want to update. Once we have done that then we commit the changes then close and clean up behind ourselves. Keep in mind to make this function work you need Administrative permissions on the network:

CODE

''' <summary>
''' Method that updates user's properties
''' </summary>
''' <param name="userLogin">Login of the user to update</param>
''' <param name="userDepartment">New department of the specified user</param>
''' <param name="userTitle">New title of the specified user</param>
''' <param name="userPhoneExt">New phone extension of the specified user</param>
Public Sub UpdateUserADAccount(ByVal userLogin As String, _
ByVal userDepartment As String, ByVal userTitle As String, ByVal userPhoneExt As String)
    Dim dirEntry As DirectoryEntry = GetDirectoryEntry()
    Dim dirSearcher As DirectorySearcher = New DirectorySearcher(dirEntry)
    '   1. Search the Active Directory for the speied user
    dirSearcher.Filter = "(&(objectCategory=Person)(objectClass=user) _
(SAMAccountName=" & userLogin & "))"
    dirSearcher.SearchScope = SearchScope.Subtree
    Dim searchResults As SearchResult = dirSearcher.FindOne()
    If Not searchResults Is Nothing Then
        Dim dirEntryResults As New DirectoryEntry(results.Path)
        'The properties listed here may be different then the properties in your Active Directory
        'so they may need to be changed according to your network
        '   2. Set the new property values for the specified user
        SetADProperty(dirEntryResults, "department", userDepartment)
        SetADProperty(dirEntryResults, "title", userTitle)
        SetADProperty(dirEntryResults, "phone", userPhoneExt)
        '   3. Commit the changes
        dirEntryResults.CommitChanges()
        '   4. Close & Cleanup
        dirEntryResults.Close()
    End If
    '   4a. Close & Cleanup
    dirEntry.Close()
End Sub


Now what if you needed to provide a list of all the computer names that are on the Active Directory. Well once you've learned what you've learned so far about using the System.DirectoryServices Namespace this is much simpler. When I created this function I decided to return a collection populated with the computer names, you could return a generic list, a dataset, or anything else. With this function you, once again (see a theme starting here?) use the GetDirectoryEntry function, you create a search object then set the filter. With this function we use the objectClass field, but instead of a Person the class is computer. This is the function:

CODE

''' <summary>
''' Function to query the Active Directory and return all the computer names
'''on the network
''' </summary>
''' <returns>A collection populated with all the computer names</returns>
Public Shared Function ListAllADComputers() As Collection
    Dim dirEntry As DirectoryEntry = GetDirectoryEntry()
    Dim pcList As New Collection()
    '   1. Search the Active Directory for all objects with type of computer
    Dim dirSearcher As DirectorySearcher = New DirectorySearcher(dirEntry)
    dirSearcher.Filter = ("(objectClass=computer)")
    '   2. Check the search results
    Dim dirSearchResults As SearchResult
    '   3. Loop through all the computer names returned
    For Each dirSearchResults In dirSearcher.FindAll()
        '   4. Check to ensure the computer name isnt already listed in the collection
        If Not pcList.Contains(dirSearchResults.GetDirectoryEntry().Name.ToString()) Then
            '   5. Add the computer name to the collection (since it dont already exist)
            pcList.Add(dirSearchResults.GetDirectoryEntry().Name.ToString())
        End If
    Next
    '   6. Return the results
    Return pcList
End Function


With this next function you can populate a collection (you can alter it to populate a Generic List, DataSet, etc) with the names of all the groups a user is a member of (i.e.; Administrators, Power Users, Guest, etc). With this you create your Directory Object (just not the GetDirectoryEntry function) passing it the path (binding path to the AD), the username & password of the user you want to query. So far we have seen SAMAaacount, givenName, sn, objectClass, and objectCategory. Now we will see the memberOf Property, this is the property telling what group the user is a member of.

Once you have the list of all the groups, then you loop through the list and add them to the collection (or whatever object you choose to use). It looks like this:

CODE

''' <summary>
''' Function to return all the groups the user is a member od
''' </summary>
''' <param name="_path">Path to bind to the AD</param>
''' <param name="username">Username of the user</param>
''' <param name="password">password of the user</param>
Private Function GetGroups(ByVal _path As String, ByVal username As String, _
ByVal password As String) As Collection
    Dim Groups As New Collection
    Dim dirEntry As New System.DirectoryServices.DirectoryEntry(_path, username, password)
    Dim dirSearcher As New DirectorySearcher(dirEntry)
    dirSearcher.Filter = String.Format("(sAMAccountName={0}))", username)
    dirSearcher.PropertiesToLoad.Add("memberOf")
    Dim propCount As Integer
    Try
        Dim dirSearchResults As SearchResult = dirSearcher.FindOne()
        propCount = dirSearchResults.Properties("memberOf").Count
        Dim dn As String
        Dim equalsIndex As String
        Dim commaIndex As String
        For i As Integer = 0 To propCount - 1
            dn = dirSearchResults.Properties("memberOf")(i)
            equalsIndex = dn.IndexOf("=", 1)
            commaIndex = dn.IndexOf(",", 1)
            If equalsIndex = -1 Then
                Return Nothing
            End If
            If Not Groups.Contains(dn.Substring((equalsIndex + 1), _
(commaIndex - equalsIndex) - 1)) Then
                Groups.Add(dn.Substring((equalsIndex + 1), (commaIndex - equalsIndex) - 1))
            End If
        Next
    Catch ex As Exception
        If ex.GetType Is GetType(System.NullReferenceException) Then
            MessageBox.Show("Selected user isn't a member of any groups at this time.", "No groups listed", MessageBoxButtons.OK, MessageBoxIcon.Error)
            'they are still a good user just does not
            'have a "memberOf" attribute so it errors out.
            'code to do something else here if you want
        Else
            MessageBox.Show(ex.Message.ToString, "Search Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
        End If
    End Try
    Return Groups
End Function


Now we will look into how to determine if a users account has been disabled. This function returns a Boolean (True/False) based on what flag is returned from the query. This function requires an Enumeration of all possible flags for a users account, this is the Enumeration:

CODE

Public Enum ADAccountOptions
   UF_TEMP_DUPLICATE_ACCOUNT = 256
   UF_NORMAL_ACCOUNT = 512
   UF_INTERDOMAIN_TRUST_ACCOUNT = 2048
   UF_WORKSTATION_TRUST_ACCOUNT = 4096
   UF_SERVER_TRUST_ACCOUNT = 8192
   UF_DONT_EXPIRE_PASSWD = 65536
   UF_SCRIPT = 1
   UF_ACCOUNTDISABLE = 2
   UF_HOMEDIR_REQUIRED = 8
   UF_LOCKOUT = 16
   UF_PASSWD_NOTREQD = 32
   UF_PASSWD_CANT_CHANGE = 64
   UF_ACCOUNT_LOCKOUT = 16
   UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED = 128
End Enum


Now for the function that checks for the value of 2 (meaning disabled):
CODE

''' <summary>
''' This will perform a logical operation on the userAccountControl values
''' to see if the user account is enabled or disabled.  The flag for determining if the
''' account is active is a bitwise value (decimal =2)
''' </summary>
''' <param name="userAccountControl"></param>
''' <returns></returns>
Public Shared Function IsAccountActive(ByVal userAccountControl As Integer) As Boolean
    Dim accountDisabled As Integer = Convert.ToInt32(ADAccountOptions.UF_ACCOUNTDISABLE)
    Dim flagExists As Integer = userAccountControl And accountDisabled
    'if a match is found, then the disabled flag exists within the control flags
    If flagExists > 0 Then
        Return False
    Else
        Return True
    End If
End Function


I will leave you with one last "tidbit". This function will allow you to add a user to a specific security group. First you create your search object, then you set your filter, this time we will use the objectClass and set it to group (since that is what we're looking for). Then we need to check to see if this user is already a member of the specified group, we do this by looping through all the members in the group (implementing IEnumerable), then checking all names against the user we want to add. Here is the function:

CODE

'' <summary>
''' Method to add a user to a group
''' </summary>
''' <param name="de">DirectoryEntry to use</param>
''' <param name="deUser">User DirectoryEntry to use</param>
''' <param name="GroupName">Group Name to add user to</param>
Public Shared Sub AddUserToGroup(ByVal de As DirectoryEntry, ByVal deUser As DirectoryEntry, ByVal GroupName As String)
    Dim deSearch As DirectorySearcher = New DirectorySearcher()
    deSearch.SearchRoot = de
    deSearch.Filter = "(&(objectClass=group) (cn=" & GroupName & "))"
    Dim results As SearchResultCollection = deSearch.FindAll()
    Dim isGroupMember As Boolean = False
    If results.Count > 0 Then
        Dim group As New DirectoryEntry(results(0).Path)
        Dim members As Object = group.Invoke("Members", Nothing)
        For Each member As Object In CType(members, IEnumerable)
            Dim x As DirectoryEntry = New DirectoryEntry(member)
            Dim name As String = x.Name
            If name <> deUser.Name Then
                isGroupMember = False
            Else
                isGroupMember = True
                Exit For
            End If
        Next member
        If (Not isGroupMember) Then
            group.Invoke("Add", New Object() {deUser.Path.ToString()})
        End If
        group.Close()
    End If
    Return
End Sub


This concludes this tutorial on Active Directory in VB.Net. Remember, this is a semi high level overview of the Active Directory, there are so many things you can do it would take ages for me to list them (and I'm sure DIC++ wouldn't appreciate me using all their HDD space), but this should at least give you a much better understanding of how the System.DirectoryServices Namespace works and what can be done with it. Remember, for anything other than simple searching of the Active Directory Administrative permissions are required. If you have any questions, about this tutorial or some other help with AD feel free to leave me a message here and I will respond as soon as I can. Thanks for reading.
kingpin2005
QUOTE(PsychoCoder @ 26 Jul, 2007 - 09:41 PM) *

This tutorial discusses working within the Active Directory (AD) using VB.Net, how to query the AD, query groups, members, adding users, suspending users, and changing users passwords. The Active Directory is the Windows directory service that provides a unified view of the entire network. Working with the Active Directory is a lot like working with a database, you write queries based on the information you want to retrieve.

There are 3 interfaces for accessing the Active Directory:

1) LDAP: The Lightweight Directory Access Protocol (LDAP) is the service protocol that runs on a layer above the TCP/IP layer (or stack) and provides an interface for accessing, searching and modifying Internet Directories, and is based on a client-server model.

2) ADSI: The Active Directory Services Interface (ADSI) is a set of COM components (or Interfaces) designed to access the directory services from different network providers in a network setup, it is designed to provide a single, central interface for accessing & managing network resources.

3) System.DirectoryServices: The System.DirectoryServices Namespace is built into the .Net Framework designed to provide programming access to LDAP directories (Active Directory) and is built on the ASDI API.

Before the release of VB.Net (back in the VB6 and prior years) you almost had to take a "brute force" approach when it came to working with the Active Directory, but with the System.DirectoryServices Namespace in .Net 2.0 this got much easier. In this tutorial I will show various functions that can be performed within the Active Directory, but remember, other than simply searching the Active Directory, a user has to have Administrative permissions to modify the Active Directory.

Before we get into the "cool" stuff, there are a couple of function I would like to offer, these are used later in the tutorial and come in quite handy. The first one is used for extracting the domain off of the username. Active Directory usernames are in the format "YOURDOMAIN\UserName", but when searching the directory you don't want the "DOMAINNAME\" on the username, so I wrote this ExtractUserName function:

CODE

''' <summary>
''' Function to extract just the login from the provided string (given in the format YOURDOMAIN\Username)
''' </summary>
''' <param name="path">Full AD login of the associate</param>
''' <returns>The login with the "YOURDOMAIN\" stripped</returns>
''' <remarks></remarks>
Public Shared Function ExtractUserName(ByVal path As String) As String
   'Split on the "\"
   Dim userPath As String() = path.Split(New Char() {"\"c})
   'Return the rest (username part)
   Return userPath((userPath.Length - 1))
End Function


The next function (SetADProperty) comes in handy when either modifying someone's account, creating a new user account or many other possible manipulations. This function takes your DirectoryEntry (GetDirectoryEntry function below), the property name and the property value and sets them for you:

CODE

''' <summary>
''' Helper method that sets properties for AD users.
''' </summary>
''' <param name="de">DirectoryEntry to use</param>
''' <param name="pName">Property name to set</param>
''' <param name="pValue">Value of property to set</param>
Public Shared Sub SetADProperty(ByVal de As DirectoryEntry, ByVal pName As String, ByVal pValue As String)
    'First make sure the property value isnt "nothing"
    If Not pValue Is Nothing Then
        'Check to see if the DirectoryEntry contains this property already
        If de.Properties.Contains(pName) Then   'The DE contains this property
            'Update the properties value
            de.Properties(pName)(0) = pValue
        Else    'Property doesnt exist
            'Add the property and set it's value
            de.Properties(pName).Add(pValue)
        End If
    End If
End Sub


The first thing we want to look at is actually connecting to the Active Directory, for this we need to get an Active Directory Entry. Though there are several ways to accomplish this, I prefer to use this function:

CODE

''' <summary>
''' Method used to create an entry to the AD using a secure connection.
''' Replace the path.
''' </summary>
''' <returns>DirectoryEntry</returns>
Public Shared Function GetDirectoryEntry() As DirectoryEntry
   'Of course change the information for the LDAP to your network
    Dim dirEntry As New DirectoryEntry("LDAP://192.168.1.1/CN=Users;DC=Yourdomain")
    'Setting username & password to Nothing forces
    'the connection to use your logon credentials
    dirEntry.Username = Nothing
    dirEntry.Password = Nothing
   'Always use a secure connection
    dirEntry.AuthenticationType = AuthenticationTypes.Secure
    Return dirEntry
End Function


Next, lets look at verifying that an Active Directory login is a valid login. Like I said earlier, querying the Active Directory is a lot like working with a database, there are certain "fields" you want to look for and read, then compare them with your values. Some of these values are:

SAMAccountName: This is the actual login
givenName: This is the users first name
sn: This is the users Sir Name (Last name)

Granted there are many more "fields" but these are the 3 I am worried about right now. For my valid login function I took it a step further than just simply verifying it's a valid login, I wanted to know if it's a valid login for the user specified (i.e.; First & Last name). This function looks like this:

CODE

''' <summary>
''' Function to search the Active Directory and ensure the Login provided in Agent Process is a valid one. The search is performed
''' to see if the login provided exists for the first and last name of the associate being added
''' </summary>
''' <param name="loginName">Login of the associate to search for</param>
''' <param name="givenName">First name fo the associate being added</param>
''' <param name="surName">Last name of the associate being added</param>
''' <returns>True or False depending if the login provided is a valid one</returns>
Public Function IsValidADLogin(ByVal loginName As String, ByVal givenName As String, ByVal surName As String) As Boolean
    Try
        'Create a DirectorySearcher Object (used for searching the AD)
        Dim search As New DirectorySearcher()
        'Set the filter on the searcher object to look for the SAMAccountName, givenName
        ' and the sn (Sur Name)
        search.Filter = String.Format("(&(SAMAccountName={0})(givenName={1})(sn={2}))", ExtractUserName(loginName), givenName, surName)
        'Now load these properties to the search
        search.PropertiesToLoad.Add("cn")
        search.PropertiesToLoad.Add("SAMAccountName")   'Users login name
        search.PropertiesToLoad.Add("givenName")    'Users first name
        search.PropertiesToLoad.Add("sn")   'Users last name
        'Use the .FindOne() Method to stop as soon as a match is found
        Dim result As SearchResult = search.FindOne()
        'Now check to see if a result was found
        If result Is Nothing Then
            'Login isn't valid
            Return False
        Else
            'Valid login
            Return True
        End If
    Catch ex As Exception
        MessageBox.Show(ex.Message, "Active Directory Error", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1)
    End Try
End Function


Now what if you needed to disable a users account, I'm sure there are many reasons for needing this so here is what you would do. You would have to get a directory entry (GetDirectoryEntry from above), then create a directory search object to search with. You would then need to set the search filter for the search, in the last function I gave you 3 "fields" in the Active Directory, now I'm giving you 2 more:

objectCategory: What category is the object in (Person, Computer, etc)
objectClass: What class is the object (in our case it is a user)
SAMAccount: (This one was defined earlier)

This function not only disables the users account, but it also hides the users email from all Exchange Address Lists, it looks like this:

CODE

''' <summary>
''' Method that disables a user account in the AD
''' and hides user's email from Exchange address lists.
''' </summary>
''' <param name="sLogin">Login of the user to disable</param>
Public Sub DisableAccount(ByVal sLogin As String)
    '   1. Search the Active Directory for the desired user
    Dim dirEntry As DirectoryEntry = GetDirectoryEntry()
    Dim dirSearcher As DirectorySearcher = New DirectorySearcher(dirEntry)
    dirSearcher.Filter = "(&(objectCategory=Person)(objectClass=user) _
(SAMAccountName=" & sLogin & "))"
    dirSearcher.SearchScope = SearchScope.Subtree
    Dim results As SearchResult = dirSearcher.FindOne()
    '   2. Check returned results
    If Not results Is Nothing Then
        '   2a. User was returned
        Dim dirEntryResults As DirectoryEntry = GetDirectoryEntry(results.Path)
        Dim iVal As Integer = CInt(dirEntryResults.Properties("userAccountControl").Value)
        '   3. Disable the users account
        dirEntryResults.Properties("userAccountControl").Value = iVal Or &H2
        '   4. Hide users email from all Exchange Mailing Lists
        dirEntryResults.Properties("msExchHideFromAddressLists").Value = "TRUE"
        dirEntryResults.CommitChanges()
        dirEntryResults.Close()
    End If
    dirEntry.Close()
End Sub


The next section is to show how to update/modify a users Active Directory account. The items being updated in this function can be changed to whatever fields you have in your Active Directory and what needs to be updated for a specific user. For arguments sake we're going to update a users:

Department
Title
Phone Extension

To accomplish this first we need to get a directory entry (GetDirectoryEntry from above), then create a search object and set the filter for the search object (for this we will use the same 3 "fields" we did for disabling a users account). This is where the similarities end, with this we have to create a 2nd Directory Entry only this time we pass the search results to it. We then use the SetADProperty function from the beginning of the tutorial to set the properties we want to update. Once we have done that then we commit the changes then close and clean up behind ourselves. Keep in mind to make this function work you need Administrative permissions on the network:

CODE

''' <summary>
''' Method that updates user's properties
''' </summary>
''' <param name="userLogin">Login of the user to update</param>
''' <param name="userDepartment">New department of the specified user</param>
''' <param name="userTitle">New title of the specified user</param>
''' <param name="userPhoneExt">New phone extension of the specified user</param>
Public Sub UpdateUserADAccount(ByVal userLogin As String, _
ByVal userDepartment As String, ByVal userTitle As String, ByVal userPhoneExt As String)
    Dim dirEntry As DirectoryEntry = GetDirectoryEntry()
    Dim dirSearcher As DirectorySearcher = New DirectorySearcher(dirEntry)
    '   1. Search the Active Directory for the speied user
    dirSearcher.Filter = "(&(objectCategory=Person)(objectClass=user) _
(SAMAccountName=" & userLogin & "))"
    dirSearcher.SearchScope = SearchScope.Subtree
    Dim searchResults As SearchResult = dirSearcher.FindOne()
    If Not searchResults Is Nothing Then
        Dim dirEntryResults As New DirectoryEntry(results.Path)
        'The properties listed here may be different then the properties in your Active Directory
        'so they may need to be changed according to your network
        '   2. Set the new property values for the specified user
        SetADProperty(dirEntryResults, "department", userDepartment)
        SetADProperty(dirEntryResults, "title", userTitle)
        SetADProperty(dirEntryResults, "phone", userPhoneExt)
        '   3. Commit the changes
        dirEntryResults.CommitChanges()
        '   4. Close & Cleanup
        dirEntryResults.Close()
    End If
    '   4a. Close & Cleanup
    dirEntry.Close()
End Sub


Now what if you needed to provide a list of all the computer names that are on the Active Directory. Well once you've learned what you've learned so far about using the System.DirectoryServices Namespace this is much simpler. When I created this function I decided to return a collection populated with the computer names, you could return a generic list, a dataset, or anything else. With this function you, once again (see a theme starting here?) use the GetDirectoryEntry function, you create a search object then set the filter. With this function we use the objectClass field, but instead of a Person the class is computer. This is the function:

CODE

''' <summary>
''' Function to query the Active Directory and return all the computer names
'''on the network
''' </summary>
''' <returns>A collection populated with all the computer names</returns>
Public Shared Function ListAllADComputers() As Collection
    Dim dirEntry As DirectoryEntry = GetDirectoryEntry()
    Dim pcList As New Collection()
    '   1. Search the Active Directory for all objects with type of computer
    Dim dirSearcher As DirectorySearcher = New DirectorySearcher(dirEntry)
    dirSearcher.Filter = ("(objectClass=computer)")
    '   2. Check the search results
    Dim dirSearchResults As SearchResult
    '   3. Loop through all the computer names returned
    For Each dirSearchResults In dirSearcher.FindAll()
        '   4. Check to ensure the computer name isnt already listed in the collection
        If Not pcList.Contains(dirSearchResults.GetDirectoryEntry().Name.ToString()) Then
            '   5. Add the computer name to the collection (since it dont already exist)
            pcList.Add(dirSearchResults.GetDirectoryEntry().Name.ToString())
        End If
    Next
    '   6. Return the results
    Return pcList
End Function


With this next function you can populate a collection (you can alter it to populate a Generic List, DataSet, etc) with the names of all the groups a user is a member of (i.e.; Administrators, Power Users, Guest, etc). With this you create your Directory Object (just not the GetDirectoryEntry function) passing it the path (binding path to the AD), the username & password of the user you want to query. So far we have seen SAMAaacount, givenName, sn, objectClass, and objectCategory. Now we will see the memberOf Property, this is the property telling what group the user is a member of.

Once you have the list of all the groups, then you loop through the list and add them to the collection (or whatever object you choose to use). It looks like this:

CODE

''' <summary>
''' Function to return all the groups the user is a member od
''' </summary>
''' <param name="_path">Path to bind to the AD</param>
''' <param name="username">Username of the user</param>
''' <param name="password">password of the user</param>
Private Function GetGroups(ByVal _path As String, ByVal username As String, _
ByVal password As String) As Collection
    Dim Groups As New Collection
    Dim dirEntry As New System.DirectoryServices.DirectoryEntry(_path, username, password)
    Dim dirSearcher As New DirectorySearcher(dirEntry)
    dirSearcher.Filter = String.Format("(sAMAccountName={0}))", username)
    dirSearcher.PropertiesToLoad.Add("memberOf")
    Dim propCount As Integer
    Try
        Dim dirSearchResults As SearchResult = dirSearcher.FindOne()
        propCount = dirSearchResults.Properties("memberOf").Count
        Dim dn As String
        Dim equalsIndex As String
        Dim commaIndex As String
        For i As Integer = 0 To propCount - 1
            dn = dirSearchResults.Properties("memberOf")(i)
            equalsIndex = dn.IndexOf("=", 1)
            commaIndex = dn.IndexOf(",", 1)
            If equalsIndex = -1 Then
                Return Nothing
            End If
            If Not Groups.Contains(dn.Substring((equalsIndex + 1), _
(commaIndex - equalsIndex) - 1)) Then
                Groups.Add(dn.Substring((equalsIndex + 1), (commaIndex - equalsIndex) - 1))
            End If
        Next
    Catch ex As Exception
        If ex.GetType Is GetType(System.NullReferenceException) Then
            MessageBox.Show("Selected user isn't a member of any groups at this time.", "No groups listed", MessageBoxButtons.OK, MessageBoxIcon.Error)
            'they are still a good user just does not
            'have a "memberOf" attribute so it errors out.
            'code to do something else here if you want
        Else
            MessageBox.Show(ex.Message.ToString, "Search Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
        End If
    End Try
    Return Groups
End Function


Now we will look into how to determine if a users account has been disabled. This function returns a Boolean (True/False) based on what flag is returned from the query. This function requires an Enumeration of all possible flags for a users account, this is the Enumeration:

CODE

Public Enum ADAccountOptions
   UF_TEMP_DUPLICATE_ACCOUNT = 256
   UF_NORMAL_ACCOUNT = 512
   UF_INTERDOMAIN_TRUST_ACCOUNT = 2048
   UF_WORKSTATION_TRUST_ACCOUNT = 4096
   UF_SERVER_TRUST_ACCOUNT = 8192
   UF_DONT_EXPIRE_PASSWD = 65536
   UF_SCRIPT = 1
   UF_ACCOUNTDISABLE = 2
   UF_HOMEDIR_REQUIRED = 8
   UF_LOCKOUT = 16
   UF_PASSWD_NOTREQD = 32
   UF_PASSWD_CANT_CHANGE = 64
   UF_ACCOUNT_LOCKOUT = 16
   UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED = 128
End Enum


Now for the function that checks for the value of 2 (meaning disabled):
CODE

''' <summary>
''' This will perform a logical operation on the userAccountControl values
''' to see if the user account is enabled or disabled.  The flag for determining if the
''' account is active is a bitwise value (decimal =2)
''' </summary>
''' <param name="userAccountControl"></param>
''' <returns></returns>
Public Shared Function IsAccountActive(ByVal userAccountControl As Integer) As Boolean
    Dim accountDisabled As Integer = Convert.ToInt32(ADAccountOptions.UF_ACCOUNTDISABLE)
    Dim flagExists As Integer = userAccountControl And accountDisabled
    'if a match is found, then the disabled flag exists within the control flags
    If flagExists > 0 Then
        Return False
    Else
        Return True
    End If
End Function


I will leave you with one last "tidbit". This function will allow you to add a user to a specific security group. First you create your search object, then you set your filter, this time we will use the objectClass and set it to group (since that is what we're looking for). Then we need to check to see if this user is already a member of the specified group, we do this by looping through all the members in the group (implementing IEnumerable), then checking all names against the user we want to add. Here is the function:

CODE

'' <summary>
''' Method to add a user to a group
''' </summary>
''' <param name="de">DirectoryEntry to use</param>
''' <param name="deUser">User DirectoryEntry to use</param>
''' <param name="GroupName">Group Name to add user to</param>
Public Shared Sub AddUserToGroup(ByVal de As DirectoryEntry, ByVal deUser As DirectoryEntry, ByVal GroupName As String)
    Dim deSearch As DirectorySearcher = New DirectorySearcher()
    deSearch.SearchRoot = de
    deSearch.Filter = "(&(objectClass=group) (cn=" & GroupName & "))"
    Dim results As SearchResultCollection = deSearch.FindAll()
    Dim isGroupMember As Boolean = False
    If results.Count > 0 Then
        Dim group As New DirectoryEntry(results(0).Path)
        Dim members As Object = group.Invoke("Members", Nothing)
        For Each member As Object In CType(members, IEnumerable)
            Dim x As DirectoryEntry = New DirectoryEntry(member)
            Dim name As String = x.Name
            If name <> deUser.Name Then
                isGroupMember = False
            Else
                isGroupMember = True
                Exit For
            End If
        Next member
        If (Not isGroupMember) Then
            group.Invoke("Add", New Object() {deUser.Path.ToString()})
        End If
        group.Close()
    End If
    Return
End Sub


This concludes this tutorial on Active Directory in VB.Net. Remember, this is a semi high level overview of the Active Directory, there are so many things you can do it would take ages for me to list them (and I'm sure DIC++ wouldn't appreciate me using all their HDD space), but this should at least give you a much better understanding of how the System.DirectoryServices Namespace works and what can be done with it. Remember, for anything other than simple searching of the Active Directory Administrative permissions are required. If you have any questions, about this tutorial or some other help with AD feel free to leave me a message here and I will respond as soon as I can. Thanks for reading.




hi guys,

i really appreciate the efforts you have put in to post this code.
but can you help me using this code in vb.net application, where in i am using a form and fileds like username, password, first name, last name, description, want to set the home drive with permissions.

please help me it's urgent.
This is a "lo-fi" version of our main content. To view the full version with more information, formatting and images, please click here.
Invision Power Board © 2001-2008 Invision Power Services, Inc.