ONDotNet.com    
 Published on ONDotNet.com (http://www.ondotnet.com/)
 See this if you're having trouble printing code examples


Introduction to System.DirectoryServices, Part 1

by Robbie Allen, coauthor of Active Directory, 2nd Edition
07/28/2003

The .NET Framework System.DirectoryServices namespace is a robust and flexible API for querying and manipulating objects in an LDAP directory, such as Microsoft's Active Directory. The System.DirectoryServices namespace contains several classes, many of which are built on top of ADSI (Active Directory Service Interfaces). If you are already familiar with ADSI, the learning curve for understanding the System.DirectoryServices classes will be pretty minimal.

The two main classes within System.DirectoryServices are DirectoryEntry and DirectorySearcher. The DirectoryEntry class represents an object in the directory and can be used to create new objects and manage existing ones. The DirectorySearcher class is the primary interface for searching the directory. It is a simple interface that contains properties for all the typical search options you need when performing LDAP queries.

Active Directory

Related Reading

Active Directory
By Robbie Allen, Alistair Lowe-Norris

In Part 1 of this article, I'll cover the DirectoryEntry class and show examples of how to iterate over the attributes of an object. Next week in Part 2, I'll cover the DirectorySearcher class and show examples of how you can modify objects. I'll touch on some of the other System.DirectoryServices classes, but these are the two main ones you should become familiar with. For the complete list of System.DirectoryServices classes, check out the .NET section of MSDN.

DirectoryEntry Basics

The DirectoryEntry class contains several properties to access the attributes of LDAP objects. The following code shows how to display the currentTime attribute of the RootDSE:


Dim objRootDSE As New DirectoryEntry("LDAP://RootDSE")
Console.WriteLine(objRootDSE.Properties("currentTime")(0))

Once you instantiate the DirectoryEntry object, you can access the currentTime attribute by passing it to the Properties property. Properties actually returns a collection of values for the attribute in the form of a PropertyCollection class, which is why you needed to specify an index of 0 to get at the first value. If the currentTime attribute was multi-valued, you can get at the other values by incrementing the index to 1 and so on.

Now let's look at how to display all of the values for the attributes of an object. In this example, I will target the RootDSE.


Dim objRootDSE As New DirectoryEntry("LDAP://RootDSE")

Dim strAttrName As String
Dim objValue As Object
For Each strAttrName In objRootDSE.Properties.PropertyNames
    For Each objValue In objRootDSE.Properties(strAttrName)
        Console.WriteLine(strAttrName & " : " & objValue.ToString)
    Next objValue
Next strAttrName

As you can see, Properties, which returns a PropertyCollection, has a PropertyNames property that returns a collection of attribute names for the object. I looped over each attribute and then looped over each value for that attribute to ensure all values were printed for single and multi-valued attributes. The ToString property converts whatever value is stored in the attribute to a printable string.

There are several properties available in the DirectoryEntry class. Table 1 contains a list of them.

Table 1. DirectoryEntry properties

Property Name Description
AuthenticationType Gets or sets the type of authentication to use when accessing the directory.
Children Gets a DirectoryEntries class which contains the child objects of this object.
Guid Gets the GUID for the object (e.g. in Active Directory the objectGUID attribute).
Name Gets the relative distinguished name of the object.
NativeGuid Gets the GUID of the object as returned by the provider.
NativeObject Gets the native ADSI object.
Parent Gets the object's parent in Active Directory.
Password Gets or sets the password to use when authenticating.
Path Gets or sets the ADsPath for the object.
Properties Gets a PropertyCollection class containing the attributes on the object.
SchemaClassName Gets the objectclass of the object.
SchemaEntry Gets DirectoryEntry class of the object's objectclass.
UsePropertyCache Gets or sets the flag indicating if the property cache should be committed after each operation.
Username Gets or sets the username to use when authenticating.

One interesting property to note is Children, which returns a DirectoryEntries collection containing each child object. Using the Children property you can quickly traverse a directory tree. The following code prints out the entire directory tree rooted at dc=rallencorp,dc=com:


Sub Main()
    Dim objADObject As New DirectoryEntry("LDAP://dc=rallencorp,dc=com")
    DisplayChildren(objADObject, " ")
End Sub

Sub DisplayChildren(ByVal objADObject As DirectoryEntry, _
                    ByVal strSpaces As String)
    Console.WriteLine(strSpaces & objADObject.Name)

    Dim objChild As New DirectoryEntry()
    For Each objChild In objADObject.Children
        DisplayChildren(objChild, strSpaces & "  ")
    Next objChild
End Sub

The DisplayChildren subroutine is recursive. For each child that is found, DisplayChildren is called and so on until childless nodes are found. The strSpaces variable is used to indent each child so that you can see the hierarchy when printed out.

Now, let's say that I wanted to traverse the tree, but only print out the Organizational Units (OU). To do that, I can use the SchemaClassName property for each object and print out only the entry if it's SchemaClassName equals organizationalUnit, which is the objectClass value for OUs.


Sub Main()
    Dim objADObject As New DirectoryEntry("LDAP://dc=rallencorp,dc=com")
    DisplayChildren(objADObject, " ")
End Sub

Sub DisplayChildren(ByVal objADObject As DirectoryEntry, _
                    ByVal strSpaces As String)
    If objADObject.SchemaClassName = "organizationalUnit" Then
       Console.WriteLine(strSpaces & objADObject.Name)
    End If

    Dim objChild As New DirectoryEntry()
    For Each objChild In objADObject.Children
        DisplayChildren(objChild, strSpaces & "  ")
    Next objChild
End Sub

I'm now going to take many of the concepts described so far and make a fully functional program. Let's expand on the first example I covered, which printed the attributes and values of the RootDSE. I'm going to turn it into a program that can accept a command-line argument, which should be the ADsPath of an object, and then display all of the attributes and values for that object. Here is the code:


Imports System
Imports System.DirectoryServices

Module Module1
    Sub Main()
        Dim cmd As String

        ' Read the commandline and get the number of arguments passed
        Dim intArgs As Integer
        Try
            intArgs = Environment.GetCommandLineArgs().Length()
        Catch exp As Exception
           ' Set intArgs to 0 if no arguments were passed
            intArgs = 0
        End Try

        ' If an argument was specified on the commandline, set
        ' strADsPath to that, if not default to query the RootDSE
        Dim strADsPath As String
        If intArgs > 1 Then
            strADsPath = Environment.GetCommandLineArgs()(1)
        Else
            strADsPath = "LDAP://RootDSE"
        End If

        ' I need to see if the object in strADsPath exists
        ' and if not, print an error and return
        Dim objADObject As New DirectoryEntry()
        Try
            If objADObject.Exists(strADsPath) = False Then
                Throw (New Exception("Object does not exist"))
            End If
        Catch exp As Exception
            Console.WriteLine("Error retrieving object: " & strADsPath)
            Return
        End Try

        ' Iterate over each attribute of the object and print its values
        Dim strAttrName As String
        Dim objValue As Object
        Try
           objADObject.Path = strADsPath
           Console.WriteLine("Displaying " & objADObject.Path)
           For Each strAttrName In objADObject.Properties.PropertyNames
               For Each objValue In objADObject.Properties(strAttrName)
                   Console.WriteLine(strAttrName & " : " & objValue.ToString)
               Next objValue
           Next strAttrName
        Catch exp As Exception
            Console.WriteLine("Fatal error accessing: " & strADsPath)
            Return
        End Try

    End Sub
End Module

The first two lines, which use the Imports keyword, allow you to specify class names contained within those namespaces without fully qualifying them. For example, by using Imports I can use the following code:


	New DirectoryEntry()

instead of:


	New System.DirectoryServices.DirectoryEntry()

For simplicity, I put the rest of the code directly in the Main subroutine. The first part of the code attempts to read the command line using the System.Environment namespace to see if a parameter was specified. A Try Catch statement was used because the call to Environment.GetCommandLineArgs.Length will throw an exception if no parameters are passed on the command line.

Note that the intArgs variable will contain the number of arguments passed to the script including the script name as the first argument. To see if the user actually passed the ADsPath, I have to check if intArgs > 1. Next I set the strADsPath variable to the value specified on the command line, and if one wasn't specified, default to the RootDSE. Next I use the Exists method to determine if the object specified in strADsPath actually exists.

The DirectoryEntry class contains a host of methods in addition to the properties I showed earlier. Table 2 contains a list of all the DirectoryEntry methods.

Table 2. DirectoryEntry methods

Method Name Description
Close Closes the DirectoryEntry and releases any system resources associated with the component
CommitChanges Saves any changes to the object in Active Directory (similar to SetInfo)
CopyTo Creates a copy of the object
DeleteTree Delete the object and any children
Equals Determines if two objects are the same
Exists Determines if the object exists in Active Directory
Invoke Allows you to invoke a native ADSI method
MoveTo Move an object to a different location
RefreshCache Refresh the property cache for the object
Rename Rename the relative distinguished name of the object
ToString String representation of the object

If the Exists check fails, I generate an exception using Throw. If the object exists, I proceed to iterate over each attribute, printing the values for it. To turn the code into an executable, you can compile the program by selecting Build -> Build Solution from the VS.NET menu. If any errors were found they would be displayed in the bottom pane. If none were found, you can then execute the program. If I named the project EntryQuery, an example command line would look like the following:


D:\VSProjs\EntryQuery\EntryQuery\bin> entryquery.exe LDAP://dc=rallencorp,dc=com

Next week, in Part 2 of this series, I'll show how to use the DirectorySearcher class and how to modify attributes.

Robbie Allen is the coauthor of Active Directory, 2nd Edition and the author of the Active Directory Cookbook.


O'Reilly & Associates recently released (April 2003) Active Directory, 2nd Edition.


Return to ONDotnet.com

Copyright © 2009 O'Reilly Media, Inc.