SqlRyan
SqlRyan

Reputation: 33944

"Domain Users" group is empty when I use DirectoryServices "member" property

I'm using the following code to get the members of a group on my domain:

  Dim de As New DirectoryEntry("LDAP://" & GroupDN)

  For Each user As String In CType(de.Properties("member"), IEnumerable)

          GroupCollection.Add(Username, Username)

  Next

My problem is that when GroupDN (the distinguishedname of the group) is "CN=Domain Users,CN=Users,DC=Mydomain,DC=local", the For...Each loop doesn't execute, and when I check the Properties statement manually, it's got a count of zero. This seems to work for every other group in my domain, but the "Domain Users" group should contain everybody, and it appears to contain nobody.

I've checked, and the group lists everybody correctly in my Windows AD tools. Is there something obvious that I'm missing here? On a side note, is there a better way to get all the members of a group?

Upvotes: 9

Views: 6895

Answers (2)

marsh-wiggle
marsh-wiggle

Reputation: 2813

The accepted answer is absolutely correct. By default every (user) object has 513 set in the property primarygroupid, which is the fixed "tail" of the Domain Users sid. But: this can be changed and every other group can be configured there, so we can't rely on that.

Here is an example method how to get the group members anyway (regardless if the default is kept or changed). I call a method like this after any query for members of active directory groups. In this example I get an array of distinguished names as result. But all other properties are possible, just add them to dSearcher.PropertiesToLoad.Add(...) and modify the result.

I know, this is a question about VB, I hope its easy to port it.


    using System.DirectoryServices;
    using System.Security.Principal;


    public static string[] GetMembersDnByPrimaryGroupId(string domainName, SecurityIdentifier sidOfGroupToGetMembersByPrimaryGroupId)
    {
        // In a single domain environement the domain name is probably not needed, but
        // we expect a multy domain environement
        if (string.IsNullOrWhiteSpace(domainName) || sidOfGroupToGetMembersByPrimaryGroupId == null)
        {
            throw new ArgumentNullException($"Neither domainName nor sid may be null / blank: DomainName: { domainName }; sid: { sidOfGroupToGetMembersByPrimaryGroupId }");
            //<----------
        }

        List<string> membersDnResult = new List<string>();
        // Get the last segment of the group sid, this is what is stored in the "primaryGroupId"
        string groupSidTail = sidOfGroupToGetMembersByPrimaryGroupId.Value.Split('-').Last();
        string path = $"LDAP://{ domainName }";
        DirectoryEntry dEntry = new DirectoryEntry(path);

        SearchResultCollection adSearchResult = null;
        DirectorySearcher dSearcher = new DirectorySearcher(dEntry);

        // For this example we need just the distinguished name but you can add
        // here the property / properties you want
        dSearcher.PropertiesToLoad.Add("distinguishedName");

        // set the filter to primarygroupid
        dSearcher.Filter = $"(&(primarygroupid={ groupSidTail }))";

        // May die thousand deaths, therefore exception handling is needed. 
        // My exception handling is outside of this method, you may want
        // to add it here
        adSearchResult = dSearcher.FindAll();

        // Get the domains sid and check if the domain part of the wanted sid
        // fits the domain sid (necesarry in multy domain environments)
        byte[] domainSidBytes = (byte[])dEntry.Properties["objectSid"].Value;
        SecurityIdentifier domainSid = new SecurityIdentifier(domainSidBytes, 0);
        if (sidOfGroupToGetMembersByPrimaryGroupId.AccountDomainSid != domainSid)
        {
            throw new ArgumentException($"Domain sid of the wanted group { sidOfGroupToGetMembersByPrimaryGroupId.AccountDomainSid } does not fit the sid { domainSid } of the searched through domain \"{ domainName }\"");
            //<----------
        }

        // We found entries by the primarygroupid
        if (adSearchResult.Count > 0)
        {
            foreach (SearchResult forMemberByPrimaryGroupId in adSearchResult)
            {
                // Every AD object has a distinguishedName, therefore we acess "[0]" 
                // wihtout any further checking
                string dn = forMemberByPrimaryGroupId.Properties["distinguishedName"][0].ToString();
                membersDnResult.Add(dn);
            }
        }

        return membersDnResult.ToArray();
    }

Upvotes: 0

tvanfosson
tvanfosson

Reputation: 532745

Unless you change the primary group id of a user, the user is not stored in the member attribute of the Domain Users group, rather it uses the fact that the primary group id is set to the Domain Users RID to determine membership in Domain Users. The normal case is that the Domain Users member attribute is empty; it would require that you make some changes to the default Active Directory implementation for this to not be the case.

The Domain Users group uses a "computed" mechanism based on the "primary group ID" of the user to determine membership and does not typically store members as multi-valued linked attributes. If the primary group of the user is changed, their membership in the Domain Users group is written to the linked attribute for the group and is no longer calculated. This was true for Windows 2000 and has not changed for Windows Server 2003.

Reference

Upvotes: 12

Related Questions