Active Directory Information for .NET Programmers
What Is It?
Network folks have been talking about how great LDAP is for years (Lightweight
Directory Access Protocol for all of you that need those acronyms spelled out).
We developers have been busy doing other things and have mostly ignored what
Active Directory (AD) could do for us. The result is applications that are not
very network aware.
As a developer, I have been much more inclined to develop a new HR database
for an application then to pull the existing information from Active Directory.
Redundant data is bad.
You can also use LDAP to make new friends. You can do most administrative tasks
with a little code, helping the network folks out. This comes in really handy
when you forwarded that offensive joke to your boss by accident and need them
to delete it. You can trade for an application that adds users.
Where Do I Start?
Whenever you connect to AD, you need to start somewhere. I am going to run
a program against an OU (Organizational Unit) called WST in a domain called
NWTraders.msft. In front of everything, we put LDAP:// (case sensitive) so our
program knows what provider to use.
This will look like:
LDAP://OU=WST,DC=NWTraders,DC=msft
Too hard? Just ask the network folks where to start. Of course, you are bound
to get a "What are going to do with my network?" response. I recommend
slipping them a twenty. If all else fails, start with the very root of the domain
(my case "LDAP://DC=SQLSoft,DC=com") for the sqlsoft.com domain.
For all my examples, I have a textbox called "txtAdsPath" where I
put the path.
NOTE: The entire code can be downloaded for free if you have an account on
My SQLSoft. (Accounts can be created free.) My entire
code can be downloaded here:
I also made an active directory browser. I hope that it will help with learning
the structure of you domain. You can download
my AD browser here:
Just the Facts
Let's get somebody and make them spill a little info. As a general rule (I
say "general" for all the Active Directory purists out there), we
can see all the information from the AD we are logged into.
First grab an item... any item. I use a textbox called txtAdsPath for the path
to our object so that we can try out many of them. I also use a listbox called
lstResult to display the results. lstResult.Items.Add just adds a new line to
the listbox.
DirectoryEntry is an entry in Active Directory. If we pass in a path when you
create a DirectoryEntry, you get that entry. Pretty cool! I display the name
("OU=LuckyUsers" for "LDAP://OU=LuckyUsers,OU=Hawaii, DC=SQLSoft,DC=com"),
the GUID (remember that big numbers make you look smart), the ADsPath (matches
what you typed in), and finally a count of the properties.
C#

VB.NET

All the Facts
Let's go a step further and list all of the properties. The actual list of
properties of an object in Active Directory can change. The Active Directory
has a schema that defines each object. Those very select few have the right
to change that schema.
If you want to have a little fun, go to the network folks and insist that your
account needs to be added to the Schema Admins Group. Better yet, say you are
working on a Web page and need the ASP.NET account added to Schema Admins. Then
run.
Here I list all the properties for an object. Remember that I gave you the
browser above.
C#

VB.NET

This code is just like the last example except for a loop through all the properties.
We quickly notice that not all properties are meaningful. Many are not being
used or for other reasons, not returning a simple value.
All My Children
AD is hierarchal. (Me love big words.) An object, like an OU, may have children
(the groups and users in that OU). A fun way to spend the day (OK, just fun
for me) is to start with the top level domain and list the children, then the
children of the children, and so on.
For my next trick, I will modify the code from above (higher in the article,
I mean), to list all the children from an object. This is much more interesting
if the object has children (like an OU).
C#

VB.NET

