I have this AD server "adserver.europe.company.com".
I know there are some ... "branches". "DC=europe,DC=company,DC=com","DC=africa,DC=company,DC=com", "DC=asia,DC=company,dc=com" etc.
If I query the server (using the DirectorySearcher class) with SearchScope = OneLevel and an empty base asking only for the distinguishedName I get only entries ending in "DC=europe,DC=company,DC=com".
However, if I use "DC=africa,DC=company,DC=com" as base, I do get the entries ending in "DC=africa,DC=company,DC=com".
How can I get all the branches for this AD server?
I managed to obtain all the partitions this way:
DirectoryContext aContext = new DirectoryContext( DirectoryContextType.DirectoryServer, LDAPServer, LDAPUser, LDAPPassword );
Forest aForest = Forest.GetForest( aContext );
foreach ( ApplicationPartition aPartition in aForest.ApplicationPartitions )
{
...
}
Related
OK, working with active directory, my one remaining requirement is to pull back all the users in a given group and get their details - first name, last name, username, email.
Having read up around this I was surprised to discover that there doesn't seem to be a way of doing this in an efficient fashion. I've worked out two ways of getting the results, but both seem absurdly wasteful.
The first is to search members of a group, like this:
DirectoryEntry searchRoot = new DirectoryEntry("LDAP://" + server, username, password);
DirectorySearcher search = new DirectorySearcher(searchRoot);
search.Filter = string.Format("(|(CN={0})(CN={1}))", "Group1", "Group2");
search.PropertiesToLoad.Add("member");
SearchResultCollection results = search.FindAll();
Which brings back a single result containing usernames, and usernames only. You can dissect this into an array of individual usernames, but then to get the details of each user you have to search AD again on a per-name basis.
The other approach is to do this:
DirectoryEntry searchRoot = new DirectoryEntry("LDAP://" + server, username, password);
DirectorySearcher search = new DirectorySearcher(searchRoot);
search.Filter = string.Format("(&(&(objectClass=user)(objectCategory=person))(memberOf=*))");
search.PropertiesToLoad.Add("memberOf");
search.PropertiesToLoad.Add("name");
search.PropertiesToLoad.Add("mail");
search.PropertiesToLoad.Add("displayName");
SearchResultCollection mySearchResultColl = search.FindAll();
foreach (SearchResult result in mySearchResultColl)
{
foreach (string prop in result.Properties["memberOf"])
{
if (prop.Contains("Group1") || prop.Contain("Group2"))
{
//add user to list
}
}
}
Which gets me what I want but involves retrieving every active directory user and then iterating through the collection to find ones that match. That works fine on my little test directory, but I shudder to think how slow it's going to be on a system with thousands of users.
I'm aware you can do what I need using the PrinicpalContext object, but as best I can tell that only works if the code is running in the same domain, which I can't guarantee. I need to be able to query across domains.
Is there a better way of doing this? Or am I just going to have to suck on the performance issues?
If you only need to find direct members, you can use Attribute Scope Query (ASQ).
This requires domain/forest functional level of 2003 (forgot domain or forest).
DirectoryEntry groupEntry = new DirectoryEntry("LDAP://<server>/<group DN>", "user", "pwd");
DirectorySearcher searcher = new DirectorySearcher(groupEntry);
searcher.SearchScope = SearchScope.Base;
searcher.AttributeScopeQuery = "member";
searcher.Filter = "(&(objectCategory=person)(objectClass=user))";
searcher.PropertiesToLoad.Clear();
searcher.PropertiesToLoad.Add("name");
searcher.PropertiesToLoad.Add("mail");
searcher.PropertiesToLoad.Add("displayName");
foreach (SearchResult result in searcher.FindAll())
{
Console.WriteLine(result.Path);
}
For nested group members, you may use the LDAP_MATCHING_RULE_IN_CHAIN matching rule.
This requires domain/forest functional level of 2008 R2 (again, forgot domain or forest).
DirectoryEntry rootEntry = new DirectoryEntry("GC://<server>", "user", "pwd");
DirectorySearcher searcher = new DirectorySearcher(rootEntry);
searcher.SearchScope = SearchScope.Subtree;
searcher.Filter = "(&(objectCategory=person)(objectClass=user)(memberOf:1.2.840.113556.1.4.1941:=<group DN>))";
searcher.PropertiesToLoad.Clear();
searcher.PropertiesToLoad.Add("name");
searcher.PropertiesToLoad.Add("mail");
searcher.PropertiesToLoad.Add("displayName");
foreach (SearchResult result in searcher.FindAll())
{
Console.WriteLine(result.Path);
}
Limitations:
Both methods don't handle primary group membership
ASQ don't work across domain, an exception will be thrown if member from another domain is found. So normally you can only perform this for global groups.
You could utilize an ANR search for some of those attributes For more information, see this article - the functionality has been there since 2000:
http://support.microsoft.com/kb/243299
In order to search for other attributes not in the default set, you'd need to tweak the schema (which may not be suitable in your situation).
Looking for LDAP query to get only those OUs from Active Directory having group in it.
most important is only using LDAP query, I don't want to filter each OU using C# code.
Thanks
Groups can be stored in organizationalUnits but also in domain, containers.
Using DirectoryEntry or AccountManagement you can do the following :
Find all the groups from the domain root
Foreach group add the container property to a list of OUs
Get unique entries from the list of OUs
Here is a solution using System.DirectoryServices.AccountManagement and System.DirectoryServices
/* Retreiving a principal context
*/
PrincipalContext domainContext = new PrincipalContext(ContextType.Domain, "WM2008R2ENT:389", "dc=dom,dc=fr", "jpb", "PWD");
/* Look for all the groups from the root
*/
GroupPrincipal allGroups = new GroupPrincipal(domainContext);
allGroups.Name = "*";
/* Bind a searcher
*/
PrincipalSearcher searcher = new PrincipalSearcher();
searcher.QueryFilter = allGroups;
PrincipalSearchResult<Principal> hRes = searcher.FindAll();
/* Read The result
*/
List<DirectoryEntry> listContainerWithGroups = new List<DirectoryEntry>();
foreach (GroupPrincipal grp in hRes)
{
DirectoryEntry deGrp = grp.GetUnderlyingObject() as DirectoryEntry;
if (deGrp != null)
listContainerWithGroups.Add(deGrp.Parent);
}
/* Get Unique Entries
*/
var listContainerWithGroupsUnique = from o in listContainerWithGroups
group o by o.Properties["distinguishedName"].Value into dePackets
select dePackets.First();
foreach (DirectoryEntry deTmp in listContainerWithGroupsUnique)
{
Console.WriteLine(deTmp.Properties["distinguishedName"].Value);
}
This isn't possible with a single search. You'll need to grab each OU and then do a one-level search of that OU for (&(objectCategory=group)(objectClass=group)). This is not going to be particuarly efficient when you consider how many searches you might need to perform. Also consider whether or not you need to handle the scenario where you have OU=A\OU=B. If OU=B includes the group, do you include OU=A (the parent)?
As I'm not very experienced in terms of retrieving information from the Active Directory I hope to get some pointers into the right direction.
I'd like to list all ous (organizational units) below a specified ou, but unfortunately I don't know how to get things working.
Let's say that the structure in our AD is as follows
SomeStartingPoint
|-MySpecifiedOuName
|-SubOuName1
|-SubOuName2
What I've got so far is
DirectoryEntry rootDSE = new DirectoryEntry( "LDAP://RootDSE" );
string defaultNamingContext = rootDSE.Proeprties[ "defaultNamingContext" ].Value.ToString();
DirectoryEntry entry = new DirectoryEntry( "LDAP://" + defaultNamingContext );
DirectorySearcher ouSearch =
new DirectorySearcher( entry.Path ) { Filter = "(objectCategory=organizationalUnit)", SearchScope = SearchScope.Subtree };
ouSearch.PropertiesToLoad.Add( "name" );
ouSearch.PropertiesToLoad.Add( "adspath" );
SearchResultCollection allOUs = ouSearch.FindAll();
Now I can loop over the allOUs and access .Properties[ "name" ][ 0 ] and .Properties[ "adspath" ][ 0 ] to list the values for all OUs.
Now when I try to use a different filter as in Filter = (&(objectCategory=organizationalUnit)(ou=MySpecifiedOuName)), I do get the single entry corresponding to exactly MySpecifiedOuName, but not the underlying OUs below, even though they contain MySpecifiedOuName within their path. This is probably down to the fact that in my example I query the wrong thing (the OU directly), but I don't know any other way.
Any ideas?
Just try with this filter :
"(objectCategory=CN=Organizational-Unit,CN=Schema,CN=Configuration,DC=dom,DC=fr)"
Adapted to your domain with configurationNamingContext from the RootDSE for CN=Configuration,DC=dom,DC=fr
This may be explained by the fact that objectCategory is a distinguich name, I know that Microsoft tools are making the translation but it seems not to work for you here.
-----Edited-----
As #Desmond insist on the fact "(objectCategory=organizationalUnit)" I just test it an it works. "(objectCategory=CN=Organizational-Unit,CN=Schema,CN=Configuration,DC=dom,DC=fr)" also works.
DirectoryEntry deBase = new DirectoryEntry("LDAP://WM2008R2ENT:389/dc=dom,dc=fr", "jpb", "Pwd");
/* Directory Search
*/
DirectorySearcher dsLookForOUs = new DirectorySearcher(deBase);
dsLookForOUs.Filter = "(objectCategory=organizationalUnit)";
dsLookForOUs.SearchScope = SearchScope.Subtree;
dsLookForOUs.PropertiesToLoad.Add("cn");
dsLookForOUs.PropertiesToLoad.Add("ou");
SearchResultCollection srcOUs = dsLookForOUs.FindAll();
foreach (SearchResult srOU in srcOUs)
{
Console.WriteLine("{0}", srOU.Path);
}
The only way to do what you want is in a recursive fashion. ou is the RDN attribute for the OU (much like CN is for users). Thus doing a search for ou=foo will only get you OUs with their OU attribute set to foo.
In order to walk the chain, you're going to need to do a search for all the OUs at the current level (use a OneLevel search instead of SubTree), and then recurse through there. This is not at all efficient, though, since you're going to be issuing numerous queries to AD.
Instead, you could do what you're doing and then build your hierarchy by sorting based on DN/depth. This is more complex but it will be more efficient from a resource access point of view.
You most likley are only missing the subtree option:
ouSearch.SearchScope = SearchScope.Subtree;
I'm trying to get a list of users from the Active Directory, who have a specified manager.
I used the following LDAP filter without success:
(manager=CN=Misterboss_n*)
However, it returns no result. Users have the following value in the manager attribute:
"CN=Misterboss_n,OU=xyz user,DC=xyz,DC=local"
What am I doing wrong? If I replace the above filter with something like this:
(givenName=John*)
it works okay (returns all users whose given name is John).
Wider context:
public List<ADUserDetail> GetAllEmployeesUnderMisterboss()
{
List<ADUserDetail> userlist = new List<ADUserDetail>();
string filter = "";
_directoryEntry = null;
DirectorySearcher directorySearch = new DirectorySearcher(SearchRoot);
directorySearch.Asynchronous = true;
directorySearch.CacheResults = true;
filter = "(manager=CN=Misterboss_n*)";
directorySearch.Filter = filter;
SearchResultCollection userCollection = directorySearch.FindAll();
foreach (SearchResult users in userCollection)
{
DirectoryEntry userEntry = new DirectoryEntry(users.Path, LDAPUser, LDAPPassword);
ADUserDetail userInfo = ADUserDetail.GetUser(userEntry);
userlist.Add(userInfo);
}
return userlist;
}
Thanks for the help!
I don't think there is a start-of-field search available for DN-typed properties. You will have to use the full DN of the manager. If you don't know the full DN, find the manager's LDAP object first and use its distinguishedName property.
Be sure to escape the DN value properly before building your filter - not every character that is valid in a DN is also valid in an LDAP filter expression:
* as \2a
( as \28
) as \29
\ as \5c
NUL as \00
/ as \2f
For code samples, see this related thread where I answered a very similar question: Getting all direct Reports from Active Directory
I've an Active Directory with domain myDomain.local, under it there exists a Distribution Group that contains many groups.
How can I read (programmatically) all these subgroups to retrieve a list of their names ?
And how to optimize the query to filter the result so that it just retrieves all the groups that ends with the word Region ?
BTW, I'm using C#.Net, ASP.Net and sharepoint, and i'm not experienced with AD.
If you're on .NET 3.5 (or can upgrade to it), you can use this code using the System.DirectoryServices.AccountManagement namespace:
// create the "context" in which to operate - your domain here,
// as the old-style NetBIOS domain, and the container where to operate in
PrincipalContext ctx = new PrincipalContext(ContextType.Domain, "YOURDOMAIN", "cn=Distribution Group,dc=YourDomain,dc=local");
// define a "prototype" - an example of what you're searching for
// Here: just a simple GroupPrincipal - you want all groups
GroupPrincipal prototype = new GroupPrincipal(ctx);
// define a PrincipalSearcher to find those principals that match your prototype
PrincipalSearcher searcher = new PrincipalSearcher(prototype);
// define a list of strings to hold the group names
List<string> groupNames = new List<string>();
// iterate over the result of the .FindAll() call
foreach(var gp in searcher.FindAll())
{
// cast result to GroupPrincipal
GroupPrincipal group = gp as GroupPrincipal;
// if everything - grab the group's name and put it into the list
if(group != null)
{
groupNames.Add(group.Name);
}
}
Does that satisfy your needs?
For more info on the System.DirectoryServices.AccountManagement namespace, read the Managing Directory Security Principals in the .NET Framework 3.5 article in MSDN magazine.
Here's the solution I made; for those who are interested:
public ArrayList getGroups()
{
// ACTIVE DIRECTORY AUTHENTICATION DATA
string ADDomain = "myDomain.local";
string ADBranchsOU = "Distribution Group";
string ADUser = "Admin";
string ADPassword = "password";
// CREATE ACTIVE DIRECTORY ENTRY
DirectoryEntry ADRoot
= new DirectoryEntry("LDAP://OU=" + ADBranchsOU
+ "," + getADDomainDCs(ADDomain),
ADUser,
ADPassword);
// CREATE ACTIVE DIRECTORY SEARCHER
DirectorySearcher searcher = new DirectorySearcher(ADRoot);
searcher.Filter = "(&(objectClass=group)(cn=* Region))";
SearchResultCollection searchResults = searcher.FindAll();
// ADDING ACTIVE DIRECTORY GROUPS TO LIST
ArrayList list = new ArrayList();
foreach (SearchResult result in searchResults)
{
string groupName = result.GetDirectoryEntry().Name.Trim().Substring(3);
list.Add(groupName);
}
return list;
}
public string getADDomainDCs(string ADDomain)
{
return (!String.IsNullOrEmpty(ADDomain))
? "DC=" + ADDomain.Replace(".", ",DC=")
: ADDomain;
}