Search | Directories | Reference Tools
UW Windows Infrastructure Service banner image
Skip Navigation LinksUW Home > Computing and Networking > Support > UW Domains > UW Windows Infrastructure > Using LDAP to Enumerate Large Groups

Using LDAP to Enumerate Large Groups in UWWI

In your day-to-day usage of the UW Windows Infrastructure, you may notice several groups that have an extremely large membership list - for example, most of the affiliation-based groups exceed 100,000 members.  If you use an application that relies on LDAP as its underlying directory protocol, you may notice some odd behavior when checking the membership of these groups.

By default, Active Directory imposes a attribute value page limit of 1500 - in short, Active Directory will return the values of that attribute in sets of 1500, and its up to the developer to page through those sets looking for the desired value.

During the development and implementation of the UW Windows Infrastructure, the project team encountered this scenario and developed code to work around it.  This code has been made available below.

The code samples are written in Visual Basic .NET and C#, tested against Microsoft .NET Framework v2.0.  Without modification, this function allows you to check to see whether or not a user is in a particular group, however, this code can be easily extended to populate a local cache or collection for a variety of operations.

These code samples are provided as-is, and no support will be offered.

[ C# | Visual Basic .NET ]


C#

...
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;
}


Visual Basic .NET

...
Imports 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 Function IsMemberOfLargeGroup( ByVal UserDN As String, ByVal Group As DirectoryEntry ) As Boolean

    Dim userFound As Boolean = False
    Dim isLastQuery As Boolean = False
    Dim exitLoop As Boolean = False
    Dim rangeStep As Int32 = 1500
    Dim rangeLow As Int32 = 0
    Dim rangeHigh As Int32 = rangeLow + (rangeStep - 1 )
    Dim attributeWithRange As String
   
    Dim groupSearch As New DirectorySearcher( Group )
    Dim searchResults As SearchResult

    groupSearch.Filter = "(objectClass=*)"

    Do
        If Not isLastQuery Then
            attributeWithRange = String.Format( "member;range={0}-{1}", rangeLow, rangeHigh )
        Else
            attributeWithRange = String.Format( "member;range={0}-*", rangeLow )
        End If

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

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

        If ( searchResults.Properties.Contains( attributeWithRange ) ) Then
            If ( searchResults.Properties[ attributeWithRange ].Contains( userDN ) ) Then
                userFound = true
            End If

            If isLastQuery Then
                exitLoop = true
            End If
        Else
            isLastQuery = true
        End If

        If Not isLastQuery Then
            rangeLow = rangeHigh + 1
            rangeHigh = rangeLow + ( rangeStep - 1 )
        End If
    Loop While Not ( exitLoop | userFound )

    IsMemberOfLargeGroup = userFound
End Function