Reputation: 85
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
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
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
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