I have the following AD forest with two trees:
DNS name of the Domain1 is domain1.local. DNS name of the Domain4 is domain4.local.
In each domain there is a domain controller with Global Catalog enabled.
I'm trying to get UserPrincipal for the user from Domain 4 by its SID. The program runs from a machine in Domain2.
I use the following code:
// Running on some machine from Domain2
PrincipalContext context = new PrincipalContext(
ContextType.Domain,
"dc2.domain2.domain1.local:3268", // Using Global Catalog port and local domain controller
"DC=domain1, DC=local", // I guess the problem is here
"domain1\\super-admin", // User has all necessary rights across all domains
"password");
UserPrincipal principal = UserPrincipal.FindByIdentity(context, "SID-OF-A-USER-FROM-DOMAIN-4");
In my case principal is null (the user was not found).
Searching within one tree (domain1 and its children) works fine with the code snippet above, but I have no idea how to modify the container parameter of the PrincipalContext constructor to really enable forest-wide searches.
Initially I thought that "DC=domain1, DC=local" points to the forest root, but it seems I have misunderstanding here.
And I know that if I change the container path to "DC=domain4, DC=local" then the search will work, but only for users in domain4.
But I really need such a container path that will point to the entire forest, so I could search for users from any domain within a forest using the same PrincipalContext.
Any help is appreciated, especially if anyone could clarify if my requirements are achievable.
We could not find any other solution except switching to DirectorySearcher. So it appears that PrincipalContext class doesn't fully support searching in the whole forest.
I cannot say this solution is ideal. I guess it can be tuned for better performance. But we are really disappointed it could not be done using PrincipalContext.
Here is the rough idea how our code works now:
...
// Here is a list of SIDs of users we want to find (initialized somewhere above)
List<string> userSids;
// List of sample results.
List<string> loadedUsers = new List<string>();
using (DirectorySearcher searcher = new DirectorySearcher(new DirectoryEntry("GC://dc2.domain2.domain1.local")))
{
StringBuilder filterStringBuilder = new StringBuilder();
// Just create a single LDAP query for all user SIDs
filterStringBuilder.Append("(&(objectClass=user)(|");
foreach (string userSid in users)
{
filterStringBuilder.AppendFormat("({0}={1})", "objectSid", userSid);
}
filterStringBuilder.Append("))");
searcher.PageSize = 1000; // Very important to have it here. Otherwise you'll get only 1000 at all. Please refere to DirectorySearcher documentation
searcher.Filter = filterStringBuilder.ToString();
// We do not want to go beyond GC
searcher.ReferralChasing = ReferralChasingOption.None;
searcher.PropertiesToLoad.AddRange(
new[] { "DistinguishedName" });
SearchResultCollection results = searcher.FindAll();
foreach (SearchResult searchResult in results)
{
string distinguishedName = searchResult.Properties["DistinguishedName"][0].ToString();
loadedUsers.Add(distinguishedName);
}
}
...