Script Center > Scripting Forums > The Official Scripting Guys Forum! > List all the members of a group error when there are no members
Ask a questionAsk a question
 

AnswerList all the members of a group error when there are no members

  • Wednesday, July 01, 2009 9:03 PMJames Anderson Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    Language:  VB Script
    Reference:  Hey Scripting Guy!  Script Center Home > Scripts > Active Directory > Groups > List All the Members of a Group

    It has a problem when there are no members of the group!  If there are no members of the group, it provides an erroneous result for that group.  I see two choices... 1.  pull up the membership property of each member and cross verify or 2. Somehow check the member property of the group to see if it's null.  I would prefer the second option if someone knows how to code it.  Here's my script so far...

    ' *****************************
    ' * List All Groups in the Domain and
    ' * List All Members of each Group
    ' *
    ' * Output to a text file on the user's desktop in the format:
    ' * group name <tab> type <tab> member name <tab> type
    ' * Prompt for text file name.
    ' *****************************
    Const MY_DOMAIN = "dc=contoso,dc=com"
    ' *****************************
    ' Start Main
    On Error Resume Next
    Const ADS_SCOPE_SUBTREE = 2
    Const ADS_GROUP_TYPE_GLOBAL_GROUP = &h2
    Const ADS_GROUP_TYPE_LOCAL_GROUP = &h4
    Const ADS_GROUP_TYPE_UNIVERSAL_GROUP = &h8
    Const ADS_GROUP_TYPE_SECURITY_ENABLED = &h80000000
    Const ForReading = 1, ForWriting = 2, ForAppending = 8
    Const myPrompt = "Enter the Output filename (i.e. Groups.txt) that will be saved on your desktop:"
    'Get filename to create and set it up to receive input
    If UCase( Right( WScript.FullName, 12 ) ) = "\CSCRIPT.EXE" Then
      WScript.StdOut.Write myPrompt & " "
      strMyFileName = WScript.StdIn.ReadLine
    Else
      strMyFileName = InputBox( myPrompt )
    End If
    if strMyFileName = "" then
      wscript.quit
    end if
    Set WshShell = CreateObject("WScript.Shell")
    Set WshSysEnv = WshShell.Environment("PROCESS")
    strMyFileName = WshSysEnv("USERPROFILE") & "\Desktop\" & strMyFileName
    Set WshSysEnv = nothing
    Set WshShell = nothing
    Set objFSO = CreateObject("Scripting.FileSystemObject")
    if objFSO.FileExists(strMyFileName) then
      wscript.echo "That filename already exists"
      wscript.quit
    end if
    Set objMyOutput = objFSO.OpenTextFile(strMyFileName, ForWriting, True)
    ' Enumerate the groups in the domain
    Set objConnection = CreateObject("ADODB.Connection")
    Set objCommand =   CreateObject("ADODB.Command")
    objConnection.Provider = "ADsDSOObject"
    objConnection.Open "Active Directory Provider"
    Set objCommand.ActiveConnection = objConnection
    objCommand.Properties("Page Size") = 1000
    objCommand.Properties("Searchscope") = ADS_SCOPE_SUBTREE
    objCommand.CommandText = _
        "SELECT ADsPath, Name FROM 'LDAP://" & MY_DOMAIN & "' WHERE objectCategory='group'"

    Set objRecordSet = objCommand.Execute

    objRecordSet.MoveFirst

    Do Until objRecordSet.EOF
      Set objGroup = GetObject(objRecordSet.Fields("ADsPath").Value)
      strGroupName = objRecordSet.Fields("Name").Value
      If objGroup.GroupType AND ADS_GROUP_TYPE_LOCAL_GROUP Then
        strGroupDesc = "Domain local "
      ElseIf objGroup.GroupType AND ADS_GROUP_TYPE_GLOBAL_GROUP Then
        strGroupDesc = "Global "
      ElseIf objGroup.GroupType AND ADS_GROUP_TYPE_UNIVERSAL_GROUP Then
        strGroupDesc = "Universal "
      Else
        strGroupDesc = "Unknown "
      End If
      If objGroup.GroupType AND ADS_GROUP_TYPE_SECURITY_ENABLED Then
        strGroupDesc = strGroupDesc & "Security group"
      Else
        strGroupDesc = strGroupDesc & "Distribution group"
      End If
     
      ' Reference List all the members of a group
      For Each strMemberOf in objGroup.Member
        Set objMember = GetObject("LDAP://" & strMemberOf)
        strMemberName = right(objMember.Name,len(objMember.Name)-3)
        ' wscript.echo strGroupName & vbcrlf & strGroupDesc & vbcrlf & strMemberName & vbcrlf & objMember.Class
        objMyOutput.WriteLine(strGroupName & vbtab & strGroupDesc & vbtab & strMemberName & vbtab & objMember.Class)
      Next
      objRecordSet.MoveNext
    Loop
    objMyOutput.close

