I have a project, to create a web interface to let user login to and change their Active Directory(AD) password. Never been touching the AD for my life, this being the great explore for me to deal with AD object in .NET framework.

To be able to access to AD, we will need to include a reference to our project, to include a System.DirecotryServices dll into our web project. In order to use the DirectoryEntry class, an imports statement will be needed, as following,

Imports System.DirectoryServices

To keep things simple, as I promised myself before I start the project, giving a visit to MSDN, I learnt that the LDAP path to the AD is something like this: LDAP://litwareinc/CN=Users,DC=litwareinc,DC=com , the litwareinc will be the domain name and the DC=com will the the .com thing (yes, I guess it). From the Internet, I learn a method to be call to change the password will be ChangePasswordand SetPassword. The SetPassword is the method that act as administrator, which does not need to specify a old password, so the suitable method for my case will be the ChangePassword.

First to create a DirectoryEntry object, I will need to provide the path to our AD, which is the LDAP://litwareinc/CN=Users,DC=litwareinc,DC=com, after that we need to invoke the ChangePassword method from the DirectoryEntry object, it will looks something like this:

Dim deObj As New DirectoryEntry("LDAP://litwareinc/CN=Users, DC=litwareinc, DC=com", UserName, Password, AuthenticationTypes.Secure)
deObj.Invoke("ChangePassword", New Object(){OldPassword, NewPassword})

First run, the above codes threw me an exception Unknown name. (Exception from HRESULT: 0x80020006 (DISP_E_UNKNOWNNAME)). after doing some trial and error, realized that I need to specified the user’s display name at the path, e.g. I want to change the name for user Amy Alberts, which using AmyA as login name, and my AD path should be LDAP://litwareinc/CN=Amy Alberts,CN=Users,DC=litwareinc,DC=com.

The question came, people won’t input their full name when they login, how do I retrieve the full name based on the login ID? From Object Viewer, I found something useful, DirecotrySearcher, which allow me to set filter on the DirectoryEntry object created previously. OK, solved first problem, yet another came, what is the filter string looks like? Another wild guess (no harm from guessing), it will search by the properties, or maybe part of the key in the path. To find out list of the properties available, I will need to cycle through the DirectoryEntry’s child and print it out (day 2 of the project, which is the state I start coding, I don’t have Internet access, have to do it the hard way :S).

In order to retrieve the list of the properties, I’m doing the hard and stupid way, hard coded login for the user first (I do have a test platform):

Dim dePath As String = "LDAP://litwareinc/CN=Amy Alberts,CN=Users,DC=litwareinc,DC=com"  
Dim objDE As New DirectoryEntry(dePath, "amya", "P@ssw0rd", AuthenticationTypes.Secure)

For Each de As PropertyValueCollection In objDE.Properties  
  Response.Write(de.PropertyName & " : " & de.Value.ToString & "")  

By studying the list printed, I learn that the properties that match with the login ID will be sAMAccountName. So now I can use the DirectorySearcher to filter out those account to only to AmyA user account, by matching the sAMAccountName to AmyA (which user use to login). After search, I can retrieve the Name from the user account, which will be Amy Alberts, now I can merge my search result Name to my path, final codes will looks like this:

Imports System.DirectoryServices

Partial Class _Default Inherits System.Web.UI.Page  
  Protected Sub btnSubmit_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnSubmit.Click  
    Dim domainName As String = ConfigurationManager.AppSettings("DOMAIN").ToString  
    Dim dcName As String = ConfigurationManager.AppSettings("DC").ToString  
    Dim dePath As String = "LDAP://" & domainName & "/CN=Users,DC=" & domainName & ",DC=" & dcName

      Dim deSearch As New DirectorySearcher(New DirectoryEntry(dePath))  
      Dim result As SearchResult  
      Dim dePathChange As String  
      Dim deObj As DirectoryEntry

      deSearch.Filter = "sAMAccountName=" & Trim(txtUsername.Text)  
      result = deSearch.FindOne()  
      dePathChange = "LDAP://" & domainName & "/CN=" & result.Properties("Name").Item(0) & ",CN=Users,DC=" & domainName & ",DC=" & dcName  
      deObj = New DirectoryEntry(dePathChange, Trim(txtUsername.Text), txtPassword.Text, AuthenticationTypes.Secure)  
      deObj.Invoke("ChangePassword",New Object() {txtPassword.Text, txtConfirmPassword.Text})  

      Dim jsString As String = "alert('Password Successfully Changed!');location.replace('Default.aspx');"

      Page.ClientScript.RegisterClientScriptBlock(Me.GetType, "Successful", jsString)  
      Catch ex As DirectoryServicesCOMException  
      lblError.Text = ex.Message  
    End Try  
  End Sub  
End Class

Without having to hardcode the domain name, moving all settings to Web.config.

Finally it works!