I need to retrieve group members for several hundred AD groups. While the code below gives the right answer, it is very slow. Is there a more efficient approach to decompose these groups to its members?
My current approach is using System.DirectoryServices.AccountManagement
. I have a List<T>
of group names to search. Iterate through each one and call GroupPrincipal.GetMembers()
for each
(Currently this code takes 2+ minutes to decompose about 100 groups; my target would be under 15 seconds)
[EnvironmentPermissionAttribute(SecurityAction.LinkDemand, Unrestricted = true)]
private static void GetGroupMembership(List<ActiveDirectoryPrincipalProperties> userGroupProperties)
{
List<ActiveDirectoryPrincipalProperties> groupProperties = new List<ActiveDirectoryPrincipalProperties>();
foreach (ActiveDirectoryPrincipalProperties gProperties in userGroupProperties)
{
if (gProperties.groupYesNo)
{
PrincipalContext ctx = new PrincipalContext(ContextType.Domain, gProperties.groupDomain);
try
{
GroupPrincipal group = GroupPrincipal.FindByIdentity(ctx, IdentityType.Name, gProperties.groupName);
foreach (Principal member in group.GetMembers(true))
{
ActiveDirectoryPrincipalProperties memberProperties = new ActiveDirectoryPrincipalProperties();
memberProperties.fullGroupName = gProperties.fullGroupName;
memberProperties.groupDomain = gProperties.groupDomain;
memberProperties.groupName = gProperties.groupName;
memberProperties.groupType = gProperties.groupType;
memberProperties.groupYesNo = false;
memberProperties.memberDomain = member.Context.Name.ToString();
memberProperties.memberName = member.SamAccountName.ToString();
memberProperties.memberType = member.StructuralObjectClass.ToString();
memberProperties.sqlUserOnlyYesNo = false;
groupProperties.Add(memberProperties);
}
group.Dispose();
}
finally
{
ctx.Dispose();
}
}
}
userGroupProperties.AddRange(groupProperties);
}
public class ActiveDirectoryPrincipalProperties
{
public string fullGroupName { get; set; }
public string groupDomain { get; set; }
public string groupName { get; set; }
public string groupType { get; set; }
public string memberDomain { get; set; }
public string memberName { get; set; }
public string memberType { get; set; }
public bool groupYesNo { get; set; }
public bool sqlUserOnlyYesNo { get; set; }
}
I shaved off about a half a minute from my procedure using a LAMDA filter on my list before iterating it's elements and removed one conditional statement in the method. Here is my updated code.
[SecurityCritical]
[SecurityPermissionAttribute(SecurityAction.Demand)]
private static void GetGroupMembership(List<ActiveDirectoryPrincipalProperties> userGroupProperties)
{
List<ActiveDirectoryPrincipalProperties> groupProperties = new List<ActiveDirectoryPrincipalProperties>();
foreach (ActiveDirectoryPrincipalProperties gProperties in userGroupProperties.FindAll(token => token.groupYesNo.Equals(true)))
{
PrincipalContext ctx = new PrincipalContext(ContextType.Domain, gProperties.groupDomain);
try
{
GroupPrincipal group = GroupPrincipal.FindByIdentity(ctx, IdentityType.Name, gProperties.groupName);
foreach (Principal member in group.GetMembers(true))
{
ActiveDirectoryPrincipalProperties memberProperties = new ActiveDirectoryPrincipalProperties();
memberProperties.fullGroupName = gProperties.fullGroupName;
memberProperties.groupDomain = gProperties.groupDomain;
memberProperties.groupName = gProperties.groupName;
memberProperties.groupType = gProperties.groupType;
memberProperties.groupYesNo = false;
memberProperties.memberDomain = member.Context.Name.ToString();
memberProperties.memberName = member.SamAccountName.ToString();
memberProperties.memberType = member.StructuralObjectClass.ToString();
memberProperties.sqlUserOnlyYesNo = false;
groupProperties.Add(memberProperties);
}
group.Dispose();
}
finally
{
ctx.Dispose();
}
}
userGroupProperties.AddRange(groupProperties);
}