Answers

  • Thursday, July 02, 2009 2:58 PMJames Anderson Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     Answer

    I have when you powershell guys do in one line what I did in an entire script, but mine still adds the type descriptions.  I found out the hard way that set object = nothing does have its uses along with some error checking!  Note to self... when looping a piece of vb script and setting an object in the loop, make sure and clear objects at the end of the loop before they get reused.  It made a difference.  I also added error checking of Err.Number to make sure the object.parameter existed and that cleaned up the output as well.  I checked it against Active Directory and it cleared up the erroneous entries.  The key was checking the error against E_ADS_PROPERTY_NOT_FOUND. Here's the result...

    ' *****************************
    ' * List All Groups in the Domain and
    ' * List All Members of each Group
    ' *
    ' * Output to a text file on the user's desktop in the format:
    ' * group name <tab> type <tab> member name <tab> type
    ' * Prompt for text file name.
    ' *****************************
    ' Variables
    Const MY_DOMAIN = "dc=contoso,dc=com"
    ' *****************************
    ' Start Main
    On Error Resume Next
    Const ADS_SCOPE_SUBTREE = 2
    Const ADS_GROUP_TYPE_GLOBAL_GROUP = &h2
    Const ADS_GROUP_TYPE_LOCAL_GROUP = &h4
    Const ADS_GROUP_TYPE_UNIVERSAL_GROUP = &h8
    Const ADS_GROUP_TYPE_SECURITY_ENABLED = &h80000000
    Const E_ADS_PROPERTY_NOT_FOUND = &h8000500D
    Const MYPROMPT = "Enter the Output filename (i.e. Groups.txt) that will be saved on your desktop:"
    Const ForReading = 1, ForWriting = 2, ForAppending = 8
    Set objFSO = CreateObject("Scripting.FileSystemObject")

    ' Setup the output file
    If UCase( Right( WScript.FullName, 12 ) ) = "\CSCRIPT.EXE" Then
      WScript.StdOut.Write MYPROMPT & " "
      strMyFileName = WScript.StdIn.ReadLine
    Else
      strMyFileName = InputBox( MYPROMPT )
    End If
    if strMyFileName = "" then
      wscript.quit
    end if
    Set WshShell = CreateObject("WScript.Shell")
    Set WshSysEnv = WshShell.Environment("PROCESS")
    strMyFileName = WshSysEnv("USERPROFILE") & "\Desktop\" & strMyFileName
    Set WshSysEnv = nothing
    Set WshShell = nothing
    if objFSO.FileExists(strMyFileName) then
      'objFSO.DeleteFile(strMyFileName)
      wscript.echo "That filename already exists"
      wscript.quit
    end if

    ' Get a recordset of groups in AD
    Set objMyOutput = objFSO.OpenTextFile(strMyFileName, ForWriting, True)
    Set objConnection = CreateObject("ADODB.Connection")
    Set objCommand =   CreateObject("ADODB.Command")
    objConnection.Provider = "ADsDSOObject"
    objConnection.Open "Active Directory Provider"
    Set objCommand.ActiveConnection = objConnection
    objCommand.Properties("Page Size") = 1000
    objCommand.Properties("Searchscope") = ADS_SCOPE_SUBTREE
    objCommand.CommandText = _
        "SELECT ADsPath, Name FROM 'LDAP://" & MY_DOMAIN & "' WHERE objectCategory='group'"
    Set objRecordSet = objCommand.Execute
    objRecordSet.MoveFirst

    ' For each Group, Get group properties
    Do Until objRecordSet.EOF
      Set objGroup = GetObject(objRecordSet.Fields("ADsPath").Value)
      strGroupName = objRecordSet.Fields("Name").Value
      If objGroup.GroupType AND ADS_GROUP_TYPE_LOCAL_GROUP Then
        strGroupDesc = "Domain local "
      ElseIf objGroup.GroupType AND ADS_GROUP_TYPE_GLOBAL_GROUP Then
        strGroupDesc = "Global "
      ElseIf objGroup.GroupType AND ADS_GROUP_TYPE_UNIVERSAL_GROUP Then
        strGroupDesc = "Universal "
      Else
        strGroupDesc = "Unknown "
      End If
      If objGroup.GroupType AND ADS_GROUP_TYPE_SECURITY_ENABLED Then
        strGroupDesc = strGroupDesc & "Security group"
      Else
        strGroupDesc = strGroupDesc & "Distribution group"
      End If

      ' Check if there are members
      err.clear
      arrMemberOf = objGroup.GetEx("Member")
      If Err.Number = E_ADS_PROPERTY_NOT_FOUND then
        ' Write a line to the outputfile with group properties and no members
        objMyOutput.WriteLine(strGroupName & vbtab & strGroupDesc & vbtab & "<null>" & vbtab & "<null>")
      Else
        ' For each group member, get member properties
        For Each strMemberOf in arrMemberOf
          Set objMember = GetObject("LDAP://" & strMemberOf)
          strMemberName = right(objMember.Name,len(objMember.Name)-3)
          ' Write a line to the outputfile with group and member properties
          objMyOutput.WriteLine(strGroupName & vbtab & strGroupDesc & vbtab & strMemberName & vbtab & objMember.Class)
          set objMember = nothing
        Next
      End If
      objRecordSet.MoveNext
      Set objGroup = nothing
    Loop
    objMyOutput.close

All Replies

  • Thursday, July 02, 2009 4:47 AMSalvador Manaois IIIModeratorUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    I haven't tested your script yet but I see that you are enumerating members of the objGroup.Member object (instead of objGroup.Members). Is this a typo?

    An alternative way of getting the members of all groups in your AD is to use a combination of DSQUERY and DSGET, as shown:

    dsquery group | dsget group -members

    This command will list all groups in your AD through DSQUEY, pipes each group to DSGET to list the members of the group. Empty groups will have, well, empty entries. =)

    Regards,

    Salvador Manaois III
    MCITP | Enterprise & Server Administrator
    MCSE MCSA MCTS(x5) CIWA C|EH
    My Blog: Bytes and Badz 
    My Shots: View My PhotoStream
  • Thursday, July 02, 2009 2:58 PMJames Anderson Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     Answer

    I have when you powershell guys do in one line what I did in an entire script, but mine still adds the type descriptions.  I found out the hard way that set object = nothing does have its uses along with some error checking!  Note to self... when looping a piece of vb script and setting an object in the loop, make sure and clear objects at the end of the loop before they get reused.  It made a difference.  I also added error checking of Err.Number to make sure the object.parameter existed and that cleaned up the output as well.  I checked it against Active Directory and it cleared up the erroneous entries.  The key was checking the error against E_ADS_PROPERTY_NOT_FOUND. Here's the result...

    ' *****************************
    ' * List All Groups in the Domain and
    ' * List All Members of each Group
    ' *
    ' * Output to a text file on the user's desktop in the format:
    ' * group name <tab> type <tab> member name <tab> type
    ' * Prompt for text file name.
    ' *****************************
    ' Variables
    Const MY_DOMAIN = "dc=contoso,dc=com"
    ' *****************************
    ' Start Main
    On Error Resume Next
    Const ADS_SCOPE_SUBTREE = 2
    Const ADS_GROUP_TYPE_GLOBAL_GROUP = &h2
    Const ADS_GROUP_TYPE_LOCAL_GROUP = &h4
    Const ADS_GROUP_TYPE_UNIVERSAL_GROUP = &h8
    Const ADS_GROUP_TYPE_SECURITY_ENABLED = &h80000000
    Const E_ADS_PROPERTY_NOT_FOUND = &h8000500D
    Const MYPROMPT = "Enter the Output filename (i.e. Groups.txt) that will be saved on your desktop:"
    Const ForReading = 1, ForWriting = 2, ForAppending = 8
    Set objFSO = CreateObject("Scripting.FileSystemObject")

    ' Setup the output file
    If UCase( Right( WScript.FullName, 12 ) ) = "\CSCRIPT.EXE" Then
      WScript.StdOut.Write MYPROMPT & " "
      strMyFileName = WScript.StdIn.ReadLine
    Else
      strMyFileName = InputBox( MYPROMPT )
    End If
    if strMyFileName = "" then
      wscript.quit
    end if
    Set WshShell = CreateObject("WScript.Shell")
    Set WshSysEnv = WshShell.Environment("PROCESS")
    strMyFileName = WshSysEnv("USERPROFILE") & "\Desktop\" & strMyFileName
    Set WshSysEnv = nothing
    Set WshShell = nothing
    if objFSO.FileExists(strMyFileName) then
      'objFSO.DeleteFile(strMyFileName)
      wscript.echo "That filename already exists"
      wscript.quit
    end if

    ' Get a recordset of groups in AD
    Set objMyOutput = objFSO.OpenTextFile(strMyFileName, ForWriting, True)
    Set objConnection = CreateObject("ADODB.Connection")
    Set objCommand =   CreateObject("ADODB.Command")
    objConnection.Provider = "ADsDSOObject"
    objConnection.Open "Active Directory Provider"
    Set objCommand.ActiveConnection = objConnection
    objCommand.Properties("Page Size") = 1000
    objCommand.Properties("Searchscope") = ADS_SCOPE_SUBTREE
    objCommand.CommandText = _
        "SELECT ADsPath, Name FROM 'LDAP://" & MY_DOMAIN & "' WHERE objectCategory='group'"
    Set objRecordSet = objCommand.Execute
    objRecordSet.MoveFirst

    ' For each Group, Get group properties
    Do Until objRecordSet.EOF
      Set objGroup = GetObject(objRecordSet.Fields("ADsPath").Value)
      strGroupName = objRecordSet.Fields("Name").Value
      If objGroup.GroupType AND ADS_GROUP_TYPE_LOCAL_GROUP Then
        strGroupDesc = "Domain local "
      ElseIf objGroup.GroupType AND ADS_GROUP_TYPE_GLOBAL_GROUP Then
        strGroupDesc = "Global "
      ElseIf objGroup.GroupType AND ADS_GROUP_TYPE_UNIVERSAL_GROUP Then
        strGroupDesc = "Universal "
      Else
        strGroupDesc = "Unknown "
      End If
      If objGroup.GroupType AND ADS_GROUP_TYPE_SECURITY_ENABLED Then
        strGroupDesc = strGroupDesc & "Security group"
      Else
        strGroupDesc = strGroupDesc & "Distribution group"
      End If

      ' Check if there are members
      err.clear
      arrMemberOf = objGroup.GetEx("Member")
      If Err.Number = E_ADS_PROPERTY_NOT_FOUND then
        ' Write a line to the outputfile with group properties and no members
        objMyOutput.WriteLine(strGroupName & vbtab & strGroupDesc & vbtab & "<null>" & vbtab & "<null>")
      Else
        ' For each group member, get member properties
        For Each strMemberOf in arrMemberOf
          Set objMember = GetObject("LDAP://" & strMemberOf)
          strMemberName = right(objMember.Name,len(objMember.Name)-3)
          ' Write a line to the outputfile with group and member properties
          objMyOutput.WriteLine(strGroupName & vbtab & strGroupDesc & vbtab & strMemberName & vbtab & objMember.Class)
          set objMember = nothing
        Next
      End If
      objRecordSet.MoveNext
      Set objGroup = nothing
    Loop
    objMyOutput.close

  • Thursday, July 02, 2009 3:08 PMJames Anderson Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    As to your question on objGroup.Member, ldp.exe has the property listed as "member", not "members".  It works with objGroup.Member (wasn't a typo).  I have found ldp.exe a great tool for looking up property names and seeing what the property values are.  The schema management console also does a nice job of listing properties.  I recommend both to anyone writing  AD scripts.