1 (изменено: Dmitrii, 2010-06-10 15:39:12)

Тема: VBScript: список групп домена, членом которых является пользователь

Определение полного списка групп безопасности Windows-домена, членом которых (явно или косвенно) является пользователь домена.
Анализируются группы любого типа, входящие в структуру Active Directory.
Метод определения косвенного ("вложенного") членства - итерационный просмотр.

Option Explicit
Dim objRoot 'объект для привязки к корню AD
Dim objGroups 'коллекция экземпляров класса "Группа безопасности", членом которых пользователь является непосредственно
Dim objGroup 'экземпляр класса "Группа безопасности"
Dim strDomain 'имя домена в форматах (последовательно): NetBIOS, RDN
Dim strDC 'имя контроллера домена в формате NetBIOS
Dim strUser 'имя пользователя, для котрого определяется членство в группах
            '(предполагается соответствие значению атрибута "Common-Name")
Dim objWsNet 'экземпляр класса "WScript.Network"
Dim objWMI 'объект для привязки к WMI-пространству, содержащему классы "Win32_UserAccount" и "Win32_GroupUser"
Dim objCollection, objItem, intNumber 'коллекция, экземпляр, кол-во экземпляров (соответственно) класса "Win32_UserAccount"
Dim objConnection, objCommand, objRSet 'объекты для получения списка групп безопасности с помощью ADO
Dim strCommandText 'текст запроса к AD с помощью ADO
Dim strPath, strTemp, arrTemp 'вспомогательные переменные
Dim blnHasResult 'флаг-признак прекращения итерационного просмотра
                 '(просмотр прекращается, если на текущем шаге итерации не найдено ни одной группы,
                 'членом которой пользователь был бы косвенно)
Dim strList 'итоговый список групп, членом которых является пользователь

'экземпляры класса "Scripting.Dictionary"
Dim dictResPositive 'уточняемый на каждом шаге итерации список групп, членом которых пользователь ЯВЛЯЕТСЯ
Dim dictResNegative 'уточняемый на каждом шаге итерации список групп, членом которых пользователь НЕ ЯВЛЯЕТСЯ
Dim dictTemp 'список групп, для которых на текущем шаге итерации проверяется косвенное членство пользователя

Const ADS_SCOPE_SUBTREE = 2 'флаг-признак, указывающий область просмотра дерева AD (всё дерево полностью)

