How to get all the AD groups for a particular user?
Solution 1
Just query the "memberOf" property and iterate though the return, example:
search.PropertiesToLoad.Add("memberOf");
StringBuilder groupNames = new StringBuilder(); //stuff them in | delimited
SearchResult result = search.FindOne();
int propertyCount = result.Properties["memberOf"].Count;
String dn;
int equalsIndex, commaIndex;
for (int propertyCounter = 0; propertyCounter < propertyCount;
propertyCounter++)
{
dn = (String)result.Properties["memberOf"][propertyCounter];
equalsIndex = dn.IndexOf("=", 1);
commaIndex = dn.IndexOf(",", 1);
if (-1 == equalsIndex)
{
return null;
}
groupNames.Append(dn.Substring((equalsIndex + 1),
(commaIndex - equalsIndex) - 1));
groupNames.Append("|");
}
return groupNames.ToString();
This just stuffs the group names into the groupNames string, pipe delimited, but when you spin through you can do whatever you want with them
Solution 2
You should use System.DirectoryServices.AccountManagement. It's much easier. Here is a nice code project article giving you an overview on all the classes in this DLL.
As you pointed out, your current approach doesn't find out the primary group. Actually, it's much worse than you thought. There are some more cases that it doesn't work, like the domain local group from another domain. You can check here for details. Here is how the code looks like if you switch to use System.DirectoryServices.AccountManagement. The following code can find the immediate groups this user assigned to, which includes the primary group.
UserPrincipal user = UserPrincipal.FindByIdentity(new PrincipalContext (ContextType.Domain, "mydomain.com"), IdentityType.SamAccountName, "username");
foreach (GroupPrincipal group in user.GetGroups())
{
Console.Out.WriteLine(group);
}
Solution 3
Use tokenGroups:
DirectorySearcher ds = new DirectorySearcher();
ds.Filter = String.Format("(&(objectClass=user)(sAMAccountName={0}))", username);
SearchResult sr = ds.FindOne();
DirectoryEntry user = sr.GetDirectoryEntry();
user.RefreshCache(new string[] { "tokenGroups" });
for (int i = 0; i < user.Properties["tokenGroups"].Count; i++) {
SecurityIdentifier sid = new SecurityIdentifier((byte[]) user.Properties["tokenGroups"][i], 0);
NTAccount nt = (NTAccount)sid.Translate(typeof(NTAccount));
//do something with the SID or name (nt.Value)
}
Note: this only gets security groups
Solution 4
This is how I list all the groups (direct and indirect) for a specific Distinguished Name:
The string 1.2.840.113556.1.4.1941 specifies LDAP_MATCHING_RULE_IN_CHAIN.
This rule is limited to filters that apply to the DN. This is a special "extended" match operator that walks the chain of ancestry in objects all the way to the root until it finds a match.
This method is 25 times faster than the UserPrincipal.GetGroups() method in my testing.
Note: The primary group (typically Domain Users) is not returned by this or GetGroups() method. To get the primary group name too, I've confirmed this method works.
Additionally, I found this list of LDAP filters extremely useful.
private IEnumerable<string> GetGroupsForDistinguishedName(DirectoryEntry domainDirectoryEntry, string distinguishedName)
{
var groups = new List<string>();
if (!string.IsNullOrEmpty(distinguishedName))
{
var getGroupsFilterForDn = $"(&(objectCategory=group)(member:1.2.840.113556.1.4.1941:={distinguishedName}))";
using (DirectorySearcher dirSearch = new DirectorySearcher(domainDirectoryEntry))
{
dirSearch.Filter = getGroupsFilterForDn;
dirSearch.PropertiesToLoad.Add("name");
using (var results = dirSearch.FindAll())
{
foreach (SearchResult result in results)
{
if (result.Properties.Contains("name"))
groups.Add((string)result.Properties["name"][0]);
}
}
}
}
return groups;
}
Solution 5
This code works even faster (two 1.5 faster than my previous version):
public List<String> GetUserGroups(WindowsIdentity identity)
{
List<String> groups = new List<String>();
String userName = identity.Name;
int pos = userName.IndexOf(@"\");
if (pos > 0) userName = userName.Substring(pos + 1);
PrincipalContext domain = new PrincipalContext(ContextType.Domain, "riomc.com");
UserPrincipal user = UserPrincipal.FindByIdentity(domain, IdentityType.SamAccountName, userName); // NGeodakov
DirectoryEntry de = new DirectoryEntry("LDAP://RIOMC.com");
DirectorySearcher search = new DirectorySearcher(de);
search.Filter = "(&(objectClass=group)(member=" + user.DistinguishedName + "))";
search.PropertiesToLoad.Add("cn");
search.PropertiesToLoad.Add("samaccountname");
search.PropertiesToLoad.Add("memberOf");
SearchResultCollection results = search.FindAll();
foreach (SearchResult sr in results)
{
GetUserGroupsRecursive(groups, sr, de);
}
return groups;
}
public void GetUserGroupsRecursive(List<String> groups, SearchResult sr, DirectoryEntry de)
{
if (sr == null) return;
String group = (String)sr.Properties["cn"][0];
if (String.IsNullOrEmpty(group))
{
group = (String)sr.Properties["samaccountname"][0];
}
if (!groups.Contains(group))
{
groups.Add(group);
}
DirectorySearcher search;
SearchResult sr1;
String name;
int equalsIndex, commaIndex;
foreach (String dn in sr.Properties["memberof"])
{
equalsIndex = dn.IndexOf("=", 1);
if (equalsIndex > 0)
{
commaIndex = dn.IndexOf(",", equalsIndex + 1);
name = dn.Substring(equalsIndex + 1, commaIndex - equalsIndex - 1);
search = new DirectorySearcher(de);
search.Filter = "(&(objectClass=group)(|(cn=" + name + ")(samaccountname=" + name + ")))";
search.PropertiesToLoad.Add("cn");
search.PropertiesToLoad.Add("samaccountname");
search.PropertiesToLoad.Add("memberOf");
sr1 = search.FindOne();
GetUserGroupsRecursive(groups, sr1, de);
}
}
}
Related videos on Youtube
Comments
-
NLV about 2 years
I checked this post already. But it doesn't answer my question. I want to get all the active directory groups in which a particular user is a member.
I've written the following code. But I'm not able to proceed further as I don't know how to give the filter and how to access the properties.
class Program { static void Main(string[] args) { DirectoryEntry de = new DirectoryEntry("LDAP://mydomain.com"); DirectorySearcher searcher = new DirectorySearcher(de); searcher.Filter = "(&(ObjectClass=group))"; searcher.PropertiesToLoad.Add("distinguishedName"); searcher.PropertiesToLoad.Add("sAMAccountName"); searcher.PropertiesToLoad.Add("name"); searcher.PropertiesToLoad.Add("objectSid"); SearchResultCollection results = searcher.FindAll(); int i = 1; foreach (SearchResult res in results) { Console.WriteLine("Result" + Convert.ToString(i++)); DisplayProperties("distinguishedName", res); DisplayProperties("sAMAccouontName", res); DisplayProperties("name", res); DisplayProperties("objectSid", res); Console.WriteLine(); } Console.ReadKey(); } private static void DisplayProperties(string property, SearchResult res) { Console.WriteLine("\t" + property); ResultPropertyValueCollection col = res.Properties[property]; foreach (object o in col) { Console.WriteLine("\t\t" + o.ToString()); } } }
Any ideas?
-
NLV over 13 yearsOkie. I learnt from here - eggheadcafe.com/software/aspnet/30375857/… that memberOf wont return Primary Groups of a user. But I can get the primaryGroupID which gives the RID(?) of the group using which I need to get the AD group. Any ideas how to do it?
-
Harvey Kwok over 13 yearsPlease check my newly posted answer. Your current approach doesn't work very well. If the user is assigned to an Universal Group in another forest, the Universal Group won't show up in the memberOf attribute
-
-
NLV over 13 yearsOkie. I've recursive found the parent groups. But still two groups (or folders?) 'Domain Users' is not coming in the list. But if I check the 'memberOf' tab it is there. Domain users is a builtin group right? Am I missing something here?
-
NathanOliver about 9 yearsPlease don't post just code only answers. Please detail what is going on in your answer.
-
Wolf5 over 7 yearsThis code was about 10 times faster than any of the other codes I have tried (using PrincipalContext). Went from about 4 seconds fetching all groups for a user to 400 ms. Thanks.
-
Flexabust Bergson almost 7 years@Wolf5 I don't understand this, am I supposed to add each
NTAccount
to a List, for each iteration? -
Kevin .NET about 6 yearsuser.GetGroups() is very slow!
-
Kiquenet about 5 yearshow to perform an LDAP search using the LDAP queries provided ?
-
Kiquenet about 5 years@Kevin.NET very slow? benchmarks ? high performance solution?
-
neumann1990 about 5 yearsUsing a filter of $"(&(objectCategory=group)(member:1.2.840.113556.1.4.1941:={distinguishedName}))" makes the query even more performant. It seems that AD uses the group filter to limit the number of objects it checks (according to Note 19 here: social.technet.microsoft.com/wiki/contents/articles/…). Other than that, this works great :)
-
DDuffy over 4 yearsI know it has been a while, but do you know how you would separate the Security Groups from the Distribution Lists?
-
DDuffy over 4 yearsnever mind. Worked it out. Adding code for "Future Generations". Pulls the group type and adds it as a second string.
StringBuilder groupTypes = new StringBuilder(); secondequalsIndex = dn.IndexOf("=", equalsIndex+1); secondcommaIndex = dn.IndexOf(",", commaIndex+1); groupTypes.Append(dn.Substring((secondequalsIndex + 1),(secondcommaIndex - secondequalsIndex) - 1)); groupTypes.Append("|");
-
Mark about 4 yearsIndeed, its an easy solution, but very slow. every item in the foreach takes up some time, so the query is not done in 1 single call, but every time you loop.
-
Chazt3n almost 4 years@Mark call tolist() on getgroups() if it's calling every time.
groups = userPrincipal.GetAuthorizationGroups().ToList();
is decently fast -
Russ Ebbing about 3 yearsSystem.DirectoryServices.AccountManagement will work for querying one but unbelievably slow when querying 500. The reason for this is this class transfers everything over the wire even if you only need to select a few fields.
-
PKCS12 over 2 yearsIs there anyway of determining the depth of how nested each group is using this method please?