Page 1 of 1

Working with the Active Directory in VB.Net This tutorial will get you started down the road of working within the Rate Topic: -----

#1 PsychoCoder  Icon User is offline

  • Google.Sucks.Init(true);
  • member icon

Reputation: 1641
  • View blog
  • Posts: 19,853
  • Joined: 26-July 07

Post icon  Posted 26 July 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:

''' <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:

''' <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:

''' <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:

''' <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:

''' <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:

''' <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:

''' <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:

''' <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:

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):
''' <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:

'' <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.

Is This A Good Question/Topic? 0
  • +

Replies To: Working with the Active Directory in VB.Net

#2 kingpin2005  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 1
  • Joined: 02-November 07

Posted 02 November 2007 - 06:45 AM

View PostPsychoCoder, on 26 Jul, 2007 - 09:41 PM, said:

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:

''' <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:

''' <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:

''' <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:

''' <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:

''' <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:

''' <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:

''' <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:

''' <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:

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):
''' <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:

'' <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.
Was This Post Helpful? 0
  • +
  • -

#3 vik2121  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 13
  • Joined: 17-April 12

Posted 17 April 2012 - 06:17 PM

Hi Guys This looks great. how ever i need some other info as well that i need your help.

I have a table in Oracle with a column userid and i have a userid column in Active Directory. based on this i want to query the Network ID and update in one of the Group in the Active Directory. Can you guys help me on how to do this in VB.NET? Appreciate your help. I am new to Oracle. I have been working with Sql Server. Thanks

This post has been edited by modi123_1: 17 April 2012 - 06:41 PM
Reason for edit:: please don't quote the entire OP..

Was This Post Helpful? 0
  • +
  • -

Page 1 of 1