How to enumerate per-Forest Active Directory domains in C#? - c#

This code enumerate Active Directory domains, if the mahine on which is running is part of the forest.
public static ArrayList EnumerateDomains()
{
ArrayList alDomains = new ArrayList();
Forest currentForest = Forest.GetCurrentForest();
DomainCollection myDomains = currentForest.Domains;
foreach (Domain objDomain in myDomains)
{
alDomains.Add(objDomain.Name);
}
return alDomains;
}
Is it posible to enumerate domains which are part of some other forest ?
What is the difference between forest and global catalog ?

The logic above should work (provided permissions are OK) if you replace the setting of currentForest with a call to Forest.GetForest that identifies the forest whose domains you wish to enumerate.
DirectoryContext context = new DirectoryContext(DirectoryContextType.Forest,
"dns-name-of-target-forest");
Forest currentForest = Forest.GetForest(context);
If you don't have permission but do know someone who does, there are DirectoryContext constructor overrides that allow you to specify an alternate name and password.
The relationship of global catalog to forest is detailed here. In short, a forest is an Active Directory (AD) abstraction for grouping of AD objects. A global catalog (if the forest has one) is a distributed data repository that is required in order for certain types of operations to be done on that forest.

Related

Active Directory group name uniqueness requirement (query from C# client)

Our company's C# product uses System.DirectoryServices.AccountManagement to query Active Directory for users and groups. We use the following method to get the principal:
...
PrincipalContext principalContext = new PrincipalContext(ContextType.Domain);
return principalContext;
...
We get Active Directory groups using (e.g. groupName = "Devs"):
...
GroupPrincipal groupPrincipal = GroupPrincipal.FindByIdentity(this.principalContext, groupName);
...
Everything works fine with this setup when we run it on a simple, one domain Active Directory database.
My question is, what will happen when we run this code on a big forest with more than one "Devs" group? Can there be more than one "Devs" security group in a forest? If so, how will it resolve "Devs"? Do I have to switch to using the method:
public static GroupPrincipal FindByIdentity(
PrincipalContext context,
IdentityType identityType,
string identityValue
)
I cannot simulate this currently (lack of resources and lack of time) and I have been reading a lot about this. I know there are local, global and universal security groups, spread among domain trees. But domain trees in a forest have some sort of trust among the roots, so they are not completely ignorant of each other. What is the worst case of having "Devs" duplicates in the forest and how could the application handle it?
It's pretty common task to search through domain hierarchy. With AccountManagement classes you can do the following:
// Connect to global catalog of the forest
var context = new PrincipalContext(ContextType.Domain, "contoso.com:3268", "DC=contoso,DC=com");
// Build a filter principal by name and context
var groupFilter = new GroupPrincipal(context) {Name = "Devs"};
// Build a searcher with a filter applied
var searcher = new PrincipalSearcher(groupFilter);
// This should return all groups in all subdomains matching specified name
var groups = searcher.FindAll().ToList();
foreach (var group in groups)
{
Console.WriteLine(group.DistinguishedName);
}
You will not have any duplicates cause there can't be more than one group with this name ("Devs") in domain. In AccountManagement terms you create GroupPrincipal object with context and name parameters and can't have more than one in context with the same name.
If you connect to the domain controller (new PrincipalContext(ContextType.Domain)) then FindByIdentity will search this single domain. If you connect to global catalog of the forest (like in my example, port 3268) then FindByIdentity will search entire forest. The DistinguishedName property will show which domain a group belongs to.
As to cross-forest access there you need to connect to global catalog in every forest separately, because there's no user/group data replication between forests global catalogs.

Determine usergroups/claims of user given LDAP server details in C#

We have a test active directory LDAP server. I also have some user names and passwords. I would like to determine the claims/user groups of a particular user, whilst logged into another domain. Can this be done with some C# code? I presume I will have to use System.DirectoryServices.dll
If you can use .Net 3.5 of higher, then try System.DirectoryServices.AccountManagement.dll assembly. It provides System.DirectoryServices.AccountManagement namespace and Principal-based classes, such as UserPrincipal and GroupPrincipal. They represent higher level of abstraction and are easier to use.
For example, to connect to LDAP server in another domain (get Principal Context in terms of this abstraction) you need to create an instance of PrincipalContext class with this constructor:
PrincipalContext anotherDomainContext = new PrincipalContext(ContextType.Domain, DomainDnsName, RootOU, ContextOptions.SimpleBind, QueryUserName, QueryUserPassword);
RootOU is something like "DC=Company,DC=COM", therefore DomainDnsName will be like "company.com" or "ldapserver.company.com". If you have serveral domains in your AD forest then try to connect to global catalog (DomainDnsName = "ldapserver.company.com:3268"). QueryUserName and QueryUserPassword are plain strings with username and password which are used to connect to LDAP server. Username may include domain name, for example:
string QueryUserName = #"company\username";
Once connected to LDAP server you can search for users:
UserPrincipal user = UserPrincipal.FindByIdentity(anotherDomainContext , IdentityType.SamAccountName, samAccountName);
where you supply samAccountName and context (connection).
With an instance of UserPrincipal at hands you gain access to its properties and methods. For example, get security groups for user:
PrincipalSearchResult<Principal> searchResults = user.GetGroups();
List<GroupPrincipal> groupsList = searchResults.Select(result => result as GroupPrincipal).
Where(group => (group != null) &&
(group.IsSecurityGroup.HasValue) &&
(group.IsSecurityGroup.Value))
Note that GetGroups returns only groups to which user belongs directrly. To get all user groups including nested, call GetAuthorizationGroups. Also, you can avoid using LINQ, it's just for filtering security groups from GetGroups.
With GroupPrincipal you can check Name property, or Members collecion.

How to check if address lists exist in Active Directory?

I need to check if All Global Address Lists, All Address List and All System Address Lists exist in Active Directory before get all item from them.
Could you give me some advices or article that I can solve my problem?
Thanks.
Address Lists are part of Exchange functionality not Active Directory which is what I think people are confused about.
Anyway, Address List data is stored in the Active Directory Configuration context under:
CN=Address Lists Container,CN=<EXCHANGE ORGANIZATION NAME>,CN=Microsoft Exchange,CN=Services,CN=Configuration,DC=<DEFAULT NAMING CONTEXT>
You can use ADSIEdit to view the information.
In C# you can use an LDAP query to retrieve information for existing Address Lists.
Edit: Something like this:
DirectoryEntry rootDSE = new DirectoryEntry("LDAP://RootDSE");
DirectoryEntry configContainer = new DirectoryEntry("LDAP://" + rootDSE.Properties["configurationNamingContext"].Value);
DirectorySearcher configSearcher = new DirectorySearcher(configContainer);
configSearcher.Filter = "(&(objectClass=addressBookContainer))";
configSearcher.PropertiesToLoad.Add("displayName");
configSearcher.PageSize = 10000;
configSearcher.SearchScope = SearchScope.Subtree;
SearchResultCollection results = configSearcher.FindAll();

Searching for users across multiple Active Directory domains

I'm using the System.DirectoryServices.AccountManagement to provide user lookup functionality.
The business has several region specific AD domains: AMR, EUR, JPN etc.
The following works for the EUR domain, but doesn't return users from the other domains (naturally):
var context = new PrincipalContext(ContextType.Domain, "mycorp.com", "DC=eur,DC=mycorp,DC=com");
var query = new UserPrincipal(GetContext());
query.Name = "*Bloggs*";
var users = new PrincipalSearcher(query).FindAll().ToList();
However, if I target the entire directory, it doesn't return users from any of the region specific domains:
var context = new PrincipalContext(ContextType.Domain, "mycorp.com", "DC=mycorp,DC=com");
How do I search the entire directory?
Update
Read up on "How Active Directory Searches Work":
http://technet.microsoft.com/en-us/library/cc755809(v=ws.10).aspx
If I suffix the server name with port 3268 it searches against the Global Catalog:
var context = new PrincipalContext(ContextType.Domain, "mycorp.com:3268", "DC=mycorp,DC=com");
However it's very, very slow. Any suggestions on how to improve performance?
Queries which have initial wildcards (*Bloggs*) will be slow unless you have a tuple index on the attribute being queries. None of the attributes in AD have this set by default. Better to not do initial wildcards.

Any way to distinguish between "people user accounts" and "computer user accounts"?

When querying Active Directory for users - is there a way to filter out user accounts created for computers? Ideally a way which is common across most typical networks. e.g.:
DirectorySearcher ds = new DirectorySearcher(new DirectoryEntry([Users_OU_root]));
ds.filter = "(&(objectClass=User)([CRITERIA_TO_FILTER_OUT_COMPUTER_USER_ACCOUNTS]))";
ds.FindAll();
...
If you're on .NET 3.5 and up, you should check out the System.DirectoryServices.AccountManagement (S.DS.AM) namespace. Read all about it here:
Managing Directory Security Principals in the .NET Framework 3.5
MSDN docs on System.DirectoryServices.AccountManagement
Basically, you can define a domain context and easily find users and/or groups in AD:
// set up domain context
PrincipalContext ctx = new PrincipalContext(ContextType.Domain);
// find a user
UserPrincipal user = UserPrincipal.FindByIdentity(ctx, "SomeUserName");
if(user != null)
{
// do something here....
}
// find the group in question
GroupPrincipal group = GroupPrincipal.FindByIdentity(ctx, "YourGroupNameHere");
// if found....
if (group != null)
{
// iterate over members
foreach (Principal p in group.GetMembers())
{
Console.WriteLine("{0}: {1}", p.StructuralObjectClass, p.DisplayName);
// do whatever you need to do to those members
}
}
The new S.DS.AM makes it really easy to play around with users and groups in AD:
Computer accounts will show up as ComputerPrincipal (derived from Principal) - so you can easily keep users and computer accounts apart.
If you cannot or don't want to move to S.DS.AM - you can also keep user and computers apart by using the objectCategory instead of the objectClass in your LDAP filter. objectCategory is beneficial anyway, since it's indexed, and not multi-valued - so query performance will be much better.
For a real-life user, use objectCategory = Person, while for a computer, use objectCategory = Computer in your LDAP filter.
Even if I agree with the answer. Active-Directory remain an LDAP server. Here is the filter you are looking for :
(&(objectCategory=user)(objectClass=user)(...))
'objectCategory=user' is a shortcut for 'objectCategory=CN=User,CN=Schema,CN=Configuration,DC=dom,DC=fr' understood by Active-Directory but it's also a way in others Directories, that's why I put an answer, even if another answer is accepted.

Categories