views:

38

answers:

1

I'm struggling a bit with my simple console-dump-program. I connect to AD using

DirectoryEntry entry =
    new DirectoryEntry("LDAP://" + domain, username, password);

and from there I recursively loop thru every child by

foreach (DirectoryEntry child in entry.Children)
{
    Traverse(child);
}

Then I start getting mambo jambo data, users popping up more then once and null objects so I wonder if the way I handle the AD that it is just a handle and not a copy so its not loaded completely when I start traversing it?

Any tips/pointers on what to do?

+2  A: 

If you can, move up to .NET 3.5 and use the new System.DirectoryServices.AccountManagement namespace - much easier to use.

See: Managing Directory Security Principals in the .NET Framework 3.5

Also: you need to understand Active Directory isn't just a flat list of users and groups - it's a hierachical system of OU's (organizational units) that can be nested into one another, and can contain users, groups, computers and more.

So what exactly do you want to do?? Get the users for a given OU (e.g. "Sales")?? Or really get all users from your AD?? You do understand this could take quite a while, depending on the size of your company's AD.......

If you really want to get ALL users and ALL groups from your entire AD - you should probably set up a DirectorySearcher at the root level:

// set search root
DirectoryEntry deRoot = new DirectoryEntry("LDAP://dc=YourCompany,dc=com");

// declare directory searcher
DirectorySearcher dsUsers = new DirectorySearcher(deRoot);

// scope is full subtree, filter defines to search for users
dsUsers.SearchScope = SearchScope.SubTree;
dsUsers.Filter = "(objectCategory=person)";

// define what properties you want to have loaded into your search results
dsUsers.PropertiesToLoad.Add("givenName");
dsUsers.PropertiesToLoad.Add("surname");
dsUsers.PropertiesToLoad.Add("samAccountName");

// loop through results of search
foreach(SearchResult result in dsUsers.FindAll())
{
   if(result.Properties["givenName"] != null)
      string givenName = result.Properties["givenName"][0].ToString();

   if(result.Properties["surname"] != null)
      string surname = result.Properties["surname"][0].ToString();

   if(result.Properties["sAMAccountName"] != null)
      string samAccountName = result.Properties["sAMAccountName"][0].ToString();
}

When reading out the properties of your SearchResult, you need to check to make sure you did actually get a value back - otherwise your assignment will crash and burn....

For the groups, just use this filter instead:

dsUsers.Filter = "(objectCategory=group)";

If you can narrow your search, e.g. to a given OU, you can get much better performance, since the search tree gets smaller and thus the search would be a lot faster. To do so, just define a different LDAP path for your deRoot directory entry (e.g. LDAP://OU=Sales,DC=YourCOmpany,DC=com or whatever OU you want to search in).

Update: as I said - with .NET 3.5, it gets a lot easier still! You need to add a reference to System.DirectoryServices.AccountManagement, and then you can use code something like this using a sort of "query-by-example" approach:

// create a domain context for the current domain
PrincipalContext domain = new PrincipalContext(ContextType.Domain);

// create a principal object decsribing what to search for
UserPrincipal user = new UserPrincipal(domain);
user.IsActive = true;

// create a principal searcher for running a search operation
PrincipalSearcher searcher = new PrincipalSearcher(user);

// run the query
PrincipalSearchResult<Principal> results = searcher.FindAll();

// iterate over all results
foreach (Principal result in results)
{
    Console.WriteLine("name: {0}", result.Name);
}

And for searching for groups, just instantiate a GroupPrincipal, set any properties on it and then pass that into the PrincipalSearcher to search for groups.

marc_s
I want to get ALL users :). Its not that big, about 100 users. But is this thread safe? I want to put this in a timer and check for changes every hour, would it still be safe?
Jason94
@Jason94: I would definitely **NOT** run this every hour.... doesn't once or twice a day suffice? AD queries across the entire domain aren't exactly very quick and cheap (in terms of performance and all)... also: are you on .NET 3.5 or could you move to 3.5 ?? Would be a **lot** easier in 3.5.....
marc_s
@marc_s: And by the way, how do i figure out SearchResult.Properties? They are not listed if they dont contain any data, but is there a list of all possible properties? I moved to .NET 3.5 and your code works great!
Jason94
@Jason94: check out Richard Mueller's web site at http://rlmueller.net/UserAttributes.htm - excellent resources on all available AD attributes and so forth.
marc_s
@Jason94: if you use the .NET 3.5 version - those should be properties on the UserPrincipal objects in your list of results. For the .NET 2.0 solution: if you want Phone or Email in your SearchResult.Properties, you need to define those when setting up the DirectorySearcher - see the .PropertiesToLoad.Add() method calls - just add your additional properties you need/want to read out.
marc_s
@Jason94: if you're serious about AD programming, you should definitely check out the "The .NET Developer's Guide to Directory Services Programming" book - has lots of useful and helpful stuff in there! http://www.amazon.com/Developers-Guide-Directory-Services-Programming/dp/0321350170/
marc_s
Im serious, but i just need to hack together one small app :D Figured out the UserPrincipal, the only thing im missing is getting a users mobile number, any clues to where its hiding?
Jason94
@marc_s: Well.. after some googling i found that you have to extend UserPricipal to get mobile number: http://tinyurl.com/35ymj25, but i get "Principal objects of type ADGrabber.NET3._5.UserPrincipalEx can not be used in a query against this store." on searcher.FindAll()
Jason94