Click here to Skip to main content
Click here to Skip to main content

Working with Active Directory in VB.NET

, 3 Aug 2007 CPOL
Rate this:
Please Sign up or sign in to vote.
This article explains how to perform tasks within the Active Directory in a Windows 2000+ network.

Introduction

This article discusses working within the Active Directory (AD) using VB.NET, how to query the AD, query groups, members, adding users, suspending users, and changing user 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 three 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 and managing network resources.
  3. System.DirectoryServices: The System.DirectoryServices namespace built into the .NET Framework is designed to provide programming access to LDAP directories (Active Directory), and is built on the ASDI API.

Background

Recently, at work, I was tasked with creating a security system for the application I currently program. I took this a step further, and wanted to ensure that in the event the application was ever taken off-site, that it couldn't run; this is when I came up with the idea of basing my security on the Active Directory of the company network. This would prevent an individual from taking the application off-site and attempting to use it, either for malicious purposes or to just "play" around with it. This also seemed to be the most secure way as only a select group of people actually have the permissions to alter the Active Directory in any way. Though there is a ton of built-in functionality in the .NET Framework for working with the Active Directory, I decided to create my own object, a wrapper around the System.DirectoryServices namespace in .NET.

The employees who use this application are all in a single department. They, in turn, have their own table in the database holding specific information about the user, i.e., access level, queue, and such. When we first launched this application, the security was based on the user's access level in the database. This determined what the user had access to. When the application launched, it checked the name the user was logged in as:

Environment.UserName.ToString

It then split the user name (at my company, your username is FirstName.LastName) to return the first and last name, and queried the aforementioned table to retrieve the user's access level based on the first and last name retrieved earlier. I wanted to keep the access level security as this ensured people didn't have access to areas of the application they shouldn't have, so I added a new column to the user table to hold their AD login (data type SysUser). From there, I would use Environment.UserName.ToString to once again retrieve the username they are logged in with. With this, I would retrieve their LoginName from the users table, and query the Active Directory to ensure this is a valid network account and they have permissions to be using this application.

Working on this AD security, I discovered the true power of the System.DirectoryServices namespace; with it you can:

  1. Add a new user to the network
  2. Suspend a user's account
  3. Enable a user's account
  4. Reset a user's password
  5. Update a user account
  6. Add a user to a specific group
  7. Remove a user from a group
  8. Retrieve the list of all groups a user is a member of
  9. Retrieve all computers connected to the network
  10. Determine if a user's account is disabled
  11. Check if a user account is active (perform a basic login)

and so much more. In this article, I will walk you through connecting to an Active Directory, searching for users in the Active Directory, disabling a user's account, resetting a user's password, setting up a mailbox for a new user, displaying all computers on the network, and adding a user to a specific group in the Active Directory.

Using the code

I will get this helper out of the way first, many of the procedures/functions will be calling this helper. The job of this helper is simply to set the properties of a user's AD account. This saves time and code when properties need to either be set or altered. This helper searches the provided property to see if it already exists in the user's account; if it doesn't, then it adds and sets the value; if it already exists, it updates the property's value:

''' <span class="code-SummaryComment"><summary></span>
''' Helper method that sets properties for AD users.
''' <span class="code-SummaryComment"></summary></span>
''' <span class="code-SummaryComment"><param name="de">DirectoryEntry to use</param></span>
''' <span class="code-SummaryComment"><param name="pName">Property name to set</param></span>
''' <span class="code-SummaryComment"><param name="pValue">Value of property to set</param></span>
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 to do when working with the Active Directory is to create a connection to the Active Directory:

''' <span class="code-SummaryComment"><summary></span>
''' Method used to create an entry to the AD.
''' Replace the path, username, and password with the ones specific to you 
'''and your network. 
'''Hardcoding a username & password can be viewed as a security risk
'''    so you may want to store this in an encrypted file.
''' <span class="code-SummaryComment"></summary></span>
''' <span class="code-SummaryComment"><returns> A DirectoryEntry </returns></span>
Public Shared Function GetDirectoryEntry() As DirectoryEntry
    Dim dirEntry As DirectoryEntry = New DirectoryEntry()
    dirEntry.Path = "LDAP://192.168.1.1/CN=Users;DC=Yourdomain"
    dirEntry.Username = "yourdomain\sampleuser"
    dirEntry.Password = "samplepassword"
    Return dirEntry
