In Part 1 of this two-part series, I showed how easy it is to read individual objects from an LDAP directory with the System.DirectoryServices namespace's DirectoryEntry class. Now, let's look at how to search with the DirectorySearcher class. The DirectorySearcher class works like many other LDAP-based
search APIs. Table 1 contains all of the DirectorySearcher properties that can be used to define search criteria. If you are familiar with LDAP, many of the properties, such as SearchScope, should look familiar.
|
Related Reading Active Directory |
Table 1. DirectorySearcher Properties
| Property Name | Description |
|---|---|
CacheResults |
Gets or sets the flag that determines if results are cached on the client side. |
ClientTimeout |
Gets or sets the time period the client is willing to wait for the server to answer the search. |
Filter |
Gets or sets the search filter string. |
PageSize |
Gets or sets the page size for paged searching. |
PropertiesToLoad |
Gets or sets the attributes to return from a search. |
PropertyNamesOnly |
Gets or sets the flag that indicates that only attribute names are to be returned from a search. |
ReferralChasing |
Gets or sets whether referrals are chased. |
SearchRoot |
Gets or sets the base from which the search should start. |
SearchScope |
Gets or sets the scope of the search. |
ServerPageTimeLimit |
Gets or sets the time the server will wait for an individual page to return from a search. |
ServerTimeLimit |
Gets or sets the time the server will wait for a search to complete. |
SizeLimit |
Gets or sets the maximum number of objects that can be returned by a search. |
Sort |
Gets or sets the attribute that is used when returning sorted search results. |
The following code shows how to search for all user objects in the rallencorp.com domain.
Dim objSearch As New DirectorySearcher()
objSearch.SearchRoot = New DirectoryEntry("LDAP://dc=rallencorp,dc=com")
objSearch.Filter = "(&(objectclass=user)(objectcategory=person))"
objSearch.SearchScope = SearchScope.Subtree
objSearch.PropertiesToLoad.Add("cn")
Dim colQueryResults As SearchResultCollection
colQueryResults = objSearch.FindAll()
Dim objResult As SearchResult
For Each objResult In colQueryResults
Console.WriteLine(objResult.Properties("cn")(0))
Next
After instantiating a new DirectorySearcher class, I set
four properties before executing the search. The SearchRoot
accepts a DirectoryEntry object representing the search
base, the Filter property is the LDAP filter string, SearchScope
is one of the values contained in the System.DirectoryServices.SearchScope
enumeration, and PropertiesToLoad.Add() builds the attribute
list to return from the query. You can specify multiple attributes in
a single statement by using PropertiesToLoad.AddRange:
objSearch.PropertiesToLoad.AddRange(New String() {"cn", "sn", "givenname"})
After all of the search parameters have been set, I can use the FindAll
method to invoke the search. A System.DirectoryServices.SearchResultsCollection
is returned by the FindAll method, and you can iterate over
each entry using a For Each loop. The SearchResultsCollection
contains System.DirectoryServices.SearchResult objects,
which are very similar to DirectoryEntry objects.
If you only want to retrieve the first object in the search results,
you can use the FindOne() method, which returns a single
SearchResult object.
Modifying objects with System.DirectoryServices can be done in
a couple of different ways. To modify an attribute that currently has a
value, you can set it using the Properties property. For
example, the following code would modify the givenName
attribute:
objADObject.Properties("givenName")(0) = "Robert"
If you want to set an attribute that was previously unset, you must use
the Properties.Add method. The following code would set
the previously unset as an attribute:
objADObject.Properties("sn").Add("Robert")
To determine if an attribute has been set, you can use Properties("<attributename>").Count,
which will return the number of values that have been set for the attribute.
Just like with ADSI, all modifications are made initially to the local
property cache and must committed to the server. With ADSI, you would
use the IADs::SetInfo method, and with System.DirectoryServices
it is called CommitChanges, which is available from the
DirectoryEntry class.
objADObject.CommitChanges()
Now that I've covered how to set an attribute, I can modify the code from
Part 1 -- which prints all of the values of an attribute -- to instead
set an attribute. The following code expects three command-line parameters;
the first is the ADsPath of the object to modify, the second
is the attribute name, and the third is the value to set the attribute to.
Dim strADsPath As String
Dim strAttrName As String
Dim strAttrValue As String
Try
Dim intArgs As Integer = Environment.GetCommandLineArgs().Length()
If intArgs <> 4 Then
Throw (New Exception("All parameters are required"))
Else
strADsPath = Environment.GetCommandLineArgs()(1)
strAttrName = Environment.GetCommandLineArgs()(2)
strAttrValue = Environment.GetCommandLineArgs()(3)
End If
Catch objExp As Exception
Console.WriteLine("Error: " & objExp.Message)
Console.WriteLine("Usage: " & Environment.GetCommandLineArgs()(0) & _
" ADsPath AttributeName Attribute Value")
Console.WriteLine()
Return
End Try
Dim objADObject As New <code>DirectoryEntry</code>()
Try
If objADObject.Exists(strADsPath) = False Then
Throw (New Exception("Object does not exist"))
End If
Catch objExp As Exception
Console.WriteLine("Error retrieving object: " & strADsPath)
Console.WriteLine("Error: " + objExp.Message)
Return
End Try
Dim strOldValue As String
Try
objADObject.Path = strADsPath
If objADObject.Properties(strAttrName).Count > 0 Then
strOldvalue = objADObject.Properties(strAttrName)(0)
objADObject.Properties(strAttrName)(0) = strAttrValue
Else
objADObject.Properties(strAttrName).Add(strAttrValue)
End If
objADObject.CommitChanges()
Catch objExp As Exception
Console.WriteLine("Error setting object: " & strADsPath)
Console.WriteLine("Error: " + objExp.Message)
Return
End Try
Console.WriteLine(strADsPath)
Console.WriteLine("Attribute: " + strAttrName)
Console.WriteLine("Old value: " + strOldValue)
Console.WriteLine("New value: " + strAttrValue)
Console.WriteLine()
Console.WriteLine("Update Successful")
This code is not that different than the example program from earlier. The main difference is the check for additional command-line parameters, and determination if the attribute that was specified on the command-line has been set previously or not.
Adding objects with System.DirectoryServices is similar
in nature to ADSI. You must first get a reference to the parent object,
and then add a child. You can add a child by using the Children.Add
method of a DirectoryEntry object. The following example
shows how to create a user object:
Dim objParent As New DirectoryEntry("LDAP://ou=sales,dc=rallencorp, _
dc=com", _
"administrator@rallencorp.com",_
"MyPassword", _
AuthenticationTypes.Secure)
Dim objChild As DirectoryEntry = objParent.Children.Add("cn=jdoe", _
"user")
objChild.Properties("sAMAccountName").Add("jdoe")
objChild.CommitChanges()
objChild.NativeObject.AccountDisabled = False
objChild.CommitChanges()
Console.WriteLine("Added user")
You may have noticed several things. First, when I instantiated the DirectoryEntry
object, I passed three additional parameters that I haven't used before.
The second parameter is the user to authenticate with, the third is
the password for the user, and last is any authentication options from
the AuthenticationTypes enumeration (ADS_AUTHENTICATION_ENUM
in ADSI).
After the first CommitChanges call, the object is created
in the directory. After that, I enable the account by calling ADSI's
AccountDisabled method. System.DirectoryServices
does not duplicate all of the functionality of ADSI. As I said earlier,
it is primarily a wrapper around ADSI.
One of the reasons System.DirectoryServices is so powerful
is because you can still access native ADSI interfaces by using the
NativeObject method. NativeObject will return
the IADs interface of the specific type of object. In our previous example,
NativeObject will return an IADsUser object,
on which I can then call the IADsUser::AccountDisabled
method. A final CommitChanges call will update the directory
and enable the account.
To use the NativeObject method, you'll need to add a reference
to the ActiveDs.dll library. From VS.NET, select Project->Add Reference
from the menu. Click the COM tab, click Active DS Type Library under
Component Name, and click the Select button. Click OK to close the window.
This concludes our introduction to the .NET Framework and the System.DirectoryServices
namespace. The information I've covered should be sufficient to get
you started writing directory-enabled applications with .NET, but if
you need additional information, check out MSDN,
which contains detailed documentation on the .NET class library, including
System.DirectoryServices.
O'Reilly & Associates recently released (April 2003) Active Directory, 2nd Edition.
Sample Chapter 14, "Upgrading to Windows Server 2003," is available free online.
You can also look at the Table of Contents, the Index, and the full description of the book.
For more information, or to order the book, click here.
Robbie Allen is the coauthor of Active Directory, 2nd Edition and the author of the Active Directory Cookbook.
Return to ONDotnet.com
Copyright © 2009 O'Reilly Media, Inc.