Display members of a group with more than 1500 members

Discussion in 'Scripting' started by Simon G, Aug 21, 2007.

  1. Simon G

    Simon G Guest

    I've got a little script which goes through our entire AD structure searching
    for groups. On finding a group it then displays all the members of the group.

    The final output is a text file which looks something like

    group a, membera
    group a, memberb
    group b, member a

    etc etc

    The problem i have found is that any groups with over 1500 users are
    truncated at 1500, so i'm not getting a true reflection of all the users in
    those groups.

    I've read a little that you have to specify the range to search, however, as
    this is going through our entire active directory i need it to be able to
    know when it hits a large group and automatically adjust to display every
    single member.

    any ideas?
     
    Simon G, Aug 21, 2007
    #1
    1. Advertisements

  2. I have an example VBScript program that documents a large group linked here:

    http://www.rlmueller.net/DocumentLargeGroup.htm

    The program uses ADO range limits to overcome the 1500 limitation.
     
    Richard Mueller [MVP], Aug 21, 2007
    #2
    1. Advertisements

  3. Simon G

    Simon G Guest

    Richard,

    Thanks that works great. I am however having a little trouble in trying to
    get it to loop through all my groups.

    The idea is that i can produce a text file which contains all the group
    names (or DN's if it's better for the script) and get the script to loop
    through each line and query the members of each group.

    I've tried putting this into the script, but it fails when it calls the sub
    routine with invalid syntax


    ' Specify the text file of user names.
    strFilePath = "c:\grouplist.txt"

    ' Open the file for read access.
    Set objFSO = CreateObject("Scripting.FileSystemObject")
    Set objFile = objFSO.OpenTextFile(strFilePath)

    ' Read each line of the file.
    Do Until objFile.AtEndOfStream
    strNTName = Trim(objFile.ReadLine)

    (rest of you script, sub routines etc)

    Loop

    ' Clean up.
    objFile.Close

    It seems to fail on this line

    Sub EnumMembers(strName, strOffset)

    I've done a wscript.echo and the varaible strNTName is being picked up
    correctly and runs through quite nicely.

    Any ideas?
     
    Simon G, Aug 22, 2007
    #3
  4. ....Snip.

    I got the following to work. The text file has the NetBIOS names (NT names)
    of the groups. I create the ADO objects (except the Recordset object)
    outside the Sub so they have global scope and only need to be created once.
    Same for the dictionary object, but I had to add a step to clear the
    dictionary object after each group is processed. I added a line to output
    the name of the group, and modified the lines that output member names so
    they do not include the group name. Note that this works also for groups
    with fewer members.
    ============
    Option Explicit

    Dim strNTName, objRootDSE, strDNSDomain, adoCommand
    Dim adoConnection, strBase, strAttributes, objGroupList
    Dim strFile, objFile, objFSO

    Const ForReading = 1

    ' Specify text file of group NetBIOS names.
    strFile = "c:\Scripts\Groups.txt"

    ' Open the file for read access.
    Set objFSO = CreateObject("Scripting.FileSystemObject")
    Set objFile = objFSO.OpenTextFile(strFile, ForReading)

    ' Determine DNS domain name.
    Set objRootDSE = GetObject("LDAP://RootDSE")
    strDNSDomain = objRootDSE.Get("DefaultNamingContext")

    ' Use ADO to search Active Directory.
    Set adoCommand = CreateObject("ADODB.Command")
    Set adoConnection = CreateObject("ADODB.Connection")
    adoConnection.Provider = "ADsDSOObject"
    adoConnection.Open = "Active Directory Provider"
    adoCommand.ActiveConnection = adoConnection
    adoCommand.Properties("Page Size") = 100
    adoCommand.Properties("Timeout") = 30
    adoCommand.Properties("Cache Results") = False

    ' Specify base of search and "member" attribute to retrieve.
    strBase = "<LDAP://" & strDNSDomain & ">"
    strAttributes = "member"

    ' Use dictionary object to track unique group members.
    Set objGroupList = CreateObject("Scripting.Dictionary")
    objGroupList.CompareMode = vbTextCompare

    ' Read each line of the file.
    Do Until objFile.AtEndOfStream
    strNTName = Trim(objFile.ReadLine)
    ' Skip blank lines.
    If (strNTName <> "") Then
    ' Enumerate group members.
    Call EnumMembers(strNTName, "")
    End If
    Loop

    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

    ' Document group NetBIOS name.
    Wscript.Echo strOffset & "Group: " & strName

    ' Filter on objects of class "group" and specified name.
    strFilter = "(&(ObjectCategory=group)" _
    & "(ObjectClass=group)" _
    & "(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
    ' Escape any forward slash characters, "/", with the
    backslash
    ' escape character. All other characters that should
    be escaped are.
    strDN = Replace(strDN, "/", "\/")
    ' Check dictionary object for duplicates.
    If (objGroupList.Exists(strDN) = False) Then
    ' Add to dictionary object.
    objGroupList.Add strDN, True

    ' Bind to each group member, to find "class" of
    member.
    Set objMember = GetObject("LDAP://" & strDN)
    ' Output group member.
    Wscript.Echo strOffset & " Member: " & strDN
    intCount = intCount + 1
    If (UCase(objMember.Class) = "GROUP") Then
    ' If the member is class "group",
    ' call subroutine recursively.
    Call _
    EnumMembers(objMember.sAMAccountName, _
    strOffset & "--")
    End If
    Else
    ' Duplicate member. Output group member.
    Wscript.Echo strOffset & " Member: " & strDN _
    & " (Duplicate)"
    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

    ' Clear the dictionary object.
    objGroupList.RemoveAll
    End Sub
     
    Richard Mueller [MVP], Aug 22, 2007
    #4
    1. Advertisements

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments (here). After that, you can post your question and our members will help you out.