End Function

As stated above, when I added the new column to the users table to hold their AD login (the SysUser data type built into SQL2000/2005), this data type holds the value in the format DOMAIN\USERNAME. When searching the Active Directory for a specific user, you need to strip the "DOMAIN\" off, so I created the following function to do this for me:

''' <span class="code-SummaryComment"><summary></span>
''' Function to extract just the login from the provided string 
''' (given in the format DOMAIN\USERNAME)
''' <span class="code-SummaryComment"></summary></span>
''' <span class="code-SummaryComment"><param name="path">Full AD login of the user</param></span>
''' <span class="code-SummaryComment"><returns>The login with the "DOMAIN\" stripped</returns></span>
Public Function ExtractUserName(ByVal path As String) As String
    Dim userPath As String() = path.Split(New Char() {"\"c})
    Return userPath((userPath.Length - 1))
End Function

To search for the provided user in the Active Directory, I created the following function:

''' <span class="code-SummaryComment"><summary></span>
''' 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 user being queried
''' <span class="code-SummaryComment"></summary></span>
''' <span class="code-SummaryComment"><param name="loginName">Login of the associate to search for</param></span>
''' <span class="code-SummaryComment"><param name="givenName">First name fo the associate being added</param></span>
''' <span class="code-SummaryComment"><param name="surName">Last name of the associate being added</param></span>
''' <span class="code-SummaryComment"><returns>True or False depending if the login provided is a valid one</returns></span>
Public Function IsValidADLogin(ByVal loginName As String, _
       ByVal givenName As String, ByVal surName As String) As Boolean
    Try
        Dim search As New DirectorySearcher()
        search.Filter = String.Format("(&(SAMAccountName={0}) & _
                        (givenName={1})(sn={2}))", _
                        ExtractUserName(loginName), givenName, surName)
        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()
        If result Is Nothing Then
            Return False
        Else
            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

With this function, if the selected user is found, then True is returned, else False is returned, letting the programmer know that this isn't a valid user in the Active Directory. As stated earlier, there are many things you can do when accessing the Active Directory; I will now walk you through just a sample of the things possible with the System.DirectoryServices namespace.

The first example will be creating a new AD user; in order to use this functionality, you must have an account that has the proper permissions to add a user. (All accounts have access to search the Active Directory, only members of the Administrator Group can perform many of the functions, including creating a new AD user.) The easiest way to do this is to delegate the permissions of a user account with DomainAdmin permissions to the thread that is running the program. The following code will take care of this for you:

'' <summary>
''' Establish identity (principal) and culture for a thread.
''' <span class="code-SummaryComment"></summary></span>
Public Shared Sub SetCultureAndIdentity()
    AppDomain.CurrentDomain.SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal)
    Dim principal As WindowsPrincipal = CType(Thread.CurrentPrincipal, WindowsPrincipal)
    Dim identity As WindowsIdentity = CType(principal.Identity, WindowsIdentity)
    System.Threading.Thread.CurrentThread.CurrentCulture = New CultureInfo("en-US")
End Sub

1. Creating a new Active Directory user account

There are four procedures required to perform this. The first procedure, CreateADAccount, is the procedure that actually creates the new user. It relies on three other procedures: SetPassword is used to set the password of the new user, EnableAccount is used to enable the new account, and AddUserToGroup is used to add the new user to the specified Active Directory Group.

First, let's look at the CreateADAccount procedure:

''' <span class="code-SummaryComment"><summary></span>
''' Procedure to create a new Active Directory account
''' <span class="code-SummaryComment"></summary></span>
''' <span class="code-SummaryComment"><param name="sUserName">Username for the new account</param></span>
''' <span class="code-SummaryComment"><param name="sPassword">Password for the new account</param></span>
''' <span class="code-SummaryComment"><param name="sFirstName">First name of the user</param></span>
''' <span class="code-SummaryComment"><param name="sLastName">Last name of the user</param></span>
''' <span class="code-SummaryComment"><param name="sGroupName">Group to add the user to</param></span>
''' <span class="code-SummaryComment"><remarks></remarks></span>
Public Sub CreateAdAccount(ByVal sUserName As String, _
       ByVal sPassword As String, _
       ByVal sFirstName As String, ByVal sLastName As String, _
       ByVal sGroupName As String)
    Dim catalog As Catalog = New Catalog()
    Dim dirEntry As New DirectoryEntry()
    ' 1. Create user account
    Dim adUsers As DirectoryEntries = dirEntry.Children
    Dim newUser As DirectoryEntry = adUsers.Add("CN=" & sUserName, "user")
    ' 2. Set properties
    SetProperty(newUser, "givenname", sFirstName)
    SetProperty(newUser, "sn", sLastName)
    SetProperty(newUser, "SAMAccountName", sUserName)
    SetProperty(newUser, "userPrincipalName", sUserName)
    newUser.CommitChanges()
    ' 3. Set the password
    SetPassword(newUser, sPassword)
    ' 5. Add the user to the specified group
    AddUserToGroup(dirEntry, newUser, sGroupName)
    ' 6. Enable the account
    EnableAccount(newUser)
    ' 7. Close & clean-up
    newUser.Close()
    dirEntry.Close()
End Sub

The three highlighted lines at the end of the procedure are referring to the three additional procedures mentioned above, the first being SetPassword:

'' <summary>
''' Method to set a user's password
''' <span class="code-SummaryComment"><param name="dEntry">DirectoryEntry to use</param></span>
''' <span class="code-SummaryComment"><param name="sPassword">Password for the new user</param></span>
Private Shared Sub SetPassword(ByVal dEntry As DirectoryEntry, _
ByVal sPassword As String)
    Dim oPassword As Object() = New Object() {sPassword}
    Dim ret As Object = dEntry.Invoke("SetPassword", oPassword)
    dEntry.CommitChanges()
End Sub

The second procedure is the EnableAccount, which is responsible for enabling the new account so the user can use it:

''' <span class="code-SummaryComment"><summary></span>
''' Method to enable a user account in the AD.
''' <span class="code-SummaryComment"></summary></span>
''' <span class="code-SummaryComment"><param name="de"></param></span>
Private Shared Sub EnableAccount(ByVal de As DirectoryEntry)
    'UF_DONT_EXPIRE_PASSWD 0x10000
    Dim exp As Integer = CInt(de.Properties("userAccountControl").Value)
    de.Properties("userAccountControl").Value = exp Or &H1
    de.CommitChanges()
    'UF_ACCOUNTDISABLE 0x0002
    Dim val As Integer = CInt(de.Properties("userAccountControl").Value)
    de.Properties("userAccountControl").Value = val And Not &H2
    de.CommitChanges()
End Sub

The third and final procedure is the AddUserToGroup, which adds the new user to the specified Active Directory Group:

'' <summary>
''' Method to add a user to a group
''' <span class="code-SummaryComment"></summary></span>
''' <span class="code-SummaryComment"><param name="de">DirectoryEntry to use</param></span>
''' <span class="code-SummaryComment"><param name="deUser">User DirectoryEntry to use</param></span>
''' <span class="code-SummaryComment"><param name="GroupName">Group Name to add user to</param></span>
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

2. Disable a user account

With the System.DirectoryServices namespace, you not only can create a new Active Directory user, but can also disable a user's Active Directory account. To disable a user's Active Directory account, use the following procedure:

''' <span class="code-SummaryComment"><summary></span>
''' Method that disables a user account in the AD 
''' and hides user's email from Exchange address lists.
''' <span class="code-SummaryComment"></summary></span>
''' <span class="code-SummaryComment"><param name="sLogin">Login of the user to disable</param></span>
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

3. Update/Modify a user's Active Directory account information

With the System.DirectoryServices namespace, you not only can create a new Active Directory user and disable a user's Active Directory account, but can also update/modify a user's Active Directory account properties. The properties in this procedure will differ from the properties in your Active Directory, so you will need access to see what properties are available; the ones I have listed are just a small sample of the possible properties. To update/modify a user's Active Directory account, use the following procedure:

''' <span class="code-SummaryComment"><summary></span>
''' Method that updates user's properties
''' <span class="code-SummaryComment"></summary></span>
''' <span class="code-SummaryComment"><param name="userLogin">Login of the user to update</param></span>
''' <span class="code-SummaryComment"><param name="userDepartment">New department of the specified user</param></span>
''' <span class="code-SummaryComment"><param name="userTitle">New title of the specified user</param></span>
''' <span class="code-SummaryComment"><param name="userPhoneExt">New phone extension of the specified user</param></span>
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
        SetProperty(dirEntryResults, "department", userDepartment)
        SetProperty(dirEntryResults, "title", userTitle)
        SetProperty(dirEntryResults, "phone", userPhoneExt)
        '   3. Commit the changes
        dirEntryResults.CommitChanges()
        '   4. Close & Cleanup
        dirEntryResults.Close()
    End If
    '   4a. Close & Cleanup
    dirEntry.Close()
End Sub

4. Listing all computers in the Active Directory

Using the System.DirectoryServices namespace in the .NET Framework, you can also create a list of all the computers listed in the Active Directory. When I created this function, I populated a Collection with each computer name, then I could enumerate through the Collection and populate a control (such as ListBox, DataGridView, and such) with the names returned from the Active Directory query. The function used is:

''' <span class="code-SummaryComment"><summary></span>
''' Function to query the Active Directory and return all the computer names 
'''on the network
''' <span class="code-SummaryComment"></summary></span>
''' <span class="code-SummaryComment"><returns>A collection populated with all the computer names</returns></span>
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

5. List all groups a user is in

The following function returns a Collection of Groups the user is a member of. I went with the Collection rather than a string concatenated with the Group names, as this way, I can populate a control with the values:

''' <span class="code-SummaryComment"><summary></span>
''' Function to return all the groups the user is a member od
''' <span class="code-SummaryComment"></summary></span>
''' <span class="code-SummaryComment"><param name="_path">Path to bind to the AD</param></span>
''' <span class="code-SummaryComment"><param name="username">Username of the user</param></span>
''' <span class="code-SummaryComment"><param name="password">password of the user</param></span>
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
End Class

6. Determine if a user's account has been disabled

The following function can be used to determine if a selected user's account had been disabled:

''' <span class="code-SummaryComment"><summary></span>
''' This will perfrom 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)
''' <span class="code-SummaryComment"></summary></span>
''' <span class="code-SummaryComment"><param name="userAccountControl"></param></span>
''' <span class="code-SummaryComment"><returns></returns></span>
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

7. Remove a user from a specific group

The following function can be used to remove a user from a specific group:

''' <span class="code-SummaryComment"><summary></span>
''' This will perform the removal of a user from the specified group
''' <span class="code-SummaryComment"></summary></span>
''' <span class="code-SummaryComment"><param name="UserName">Username of the user to remove</param></span>
''' <span class="code-SummaryComment"><param name="GroupName">Groupname to remove them from</param></span>
''' <span class="code-SummaryComment"><remarks></remarks></span>
Public Shared Sub RemoveUserFromGroup(ByVal UserName As String, _
                  ByVal GroupName As String)

    Dim Domain As New String("")
    
    'get reference to group
    Domain = "/CN=" + GroupName + ",CN=Users," + GetLDAPDomain()
    Dim oGroup As DirectoryEntry = GetDirectoryObject(Domain)
    
    'get reference to user
    Domain = "/CN=" + UserName + ",CN=Users," + GetLDAPDomain()
    Dim oUser As DirectoryEntry = GetDirectoryObject(Domain)
    
    'Add the user to the group via the invoke method
    oGroup.Invoke("Remove", New Object() {oUser.Path.ToString()})
    
    oGroup.Close()
    oUser.Close()
End Sub

8. Check a user account to see if it's active

The following process does a simulated login to a user's account to see if it's a valid account and if it's active. This method calls other functions to work properly: GetUser and the overrides GetUser, IsUserValid, and IsAccountActive; all of these will be listed here.

 ''' <span class="code-SummaryComment"><summary></span>
''' This method will not actually log a user in, but will perform tests to ensure
''' that the user account exists (matched by both the username and password), and also
''' checks if the account is active.
''' <span class="code-SummaryComment"></summary></span>
''' <span class="code-SummaryComment"><param name="UserName"></param></span>
''' <span class="code-SummaryComment"><param name="Password"></param></span>
Public Shared Function Login(ByVal UserName As String, ByVal Password As String) _
              As ADWrapper.LoginResult
    'first, check if the logon exists based on the username and password
    If IsUserValid(UserName, Password) Then
        Dim dirEntry As DirectoryEntry = GetUser(UserName)
        If Not dirEntry Is Nothing Then
            'convert the accountControl value so that a logical 
            'operation can be performed
            'to check of the Disabled option exists.
            Dim accountControl As Integer = & _
            Convert.ToInt32(dirEntry.Properties("userAccountControl")(0))
            dirEntry.Close()

            'if the disabled item does not exist then the account is active
            If Not IsAccountActive(accountControl) Then
                Return LoginResult.LOGIN_USER_ACCOUNT_INACTIVE
            Else
                Return LoginResult.LOGIN_OK

            End If
        Else
            Return LoginResult.LOGIN_USER_DOESNT_EXIST
        End If
    Else
        Return LoginResult.LOGIN_USER_DOESNT_EXIST
    End If
End Function

GetUser (+Overrides version)

''' <span class="code-SummaryComment"><summary></span>
''' This will return a DirectoryEntry object if the user does exist
''' <span class="code-SummaryComment"></summary></span>
''' <span class="code-SummaryComment"><param name="UserName"></param></span>
''' <span class="code-SummaryComment"><returns></returns></span>
Public Shared Function GetUser(ByVal UserName As String) As DirectoryEntry
    'create an instance of the DirectoryEntry
    Dim dirEntry As DirectoryEntry = GetDirectoryObject("/" + GetLDAPDomain())

    'create instance fo the direcory searcher
    Dim dirSearch As New DirectorySearcher(dirEntry)

    dirSearch.SearchRoot = dirEntry
    'set the search filter
    dirSearch.Filter = "(&(objectCategory=user)(cn=" + UserName + "))"
    'deSearch.SearchScope = SearchScope.Subtree;

    'find the first instance
    Dim searchResults As SearchResult = dirSearch.FindOne()

    'if found then return, otherwise return Null
    If Not searchResults Is Nothing Then
        'de= new DirectoryEntry(results.Path,ADAdminUser, _
             ADAdminPassword,AuthenticationTypes.Secure);
        'if so then return the DirectoryEntry object
        Return searchResults.GetDirectoryEntry()
    Else
        Return Nothing
    End If
End Function


''' <span class="code-SummaryComment"><summary></span>
''' Override method which will perfrom query based on combination of username 
'''and password This is used with the login process to validate the user 
'''credentials and return a user object for further validation. This is 
'''slightly different from the other GetUser... methods as this will use the 
'''UserName and Password supplied as the authentication to check if the user 
'''exists, if so then  the users object will be queried using these 
'''credentials
''' <span class="code-SummaryComment"></summary></span>
''' <span class="code-SummaryComment"><param name="UserName"></param></span>
''' <span class="code-SummaryComment"><param name="password"></param></span>
''' <span class="code-SummaryComment"><returns></returns></span>
Public Shared Function GetUser(ByVal UserName As String, ByVal Password _
                               As String) As DirectoryEntry
    'create an instance of the DirectoryEntry
    Dim dirEntry As DirectoryEntry = GetDirectoryObject(UserName, Password)

    'create instance fo the direcory searcher
    Dim dirSearch As New DirectorySearcher()

    dirSearch.SearchRoot = dirEntry
    'set the search filter
    dirSearch.Filter = "(&(objectClass=user)(cn=" + UserName + "))"
    dirSearch.SearchScope = SearchScope.Subtree

    'set the property to return
    'deSearch.PropertiesToLoad.Add("givenName");

    'find the first instance
    Dim searchResults As SearchResult = dirSearch.FindOne()

    'if a match is found, then create directiry object and return, 
    'otherwise return Null
    If Not searchResults Is Nothing Then
        'create the user object based on the admin priv.
        dirEntry = New DirectoryEntry(searchResults.Path, ADAdminUser, _
                       ADAdminPassword, AuthenticationTypes.Secure)
        Return dirEntry
    Else
        Return Nothing
    End If
End Function

IsUserValid

This procedure will check to make sure it is a valid user account:

''' <span class="code-SummaryComment"><summary></span>
''' This method will attempt to log in a user based on the username and 
''' password
''' to ensure that they have been set up within the Active Directory. 
'''This is the basic UserName, Password
''' check.
''' <span class="code-SummaryComment"></summary></span>
''' <span class="code-SummaryComment"><param name="UserName"></param></span>
''' <span class="code-SummaryComment"><param name="Password"></param></span>
Public Shared Function IsUserValid(ByVal UserName As String, _
                       ByVal Password As String) As Boolean
    Try
        'if the object can be created then return true
        Dim dirUser As DirectoryEntry = GetUser(UserName, Password)
        dirUser.Close()
        Return True
    Catch generatedExceptionName As Exception
        'otherwise return false
        Return False
    End Try
End Function

IsUserActive

This will check to see if it is an active user account:

''' <span class="code-SummaryComment"><summary></span>
''' This will perfrom 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)
''' <span class="code-SummaryComment"></summary></span>
''' <span class="code-SummaryComment"><param name="userAccountControl"></param></span>
''' <span class="code-SummaryComment"><returns></returns></span>
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

Points of interest

This is really a high level overview of working with the Active Directory in VB.NET. There is more functionality in the wrapper DLL I have for download with this article, such as querying the AD for all active groups, returning all users who are a member of a Group you specify, etc. Working with the Active Directory can be daunting at first, but don't let it scare you off. Once you learn just a few items, it's no more complicated than querying a database for information.

*Special thanks to Erika Ehrli, whose article on VBDotNetHeaven.Com helped me solve some AD logic I struggled with.*

Happy coding!

License

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

Share

About the Author

Psycho-*Coder*-Extreme
Software Developer (Senior)
United States United States
I received my Bachelors from the University of Georgia in 1989 and have been a Windows programmer ever since. I have worked with encryption algorithms, online payment systems, Windows Development, MSSQL 4 and above, VB 4 and above. I received my .Net Certification in 2005 and currently work as a Software Developer in a .Net House (mainly VB.Net, but I also do some C# on the side). I run a small Web & Software Development company from my home and (as stated) work full-time as a .Net programmer.
 
Update: I am now Senior Application Developer for a small upstart company. I develop solely in C# utilizing MSSQL

Comments and Discussions

 
Suggestion[My vote of 2] Code should PinmemberMember 1089060017-Oct-14 11:26 
QuestionC# Conversion PinmemberHeppythemanman127-Jun-14 13:21 
AnswerRe: C# Conversion PinmemberWanderingLecher22-Oct-14 11:22 
QuestionHelp VB 2010 + AD PinmemberMichael Monestel A16-Jun-14 15:03 
QuestionMultiple Problems with code associated with this Article PinmemberRon Nospam13-Feb-14 11:14 
QuestionSetPassword Error PinmemberManuel Benavente14-Nov-13 4:36 
QuestionHow can I display all contacts from 'Global Address List' PinmemberJohnbrithol15-Mar-13 7:50 
QuestionHow to make this work with a second domain? PinmemberJDMils11-Jun-12 20:57 
BugErrors PinmemberPsJackson11-Jun-12 7:59 
GeneralMy vote of 5 PinmemberMember 832743921-Mar-12 14:57 
QuestionAdd user to group? PinmemberMember 87055527-Mar-12 6:37 
GeneralMy vote of 5 Pinmemberasid-russ29-Feb-12 7:46 
GeneralHow can i add a user to "memberof " Active Directory attribute(new group). Pinmembercheriaan9-May-11 5:07 
GeneralMy vote of 5 PinmemberRugwed26-Mar-11 1:26 
It gives me what I am looking for
GeneralServer not found!!! PinmemberRugwed26-Mar-11 1:14 
GeneralMy vote of 5 Pinmemberrusskyone25-Mar-11 8:43 
GeneralMy vote of 5 PinmvpSandeep Mewara11-Feb-11 18:58 
GeneralGetUser function Pinmembercterrinw3-Oct-10 22:41 
GeneralAdd user from another domain PinmemberMichael J. Collins3-Sep-10 9:15 
QuestionHow to use the ADWrapper DLL PinmemberDaniellange118-Jul-10 1:31 
GeneralExcellent Reference .... PinmemberRiddick31428-Jun-10 7:00 
GeneralThank you! PinmemberKoaQiu16-Nov-09 22:56 
QuestionAll logged in users PinmemberSevententh16-Nov-09 7:02 
GeneralRemoving Computer Account From A Group Pinmembersismeya11-Nov-09 10:18 
AnswerThe value provided for adsObject does not implement IADs - FIXED!!! PinmemberNadimJ23-Sep-09 1:53 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web03 | 2.8.141220.1 | Last Updated 3 Aug 2007
Article Copyright 2007 by Psycho-*Coder*-Extreme
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid