' EnumLocalGroup.vbs
' VBScript program to enumerate members of a local group.
'
' ----------------------------------------------------------------------
' Copyright (c) 2007 Richard L. Mueller
' Hilltop Lab web site - http://www.rlmueller.net
' Version 1.0 - April 5, 2007
' Version 1.1 - July 31, 2007 - Escape any "/" characters in group DN's.
' Version 1.2 - July 22, 2008 - Skip local implicit groups.
' A VBScript program demonstrating how to enumerate members of a local
' group. Reveals direct membership in the local group, membership in
' nested local groups, membership in domain groups that are members of
' the local group, and membership in nested domain groups.
'
' You have a royalty-free right to use, modify, reproduce, and
' distribute this script file in any way you find useful, provided that
' you agree that the copyright owner above has no warranty, obligations,
' or liability for such use.

Option Explicit

Dim objNetwork, objLocalGroup

' These attributes must be declared in the main program,
' so they are global in scope.
Dim objTrans, strComputer, strNetBIOSDomain

' Constants for the NameTranslate object.
Const ADS_NAME_INITTYPE_GC = 3
Const ADS_NAME_TYPE_NT4 = 3
Const ADS_NAME_TYPE_1779 = 1

' Determine NetBIOS name of domain and local computer.
Set objNetwork = CreateObject("Wscript.Network")
strNetBIOSDomain = objNetwork.UserDomain
strComputer = objNetwork.ComputerName
Set objNetwork = Nothing

' Bind to local Administrators group.
Set objLocalGroup = GetObject("WinNT://" & strComputer _
    & "/Administrators,group")

' Enumerate members of the local group.
Call EnumLocalGroup(objLocalGroup)

Sub EnumLocalGroup(ByVal objGroup)
    ' Subroutine to enumerate members of local group.
    ' The variable strComputer has global scope.

    Dim objMember

    ' Enumerate direct members of group.
    For Each objMember In objGroup.Members
        Wscript.Echo objMember.AdsPath
        ' Test if member is a group.
        If (LCase(objMember.Class) = "group") Then
            ' Nested group. Test if objMember is a local group.
            If (InStr(LCase(objMember.AdsPath), "/" _
                    & LCase(strComputer) & "/") > 0) Then
                ' objMember is a local group.
                ' Call sub recursively to enumerate nested local group.
                Call EnumLocalGroup(objMember)
            ElseIf (InStr(LCase(objMember.AdsPath), _
                    "/nt authority/") > 0) Then
                ' objMember is local implicit group (special identity).
                ' Membership cannot be enumerated.
            Else
                ' objMember is a domain group.
                ' Call sub that uses LDAP provider to enumerate
                ' nested domain group. objMember is bound with
                ' WinNT provider.
                Call EnumDomainGroup(objMember, True)
            End If
        End If
    Next

End Sub

Sub EnumDomainGroup(ByVal objDomainGroup, ByVal blnNT)
    ' Subroutine to enumerate members of domain group.
    ' blnNT is True if objDomainGroup is bound with WinNT,
    ' False if bound with LDAP.
    ' The variables objTrans and strNetBIOSDomain have global scope.

    Dim strNTName, strGroupDN, objGroup, objMember

    ' Check if this function called before.
    If (IsEmpty(objTrans) = True) Then
        ' objDomainGroup must be bound with WinNT.
        ' Setup NameTranslate. Connect to Global Catalog.
        Set objTrans = CreateObject("NameTranslate")
        objTrans.Init ADS_NAME_INITTYPE_GC, ""

        ' Convert NetBIOS name of group to Distinguished Name.
        strNTName = strNetBIOSDomain & "\" & objDomainGroup.Name
        objTrans.Set ADS_NAME_TYPE_NT4, strNTName
        strGroupDN = objTrans.Get(ADS_NAME_TYPE_1779)
        ' Escape any forward slash characters, "/", with the backslash
        ' escape character. All other characters that should be escaped are.
        strGroupDN = Replace(strGroupDN, "/", "\/")
    Else
        ' NameTranslate already setup. Check if objDomainGroup
        ' bound with WinNT.
        If (blnNT = True) Then
            ' Convert NetBIOS name of group to Distinguished Name.
            strNTName = strNetBIOSDomain & "\" & objDomainGroup.Name
            objTrans.Set ADS_NAME_TYPE_NT4, strNTName
            strGroupDN = objTrans.Get(ADS_NAME_TYPE_1779)
            ' Escape any forward slash characters, "/", with the backslash
            ' escape character. All other characters that should be escaped are.
            strGroupDN = Replace(strGroupDN, "/", "\/")
        Else
            ' objDomainGroup bound with LDAP. Retrieve Distinguished Name.
            strGroupDN = objDomainGroup.distinguishedName
            ' Escape any forward slash characters, "/", with the backslash
            ' escape character. All other characters that should be escaped are.
            strGroupDN = Replace(strGroupDN, "/", "\/")
        End If
    End If
    ' Bind to group with the LDAP provider, if required.
    If (blnNT = True) Then
        Set objGroup = GetObject("LDAP://" & strGroupDN)
    Else
        Set objGroup = objDomainGroup
    End If
    ' Enumerate direct members of objDomainGroup (bound with LDAP).
    For Each objMember In objGroup.Members
        Wscript.Echo objMember.AdsPath
        ' Check if objMember is a group.
        If (LCase(objMember.Class) = "group") Then
            ' Call sub recursively. objMember bound with LDAP.
            Call EnumDomainGroup(objMember, False)
        End If
    Next

End Sub