Search code examples
.netactive-directorywmidirectoryservices

Counting Active Directory Security group members


First, I have to enumerate all the AD groups of the current user. Getting the SID and the name is easy:

foreach(var group in WindowsIdentity.GetCurrent().Groups)
{
   var sid = new SecurityIdentifier(group.Value);
   string name = (group.Translate(typeof(NTAccount)) as NTAccount).Value;
}

Then I need to determine the number of members in each of the groups, and I can't get that to work. I guess I need a way to get the distinguished name of the groups of the current users. Any idea how to get that?

What I did so far: I tried a WMI query like this, but it returns no results because I would need the fully qualified domain name but NTAccount gives me only the "friendly" domain account (DOMAIN\group):

SELECT PartComponent FROM Win32_GroupUser 
WHERE (GroupComponent = "Win32_Group.Domain='somedomain', Name='somegroup'")

Then I tried LDAP, working around the missing path/Distinguished Name by binding to the SID of the group:

var adGroupEntry = new DirectoryEntry(String.Format("LDAP://<SID={0}>", group.Value));
if (adGroupEntry != null)
{
  IEnumerable members = adGroupEntry.Invoke("Members", null) as IEnumerable;
  if (members != null)
  {
    foreach (object member in members)
    {
      noOfMembers++;
    }
  }
}

It finds the group, the name property returns the correct value, however Members never contains any elements. If I would know the full LDAP path for a group (like when I hard-code it), the code above would actually yield the correct number of users in the group.

What am I missing here? I'm not very familiar with Active Directory or WMI.

Restrictions:

  • I can't hard-code any domain names or LDAP paths.
  • I'm limited to .NET 2.0, so I can't use System.DirectoryServices.AccountManagement.

Solution

  • I can repro your problem in my environment. It sounds like a bug in ADSI to me.

    The returned COM object doesn't work if you are using serverless binding and SID binding at the same time.

    I could workaround that by not using serverless bindnig. i.e. using this LDAP://*yourdomain.com*/<SID={0}> instead

    I could also workaround that by binding the object again.

    var adGroupEntry = new DirectoryEntry(String.Format("LDAP://<SID={0}>",group.Value));
    string dn = adGroupEntry.Properties["distinguishedName"].Value as string;
    DirectoryEntry de = new DirectoryEntry("LDAP://" + dn);
    if (de != null)
    {
        IEnumerable members = de.Invoke("Members", null) as IEnumerable;
        if (members != null)
        {
            foreach (object member in members) 
            {
                noOfMembers++;     
            }   
        } 
    }