Where Is It?
By now we have a little idea about how to get information out of AD, but sometimes
we don't even know where we are. Unless you are the network person who set the
whole thing up or you actually read documentation, you probably need to go searching
for the objects you care about.
We still grab a DirectoryEntry object, but then we pass it off to a DirectorySearcher
object along with a filter. (I use a textbox called txtFilter.)
This time the DirectoryEntry is pointing to our starting point of the search,
not the object we are looking for. Maybe we only want to look in a particular
OU so we may start in that OU.
The filter is a little hard to explain. We have a couple magic characters.
& And
| Or
! Not
* Wildcard
The use of parentheses is a little odd. We group with parentheses. If I had
two things I was looking for, I would group each criterion with parentheses.
Then I would put them both in an outer set with a "&" as the first
character inside.
You can also use wildcards...
- Everyone who's firstname, for example, starts with M:
(givenName=M*)
You can add two things together...
You can show off...
- All users with a telephone... (Sorry to the Amish):
(&(objectClass=user)(telephoneNumber=*))
- All users without a telephone:
(&(objectClass=user)(!(telephoneNumber=*)))
- All users in Washington or Oregon:
(&(objectClass=user)(|(st=WA)(st=OR)))
Remember that | (pipe) is that thing looks on the keyboard like a colon except
with little lines instead of dots. (Pop quiz later.)
Okay, enough about the filter. Let's look at actually running the search. The
way I have set up the code, I have assumed that I might return more than one
thing. Thus I have used the FindAll method instead of the FindOne method, which
is for looking for something unique. Also note that the filter is in a textbox
called txtFilter.
C#

VB.NET

Are You Somebody?
Sometimes we want to see if someone is valid. I started working on this because
someone asked a pretty simple question... "I want to use ASP.NET forms-based
authentication, but I want to use the domain username and password for employees
and a custom database for anonymous Web users." That is really not a simple
question, but here was the toughest part... "Check a username and password
against Active Directory first."
Here's how this goes:
- Grab a directory entry object, but this time, pass in the username and
password.
- Set up a filter looking for that person. (The path in Step 1 should be
an object that contains the user; make it the root of the domain to be safe.)
- Try it and see if it blows up with an exception.
C#

VB.NET

Changing the World
Believe it or not, you can't change, add, or delete things unless you have
the rights to do so. If you only care about reading AD and not changing it,
you can skip this section. (I won't tell your boss.) Spend the time instead
by looking a pretty pictures of airplanes (http://www.airliners.net).
There is a great saying from the sixties... "If you are not part of the
solution, you might as well enjoy being part of the problem." (I might
be a little off.) Let's be part of the problem and start messing things up in
AD. I am, of course, kidding. You need a development environment to work with.
Try looking up "Active Directory Application Mode" (ADAM) in Technet.
Here is a hyplerlink that will probably change before this arcticle gets published:
(http://www.microsoft.com/windowsserver2003/adam/default.mspx).
Adding a New User
I choose the hardest thing here just because it will illustrate all we have
to do. If you only want change something that already exists, just grab the
object, change the properties, then call commitchanges. Instead, adding a user
requires a bit more work.
Here are the basic steps:
- Get the object you want to add the new item in (oOU).
- Add to the children of the thing you made in Step 1. Note that we have to
give the new user a name (like "cn=John.Doe") and also tell AD what
type of thing it is ("user").
- Set up some properties. Note that the syntax is a little odd. You Add(value)
to the Properties(propertyName). If we where changing an existing item, we
would just assign the property a new value.
- Then we save it. Calling the method Save would have made sense so instead
they decided to call it CommitChanges as if we are doing some fancy database
thing.
If we where adding an OU or group we would be done. With a user, a little
more work is needed. We can only enable a user and set the password after
the user is saved back to AD.
- Invoke the setPassword method, passing it the password ("P@ssw0rd").
- Cast the object to a COM interface just to set the AccountDisabled = false
(yuck). They just didn't expose the AccountDisabled method in .NET.
a) Set a reference to the COM AD library.
b) The oNewUser object has a NativeObject Property that gives you access to
the COM active directory object(IADsUser) that scripters have been using for
years. This object has the AccountDisable property that you set to false.
Disabled = false would be enabled. (English teachers don't make computer programs,
or least not ones with double negatives.) Here is what the
IADsUser class looks like in the object browser.
- Save it again (CommitChanges)
CS Example
VB.NET

Final Notes
What's error handling? Yes I know, these programs desperately need error handling
in them. I left it out for one reason... to keep it simple.
I hope I have demystified the System.DirectoryServices a little. My point certainly
wasn't to show you everything but enough to get started.
As always, if there is any questions and/or errors, please e-mail me:
.
|