AD function IsComputerGroupMember fails if group is "large"

Aug 28, 2012 at 10:24 PM

I have run into an issue where if a group contains a large number of members, then the webservice may return "FALSE" when the user object really is a group member.

From my limited testing, this happens if the group is > 1500 members.

Judging from the trace log, the function only collects the first 1500 members.  If the search object is not one of the first 1500 members, then FALSE is returned even though the searched for object may or may not be a group memeber.

Obviously, I don;t know the cause of this, but I have done some AD programming in the past, and I would guess that it is from the way AD pages data.  When you ask AD for some information, it doens't return all its data all the time.  If the amount of data to be returned by a query is large, it will only return the first X records.  Programmers have to work around this by paging the data - requesting multiple data sets until the end of the data is reached. Do you think that is what is going on here?

I have one group with 6700 memebers - so the function doesn't work on that group. 

This is an issue for me because the Addcomputertogroup will return false if you try to add a computer to a group it is already a member of.  I tried to work around that limitation by first checking to see if the object was already a group member and then call the addtogroup function iff the object was not already a group memeber.  Since I am now trying to add objects to a large group - the is member gives me incorrect information and then the addtogroup returns false because I incorrectly try to add an object to a group it is already a member of.

In my opinion, the add object to a group ought to return true if either the object add is successful or if the object is already a group memeber and only return false if the add failed in some way other than already a member - like permissions error or group doesn't exist or object doesn't exist.  If at the end of the add to group, the requested object is in the group, to me that is a success whether or not the object was already in the group. I've mentioned this before and I can't rember if this part is changed in the next rev or not.

Aug 28, 2012 at 10:31 PM

Here is a sample of what retrieving a list of objects in an AD collection looks like.  This will return 1000 members at a time (which is the AD default I think for maximum returns per set)

Sub EnumMembers(ByVal strName, ByVal strOffset)
    ' Recursive subroutine to enumerate members of a group,
    ' including nested group memberships.
    ' Uses range limits to handle groups with more than 1000 members.

    Dim strFilter, strQuery, adoRecordset, objMember
    Dim strDN, intCount, blnLast, intLowRange
    Dim intHighRange, intRangeStep, objField

    ' Filter on objects of class "group" and specified name.
    strFilter = "(&(sAMAccountName=" & strName & "))"

    ' Setup to retrieve 1000 members at a time.
    blnLast = False
    intRangeStep = 999
    intLowRange = 0
    IntHighRange = intLowRange + intRangeStep

    Do While True

        If (blnLast = True) Then
            ' If last query, retrieve remaining members.
            strQuery = strBase & ";" & strFilter & ";" _
                & strAttributes & ";range=" & intLowRange _
                & "-*;subtree"
        Else
            ' If not last query, retrieve 1000 members.
            strQuery = strBase & ";" & strFilter & ";" _
              & strAttributes & ";range=" & intLowRange & "-" _
              & intHighRange & ";subtree"
        End If
        adoCommand.CommandText = strQuery
        Set adoRecordset = adoCommand.Execute
        intCount = 0

        Do Until adoRecordset.EOF
            For Each objField In adoRecordset.Fields
                If (VarType(objField) = (vbArray + vbVariant)) Then
                    For Each strDN In objField.Value
   Set objMember = GetObject("LDAP://" & strDN)
                        If (objGroupList.Exists(objMember.CN) = False) and  (UCase(objMember.Class) = "COMPUTER") Then
                             objGroupList.Add objMember.CN, True
                             'Wscript.Echo strOffset & "Member of "& strName & ": " & objMember.cn
                             intCount = intCount + 1
                        End If
                    Next
                End If
            Next
            adoRecordset.MoveNext
        Loop
        adoRecordset.Close

        ' If this is the last query, exit the Do While loop.
        If (blnLast = True) Then
            Exit Do
        End If

        ' If the previous query returned no members, then the previous
        ' query for the next 1000 members failed. Perform one more
        ' query to retrieve remaining members (less than 1000).
        If (intCount = 0) Then
            blnLast = True
        Else
            ' Setup to retrieve next 1000 members.
            intLowRange = intHighRange + 1
            intHighRange = intLowRange + intRangeStep
        End If
    Loop
End Sub