strUser = Trim(InputBox("Имя пользователя:"))
If Len(strUser) > 0 Then
    Set objWsNet = CreateObject("WScript.Network")
    strDomain = objWsNet.UserDomain
    Set objWsNet = Nothing
    Set objRoot = GetObject("LDAP://RootDSE")
    strDC = objRoot.Get("dnsHostName")
    On Error Resume Next
    Set objWMI = GetObject("winmgmts:{impersonationLevel=impersonate}!\\" & strDC & "\root\cimv2")
    If Err.Number = 0 Then
        Set objCollection = objWMI.ExecQuery("SELECT * FROM Win32_UserAccount WHERE Domain='" & strDomain & "' AND Name='" & strUser & "'")
        intNumber = objCollection.Count
        If intNumber > 0 Then
            Set dictResPositive = CreateObject("Scripting.Dictionary")
            dictResPositive.CompareMode = 1
            Set dictResNegative = CreateObject("Scripting.Dictionary")
            dictResNegative.CompareMode = 1
            Set dictTemp = CreateObject("Scripting.Dictionary")
            dictTemp.CompareMode = 1
            'WScript.Echo "Ищем группы явного членства пользователя."
            For Each objItem In objCollection
                Set objGroups = objWMI.ExecQuery("ASSOCIATORS OF {Win32_UserAccount.Domain='" & strDomain & _
                                "',Name='" & strUser & "'} WHERE AssocClass=Win32_GroupUser")
                If objGroups.Count > 0 Then
                    For Each objGroup In objGroups
                        dictResPositive.Add objGroup.Name, True
                        'WScript.Echo "Пользователь - явный член группы " & objGroup.Name
                    Next
                End If
                Set objGroups = Nothing
            Next
            Set objItem = Nothing
            strDomain = objRoot.Get("DefaultNamingContext")
            Set objConnection = CreateObject("ADODB.Connection")
            objConnection.Provider = "ADsDSOObject"
            objConnection.Open "Active Directory Provider"
            Set objCommand = CreateObject("ADODB.Command")
            Set objCommand.ActiveConnection = objConnection
            objCommand.Properties("Page Size") = 1000
            objCommand.Properties("Timeout") = 30
            objCommand.Properties("Searchscope") = ADS_SCOPE_SUBTREE
            strCommandText = "SELECT ADsPath,cn FROM 'LDAP://" & strDomain & "' WHERE objectCategory='Group'"
            objCommand.CommandText = strCommandText
            Set objRSet = objCommand.Execute
            objRSet.MoveFirst
            Do
                strPath = objRSet.Fields("ADsPath").Value
                If dictResPositive.Exists(objRSet.Fields("cn").Value) Then
                    dictTemp.Add strPath, vbNull
                    'WScript.Echo "[ + ] " & strPath
                Else
                    dictResNegative.Add strPath, False
                    'WScript.Echo "[ - ] " & strPath
                End If
                objRSet.MoveNext
            Loop While Not objRSet.EOF
            Set objRSet = Nothing
            Set objCommand = Nothing
            objConnection.Close
            Set objConnection = Nothing
            'WScript.Echo vbNewLine & "Ищем группы косвенного членства пользователя."
            Do
                blnHasResult = False
                For Each strPath In dictResNegative.Keys
                    Set objGroup = GetObject(strPath)
                    'WScript.Echo "[ ? ] " & strPath
                    For Each strTemp In dictTemp.Keys
                        If objGroup.IsMember(strTemp) Then
                            dictResNegative.Item(strPath) = True
                            If Not blnHasResult Then blnHasResult = True
                        End If
                    Next
                Next
                If blnHasResult Then
                    dictTemp.RemoveAll
                    For Each strPath In dictResNegative.Keys
                        If dictResNegative.Item(strPath) Then
                            dictResPositive.Add strPath, False
                            dictTemp.Add strPath, vbNull
                            dictResNegative.Remove strPath
                            'WScript.Echo "[ + ] " & strPath
                        End If
                    Next
                End If
            Loop While blnHasResult
            Set objGroup = Nothing
            For Each strTemp In dictResPositive.Keys
                If dictResPositive.Item(strTemp) Then
                    strList = strList & strTemp & vbNewLine
                Else
                    arrTemp = Split(strTemp, ",")
                    strList = strList & Mid(arrTemp(0), 11) & vbNewLine
                    Erase arrTemp
                End If
            Next
            dictResPositive.RemoveAll
            dictResNegative.RemoveAll
            dictTemp.RemoveAll
            Set dictResPositive = Nothing
            Set dictResNegative = Nothing
            Set dictTemp = Nothing
            WScript.Echo "Итоговый список:" & vbNewLine & "===" & vbNewLine & strList
        Else
            WScript.Echo "Учётная запись " & UCase(strDomain & "\" & strUser) & " не обнаружена."
        End If
        Set objCollection = Nothing
    Else
        WScript.Echo "Ошибка подключения к контроллеру " & UCase(strDC) & ": " & Err.Description
        Err.Clear
    End If
    Set objWMI = Nothing
    Set objRoot = Nothing
    On Error GoTo 0
End If
WScript.Quit 0

Пример итогового списка для встроенной учётной записи администратора домена:

Администраторы домена
Администраторы предприятия
Администраторы схемы
Владельцы-создатели групповой политики
Пользователи домена
Администраторы
Пользователи
Группа с запрещением репликации паролей RODC

Примечания:
1) сценарий тестировался в домене с контроллерами на платформе Windows Server 2008 Std.;
2) для работы сценария необходимы полномочия администратора домена.