user2146755
user2146755

Reputation: 85

c# - DirectoryServices - Inaccurate group member count

I'm sure there is a very simple explanation for this but I can't find it. I'm trying to get a count of members of all groups in our domain and the code below works for the most part but there is one particular group I can't get an accurate count on. The group has over 1000 members but for some reason the code below keeps returning 0. Is it due to the number of members? There is nothing special about the group, it's just a normal global security group.

DirectoryEntry context = new DirectoryEntry("LDAP://MyDomain.test");
DirectorySearcher searchGroups = new DirectorySearcher(context);
searchGroups.SearchScope = SearchScope.Subtree;
searchGroups.PageSize = 500;
searchGroups.Filter = "objectClass=group";
searchGroups.PropertiesToLoad.Add("member");

SearchResultCollection searchGroupsResults = searchGroups.FindAll();

foreach (SearchResult group in searchGroupsResults)
{
    Console.WriteLine("Group: " + group.Properties["name"][0].ToString() + " - Member Count: " + group.Properties["member"].Count.ToString());
}

This returns:
Group: MyGroup1 - Member Count: 0

Group: MyGroup2 - Member Count: 861

Group: MyGroup3 - Member Count: 920

Group: MyGroup4 - Member Count: 9 ....

Upvotes: 1

Views: 896

Answers (3)

Lex Li
Lex Li

Reputation: 63203

It is much more complicated if you query a large group, and Microsoft demonstrated a complex algorithm in C++,

https://msdn.microsoft.com/en-us/library/aa367017(v=vs.85).aspx

and someone translated to .NET

https://itconnect.uw.edu/wares/msinf/authn/ldap/enumerate-large-groups/

...
 using System.DirectoryServices;
 ...

/// <summary>
 /// Determines whether or not the specified user is a member of the group.
 /// </summary>
 /// <param name="UserDN">A System.String containing the user's distinguished name (DN).</param>
 /// <param name="Group">A System.DirectoryServices.DirectoryEntry object of the target group.</param>
 private Boolean IsMemberOfLargeGroup( String UserDN, DirectoryEntry Group )
 {
 Boolean userFound = false;
 Boolean isLastQuery = false;
 Boolean exitLoop = false;
 Int32 rangeStep = 1500;
 Int32 rangeLow = 0;
 Int32 rangeHigh = rangeLow + ( rangeStep - 1 );
 String attributeWithRange;

DirectorySearcher groupSearch = new DirectorySearcher( Group );
 SearchResult searchResults;

groupSearch.Filter = "(objectClass=*)";

do
 {
 if( !isLastQuery )
 attributeWithRange = String.Format( "member;range={0}-{1}", rangeLow, rangeHigh );
 else
 attributeWithRange = String.Format( "member;range={0}-*", rangeLow );

groupSearch.PropertiesToLoad.Clear();
 groupSearch.PropertiesToLoad.Add( attributeWithRange );

searchResults = groupSearch.FindOne();
 groupSearch.Dispose();

if( searchResults.Properties.Contains( attributeWithRange ) )
 {
 if( searchResults.Properties[ attributeWithRange ].Contains( userDN ) )
 userFound = true;

if( isLastQuery )
 exitLoop = true;
 }
 else
 {
 isLastQuery = true;
 }

if( !isLastQuery )
 {
 rangeLow = rangeHigh + 1;
 rangeHigh = rangeLow + ( rangeStep - 1 );
 }
 }
 while( ! ( exitLoop | userFound ) );

return userFound;
 }

Upvotes: 2

user2146755
user2146755

Reputation: 85

thank you for your help. I did as you suggested and used the Dispose() method but it didn't help. I was able to get around the problem by using a DirectoryEntry rather than a SearchResult. See below.

DirectoryEntry context = new DirectoryEntry("LDAP://My.Domain.com");
DirectorySearcher searchGroups = new DirectorySearcher(context);
searchGroups.SearchScope = SearchScope.Subtree;
searchGroups.PageSize = 500;
searchGroups.Filter = "objectClass=group";
searchGroups.PropertiesToLoad.Add("name");
searchGroups.PropertiesToLoad.Add("member");

SearchResultCollection searchGroupsResults = searchGroups.FindAll();

foreach (SearchResult group in searchGroupsResults)
{
    DirectoryEntry groupDirectoryEntry = group.GetDirectoryEntry();

    Console.WriteLine("Group: " + group.Properties["name"][0].ToString() + " - Member Count: " + groupDirectoryEntry.Properties["member"].Count.ToString());
}

Upvotes: 0

Jacky
Jacky

Reputation: 3239

It might due to your searchGroups cannot release all of its unmanaged resources when it is garbage collected, as it stated in msnd: https://msdn.microsoft.com/en-us/library/system.directoryservices.directorysearcher.findall.aspx

Try wrapping everything in a using, or use Dispose():

using SearchResultCollection searchGroupsResults = searchGroups.FindAll())
{
    foreach (SearchResult group in searchGroupsResults)
    {
        Console.WriteLine("Group: " + group.Properties["name"][0].ToString() + " - Member Count: " + group.Properties["member"].Count.ToString());
    }
}

Upvotes: 0

Related Questions