1

Тема: AD/HTA-VBS: Управление группами у/з на указанном DC

По долгу службы необходимо было десятки раз на дню переключаться в AD-оснастке на разные контроллеры домена для выдачи прав (групп) пользователям/машинам.


Т.к. основной программке работы с пользователем известен контроллер, на котором залогинен этот пользователь, сваял небольшое HTA, которое заменяет в известной мере саму AD-оснастку: позволяет выбрать из AD группы (по маске имени) и добавить пользователя в группы на указанном сервере или удалить его из выбранных из имеющихся у него групп.

Программку вызываю из основого приложения в качестве модального диалога, но реализована его работа и в качестве отдельного приложения.

Скрипт позволяет:

+ открыть спойлер

- получить группы у/з на контроллере (указанном явно либо текущем);
- выбрать из полученных групп некоторые (или все) для их удаления (или выдачи другой учётной записи);
- указать маску (начало имени) для выборки из AD группы - для последующего её добавления учётке;
- внести у/з в подготовленные группы на указанном сервере;
- исключить у/з из подготовленных групп на указанном сервере;

Если передавать параметрами учётку, контроллер и даже перечень групп, то многие действия будут автоматизированы.


Теоретически скрипт можно использовать и для того, чтобы облегчить включениеь у/з в группы последовательно на нескольких серверах, или для того, чтобы набор имеющихся групп у одного пользователя выдать другом.

Довольно удобно делать исключение из всех групп - скрипт не прерывает работу на предупреждения, если у вас нет прав на какую либо группу - не надо клацать на ОКей как в AD-оснастке.

Картинки и "мурзилку" можно увидеть здесь - sites.google.com/.../run_select_groups_for_add-hta

Сам код run_select_Groups_for_Add.hta:

+ открыть спойлер

<!-- Окно для управления группами учетной записи (пользователя или машины) на указанном контроллере.

Можно запускать из HTA-приложения в качестве ModelessDialog (см.ниже пример команды запуска) 
либо из любого другого приложения в качестве самостоятельного приложения (см.ниже параметры командной строки).

Позволяет:
 - получить группы у/з на контроллере (указанном явно либо текущем);
 - выбрать из полученных групп некоторые (или все) для их удаления (или выдачи другой учётной записи);
 - указать маску (начало имени) для выборки из AD группы - для последующего её добавления учётке;
 - внести у/з в подготовленные группы на указанном сервере;
 - исключить у/з из подготовленных групп на указанном сервере;
-------------------------------------------------------------

Параметры передаются одной строкой.  Синтаксис строки параметров "своеобразный":

   "УЗ[~comp][`Сервер[`Группа1[~Группа2[~....]]]]"

где:
 - УЗ - учетная запись пользователя (один из аттрибутов: distinguishedName / samAccountName / mail / givenName sn / sn givenName) 
          или у/з машины (distinguishedName / cn);
 - "~comp" после УЗ - признак того, что у/з является компьютером, а не пользователем;
 - символ "`" - разделитель параметров в строке;
 - Сервер - контроллер, на котором производятся операции с группами;
 - Группа - добавляемая/удаляемая группа (синтаксис - distinguishedName), групп может быть целый перечень, 
    разделение групп между собой - символом "~".
-------------------------------------------------------------- -->

<!--Примеры (VBScript) запуска из HTA-приложения диалога выбора и добавления групп в Моделесс-окне

window.showModelessDialog  _
    "run_select_Groups_for_Add.hta", _
    sDN & "`" & sDC & "`" & listGrp, _
    "dialogHeight:800px;dialogWidth:855px"
'
'где: sDN - distinguishedName у/записи
'     sDC - имя Domain Controller
'     listGrp - список добавляемых/удадяемых групп (distinguishedName), в качестве разделителя - символ "~"

' Запуск диалога просмотра групп пользователя для выбора и удаления из групп на указанном контроллере
window.showModelessDialog  _
    "run_select_Groups_for_Add.hta", _
    "PupkinV`myDC", _
    "dialogHeight:800px;dialogWidth:855px"

' Запуск диалога для добавления указанной группы компьютеру (+ возможность выбора и удаления других групп) на указанном контроллере
window.showModelessDialog  _
    "run_select_Groups_for_Add.hta", _
    "pupkinPC001~comp`myDC`LDAP://CN=SI Soft1,OU=Software groups,OU=Groups,DC=myFirma,DC=com", _
    "dialogHeight:800px;dialogWidth:855px"
-------------------------------------------------------------- -->



<!--Примеры запуска не в виде диалогового окна, а из командной строки.

При таком старте вся строка с параметрами обязательно должна быть взята в кавычки - "....".
Можно передавать все параметры и одной строкой и каждый из параметров отдельно брать в кавычки и разделять их пробелом:
     run_select_Groups_for_Add.hta "УЗ[~comp]" ["Сервер"] ["Группы через ~-разделитель"]

Запуск с указанием только лишь пользовательской у/з (логином):
  run_select_Groups_for_Add.hta "PupkinV"

Запуск с указанием пользовательской у/з (имейлом) и контроллера:
  run_select_Groups_for_Add.hta "Vasia.Pupkin@myMail.com`myDC"

Запуск с указанием пользовательской у/з (имейлом), контроллера и перечня групп:
  run_select_Groups_for_Add.hta "Vasia.Pupkin@myMail.com`myDC`LDAP://CN=SI Soft1,OU=Software groups,OU=Groups,DC=myFirma,DC=com~LDAP://CN=SI Soft2,OU=Software groups,OU=Groups,DC=myFirma,DC=com"
  run_select_Groups_for_Add.hta "Vasia.Pupkin@myMail.com" "myDC"
    
Запуск с указанием компьютерной у/з (имя машины) и контроллера:
  run_select_Groups_for_Add.hta "pupkinPC001~comp`myDC"

Запуск с указанием компьютерной у/з, контроллера и перечня групп:
  run_select_Groups_for_Add.hta "pupkinPC001~comp" "myDC" "LDAP://CN=SI Soft1,OU=Software groups,OU=Groups,DC=myFirma,DC=com~LDAP://CN=SI Soft2,OU=Software groups,OU=Groups,DC=myFirma,DC=com"

    -->

<HTML>
<HEAD>
<META content="text/html; charset=utf-8" http-equiv=Content-Type>
<TITLE>AD-selection groups to add them to account (v.2 by 29.11.2013)</TITLE>
<HTA:APPLICATION
APPLICATION ID="oSelectGroupsHTA"
APPLICATIONNAME="select_Groups"
BORDER = "thin"
CAPTION = "yes"
RESIZE = "no"
SHOWINTASKBAR = "yes"
SINGLEINSTANCE = "no"
SYSMENU = "yes"
WINDOWSTATE = "normal"
SCROLL = "no"
SCROLLFLAT = "yes"
VERSION = "2.0"
INNERBORDER = "no"
SELECTION = "yes"
MAXIMIZEBUTTON = "no"
MINIMIZEBUTTON = "yes"
NAVIGABLE = "yes"
CONTEXTMENU = "yes"
BORDERSTYLE = "normal">
</HTA>

<style>
select
{
   font-family: verdana;
   font-size: 7pt;
   width: 310px;
   margin-left: 0px;
}

.button
{
 font-family: verdana;
 font-size: 8pt;
}

.frameLog 
{
    width: 820px;
    height: 378px;
    border: 1px solid #000;
    overflow: auto;
        margin-left: 1px;
}

*{font-family:Verdana;font-size:11;}
</style>

<SCRIPT LANGUAGE="JScript">

var wshShell = new ActiveXObject( 'WScript.Shell' );

////// События перехвата клавиш приложения
document.attachEvent("onkeydown", ue_body_onkeydown);

function ue_body_onkeydown(){
// F2 - запись в группы
if (event.keyCode==113) ue_btDone();

// F3 - перечитывание групп у/з
if (event.keyCode==114) uf_Reload();

// F8 - признак удаления из групп
if (event.keyCode==119) idLbl_ChkDel.click();
}

// добавление строки в протокол и форсирование перерисовки окна программы
function uf_outWrite( pTxt ) {
    idDivLog.insertAdjacentHTML("beforeEnd",pTxt)
    wshShell.Run( 'ping /?' , 0 , true );
}

// возвращает полученную строку после преобразования в массив, сортировки и обратного преобразоваия в строку
// pDelim - символ разделения элементов массива
function uf_sortList (pWS, pDelim)
{
    arrWS = pWS.split(pDelim);
    arrWS.sort();
    var ret = "";
    ret = arrWS.join(pDelim);
    return ret;
}

// Выбрать все группы учетной записи
function uf_allUsersGroups(){
    var objSel = document.getElementById( "idSelectorUsersGroups" );
    var objTA  = document.getElementById( "ta_addedGrp" );
    var sVal;
    for (var i=0; i<objSel.length; i++) {
        sVal = objSel.options[i].value;
        if (sVal.length>0) {
            objTA.value += "\r\n";
            objTA.value += sVal;
        }
    }
}

// Изменение размеров и центровка окна (вызывается при вызове не в качестве диалога)
function uf_resizeMyWin(){
    var winWidth=865; // ширина окна
    var winHeight=850; // высота окна
    
    // изменяем размер
    window.resizeTo(winWidth, winHeight);

    // окно в центр экрана // http://habrahabr.ru/post/40707/
    var winPosX=screen.width/2-winWidth/2;
    var winPosY=screen.height/2-winHeight/2 - 20 ; // чуть приподнимаем
    window.moveTo(winPosX, winPosY);
}
</SCRIPT>


<SCRIPT LANGUAGE="VBScript">

' ----- константы глобальные для всего приложения
Const ADS_SERVER_BIND = &h200

Sub Window_Onload
    ta_addedGrp.Value = ""
    
    Dim sArg : sArg = "" ' строка с параметрами (либо из window.dialogArguments, либо .commandLine)'
    
    ' --- по идее в window.dialogArguments должно быть 3 аргумента разделенных символом "`" 
    Dim winArg : winArg = ""
    On Error Resume Next
    ' MsgBox window.dialogArguments,, "window.dialogArguments" '''for debug
    winArg = window.dialogArguments
    On Error GoTo 0
    
    Dim pArray ' массив для разбора параметров'
    Dim pArrDn
    
    If Len(winArg)>0 Then
        sArg = winArg
    Else
        ' При вызове из командной командной строки с параметрами, размеры окна командой запуска не передаются - меняем на свои
        uf_resizeMyWin
        
        ' параметры командной строки запуска
        ' from http://blogs.technet.com/b/heyscriptingguy/archive/2005/04/20/how-can-i-pass-command-line-variables-to-an-hta-when-it-starts.aspx'
        pArray = Split(oSelectGroupsHTA.commandLine, chr(34))
    For i = 3 to (Ubound(pArray) - 1) Step 2
            ' сливаем все аргументы в одну строку, если строка была разбита на части, а части были окаймлены кавычками'
            If Left(pArray(i),1)<>"`" AND Len(sArg)>0 then sArg = sArg & "`"
            sArg = sArg & pArray(i)
    Next
        ' Msgbox "commandLine = " & oSelectGroupsHTA.commandLine & vbCrLf & vbCrLf & "sArg = " & sArg '''for debug
    End IF

    If Len(sArg)>0 Then
        ' окно вызывалось в качестве диалога '
        On Error Resume Next
        pArray = Split(sArg, "`")
        If IsArray(pArray) Then
            ' 1-й -- "distinguishedName" добавляемой в группы у/з и через "~" - тип учетной записи'
            pArrDn = Split(pArray(0), "~")
            idAcc.value = pArrDn(0)
            ' --- учитываем в distinguishedName у/з возможный признак типа "компьютер" - наличие завершающей строки "~comp"
            If UBound(pArrDn)=1 Then
                If pArrDn(1)="comp" then rbtTypeAcc(1).checked = True
            End IF
            ' 2-й -- имя контроллера домена, на котором будет выполнятся запись в группы
            idDC.value  = pArray(1) 
            ' 3-й -- перечень групп, разделение - символ "~"
            ta_addedGrp.Value = Replace(pArray(2),"~", vbCrLf) 
        End IF
        On Error GoTo 0
    End IF

    If Len(idAcc.value)>0 Then
        uf_Reload() ' формируем список имеющихся у у/з групп'
    Else
        idSpnUsersGrpSel.InnerHTML = "<SELECT size='12' id='idSelectorUsersGroups' " &_
            "title='Enter / dblclick - добавление группы к выбранным, например, для удаления пользователя из групп' " &_
            ">" &_
            "<OPTION VALUE=''/>-- Select an Existing Group for Removing --</SELECT>"
    End IF

    ' --- выборку не выполняем - создаем пустой SELECT'
    idSpnGrpSelector.InnerHTML = "<SELECT id='selectGrp' NAME='FindedGrp' size='12' " &_
            "title='Результат выборки из AD групп по указанной выше маске поиска' " &_
            ">" &_
            "<OPTION VALUE=''/>-- Select an Group for Add/Remove --</SELECT>"
    document.all.idGrpName.select ' выделяем текст в поле ввода имени искомой группы'
    document.all.idGrpName.focus()
End SUB

'----------------------------------------------------------'
' Builds a <SELECT> form with finded groups in canonical format
' pName - condition to seek for groups (by their name).
'    Example: "AND (name='....*' OR ..... )"
'----------------------------------------------------------'
Function SelectFindedGrp ( pName )

    Dim defaultNC, AdConn, AdComm, AdRs, AdQuery
    SelectFindedGrp = _
        "<SELECT id='selectGrp' NAME='FindedGrp' SIZE='12' " &_
        "onkeypress='ue_keySelect();' " &_
        "ondblclick='ue_prompt();' " &_
        "title='Enter / dblclick - добавление группы к выбранным' " &_
        ">" &_
        "<OPTION VALUE=''/>-- Select an Group for Add/Remove --"
    defaultNC = GetObject("LDAP://RootDSE").Get("DefaultNamingContext")
    AdQuery = "SELECT AdsPath, cn " & _
                    " FROM 'LDAP://" & defaultNC & "'" & _
                    " WHERE objectCategory='group' " & pName
                    
    '-------------------------------------------------------------------'
    ' Create ADOBO connection and command objects and set search options'
    '-------------------------------------------------------------------'
    Set AdConn = CreateObject("ADODB.Connection")
    AdConn.Provider = "ADsDSOObject"
    AdConn.Open "Active Directory Provider"
    Set AdComm = CreateObject("ADODB.Command")
    AdComm.ActiveConnection = AdConn
    AdComm.Properties("SearchScope") = 2
    AdComm.Properties("Page Size") = 500
    AdComm.Properties("Sort On") = "cn" 
    '---------------------------------------'
    ' Set query text and execute the query. '
    '---------------------------------------'
    AdComm.CommandText = AdQuery
    Set AdRs = AdComm.Execute
    If Not AdRs.EOF Then
        With AdRs
            While Not .EOF
                AdsPath = .Fields("AdsPath")
                canonicalName = .Fields("cn")
                SelectFindedGrp = SelectFindedGrp & "<OPTION VALUE='" & AdsPath & "'/>" & canonicalName & vbCrLf
                .MoveNext
            Wend
        End With
    End IF 'Not AdRs.EOF'

    Set AdConn = Nothing
    Set AdComm = Nothing
    Set AdRs = Nothing
    
    SelectFindedGrp = SelectFindedGrp & "</SELECT>"
End Function

' Формируем список с группами у/з'
Sub uf_Reload
    
    If Len( idAcc.value )>0 Then
        If NOT uf_verifyCN() Then EXIT SUB ' не пройден контроль указанного значения у/з'
        
        Dim objConnection, objCommand, objRSet
        Set objConnection = CreateObject("ADODB.Connection")
        objConnection.Provider = "ADsDSOObject"
        objConnection.Properties("ADSI Flag") = ADS_SERVER_BIND
        objConnection.Open "Active Directory Provider"
        Set objCommand = CreateObject("ADODB.Command")
        Set objCommand.ActiveConnection = objConnection
        objCommand.Properties("Page Size") = 1000
        objCommand.Properties("Timeout") = 30
        
        ' Если выбрана вторая кнопка - rbtTypeAcc(1) - значит это не пользовательская у/з, а - компьютерная'
        Dim sFndCategory : sFndCategory = "(objectCategory=person)(objectClass=user)"
        If rbtTypeAcc(1).checked = true Then
            sFndCategory = "(objectCategory=Computer)"
        End IF
        
        Dim sCN : sCN = uf_GetCN_fromStr (idAcc.value) ' получаем значение cn из distinguishedName'
        Dim sFndExpr : sFndExpr = "cn=" & sCN
        
        objCommand.CommandText = _
            "<LDAP://" & idDC.value & ">;" &_
            "(&" & sFndCategory & "(" & sFndExpr & ")" &_
            ");memberof;Subtree"
        ' --- Запрос к AD с обработкой ошибки подключения'
        On Error Resume Next
        Set objRSet = objCommand.Execute
        If Err.Number <> 0 Then
            uf_outWrite _
                time() & "&nbsp;&nbsp;<b>Ошибка работы с AD.</b>&nbsp; <font color='red'>Error! #" & _
                CStr(Err.Number) & " Desc.: <b>" & Err.Description & "</b></font><br><br>"
            On Error GoTo 0
        else
            On Error GoTo 0
            If objRSet.RecordCount <> 1 Then
                MsgBox "Не найдена учетная запись - " & sCN & vbCr & vbCr & objCommand.CommandText,_
                    vbExclamation, "Неудачный поиск в AD"
                EXIT SUB
            End IF
            objRSet.MoveFirst
            
            '--- группы юзера'
            Dim iCnt : iCnt = 0
            Dim sOption : sOption = ""
            Dim sMemberOf
            Dim aMemberOf : aMemberOf = objRSet.Fields("memberof")
            If IsArray(aMemberOf) Then
                For iMemb = 0 to UBound(aMemberOf)
                    aMemberOf(iMemb) = "LDAP://" & aMemberOf(iMemb)
                NEXT
                sMemberOf = Join(aMemberOf, vbCr)
                sMemberOf = uf_sortList (sMemberOf, vbCr) 'сортируем список групп'
                aMemberOf = Split(sMemberOf, vbCr) 'возвращаем в массив после сортировки
                
                For iMemb = 0 to UBound(aMemberOf)
                    sOption = sOption & "<OPTION VALUE='" & aMemberOf(iMemb) & "'/>" & uf_GetCN_fromStr( aMemberOf(iMemb) ) & vbCrLf
                    iCnt = iCnt + 1
                NEXT
            else 'IsArray(aMemberOf)' - групп у пользователя еще нет
                sMemberOf = "Учетка ни в какие группы не входит!"
                sOption = sOption & "<OPTION />" & sMemberOf & vbCrLf
            End IF
            Set objRSet = Nothing
            Set objCommand = Nothing
            Set objConnection = Nothing
            uf_outWrite _
                "Получено " & time() & " - <b>" & CStr(iCnt) & "</b> гр. учетной записи [" & sFndExpr & "]<br>"
        End IF 'err<>0'
        sOption = _
            "<SELECT id='idSelectorUsersGroups' SIZE='12' " &_
            "onkeypress='ue_keySelectUsrGrp();' " &_
            "ondblclick='ue_promptUsrGrp();' " &_
            "title='Enter / dblclick - добавление группы к выбранным' " &_
            ">" &_
            "<OPTION VALUE=''/>-- Select an Existing Group for Removing --" & sOption & "</SELECT>"
        idSpnUsersGrpSel.innerHTML = sOption
    End IF 'Len(sLDAP)>0'
    
End SUB

' --- uf_GetCN_fromStr() ----------------------------------------------- 
' получение из строки "CN" - текст от "CN=" до ","
' "CN=" может быть в любом месте параметра
Function uf_GetCN_fromStr (sParam)
    Dim ret, iPosBeg, iPosEnd
    ret = sParam
    iPosBeg = InStr(sParam,"CN=")
    If iPosBeg>0 Then
        iPosEnd = InStr(iPosBeg, sParam, ",")
        If iPosEnd>0 Then
            ret = Mid(sParam, iPosBeg + 3, iPosEnd - (iPosBeg + 3) )
        End IF
    End IF
    uf_GetCN_fromStr = ret
End Function
'--------------------------------------------------------------

' --- uf_verifyCN() ----------------------------------------------- 
' Проверка наличия "CN=" в поле учетной записи. Установка контроллера, если не был указан.
' Например, после ручного редактирования у/з или передачи его параметром не в формате distinguishedName.
Function uf_verifyCN ()

    Dim sAcc : sAcc = idAcc.value
    If Len(sAcc)=0 Then
        uf_outWrite "<font color='darkred'>Не указана у/з !</font><br>"
        MsgBox "Не указана учетная запись", vbCritical, "Контроль у/з"
        uf_verifyCN = False
    else
        If Len(idDC.value)=0 Then ' не указан сервер DC - определяем свой'
            Dim objRoot, sSrvName
            Set objRoot = GetObject("LDAP://RootDSE")
            sSrvName = objRoot.Get("ServerName") 
            If InStr(sSrvName,",")>0 Then sSrvName = Left(sSrvName, InStr(sSrvName,",") -1 )
            If InStr(sSrvName,"=")>0 Then sSrvName = Right(sSrvName, Len(sSrvName) - InStr(sSrvName,"=") )
            'протоколируем поиск DC'
            uf_outWrite "<hr>Не был указан DC! Определяем текущий: <b>" & sSrvName &"</b>"
            idDC.value = sSrvName
            Set objRoot = Nothing
        End IF

        If UCase(Left(sAcc,3))="CN=" Then
            uf_verifyCN = True ' раз строка начинается на CN=, то предполагаем, что это уже корректный distinguishedName'
        Else
        
            ' --- Формирование условия поиска и запрос в AD указанной у/з, определение однозначности найденного значения
            
            Dim objConnection, objCommand, objRSet
            Set objConnection = CreateObject("ADODB.Connection")
            objConnection.Provider = "ADsDSOObject"
            objConnection.Properties("ADSI Flag") = ADS_SERVER_BIND
            objConnection.Open "Active Directory Provider"
            Set objCommand = CreateObject("ADODB.Command")
            Set objCommand.ActiveConnection = objConnection
            objCommand.Properties("Page Size") = 1000
            objCommand.Properties("Timeout") = 30
            ' --- Формирование условия поиска '
            ' Если выбрана вторая кнопка - rbtTypeAcc(1) - значит это не пользовательская у/з, а - компьютерная'
            Dim sFndCategory, sFndExpr 
            If rbtTypeAcc(1).checked = true Then
                sFndCategory = "(objectCategory=Computer)"
                ' для машины вариантов нет - ищем только по ее имени'
                sFndExpr = "cn=" & sAcc
            else
                sFndCategory = "(objectCategory=person)(objectClass=user)"
                ' для пользователя есть варианты: могли указать: учетку / имейл / имя+фамилию / фамилию+имя'
                If InStr(sAcc," ")>0 Then ' имеется пробел, проверяем - единственный ли он?'
                    If InStr(sAcc," ")=InStrRev(sAcc," ") Then
                        ' ищем: имя+фамилию OR фамилию+имя'
                        Dim arrNm : arrNm = Split(sAcc, " ")
                        sFndExpr = "(|" & _
                            "(&(givenName=" & arrNm(0) & ")(sn=" & arrNm(1) & "))" & _
                            "(&(givenName=" & arrNm(1) & ")(sn=" & arrNm(0) & "))" & _
                            ")"
                    Else ' пробел в искомой строке не единственный, предположим, что это значение displayName / cn'
                        sFndExpr = "displayName=" & sAcc
                        sFndExpr = "(|" & _
                            "(displayName=" & sAcc & ")" & _
                            "(cn=" & sAcc & ")" & _
                            ")"
                    End IF
                Else
                    If InStr(sAcc,"@")>0 Then 'видимо - это имейл'
                        sFndExpr = "mail=" & sAcc
                    Else 'возможно, что это логин'
                        sFndExpr = "samAccountName=" & sAcc
                    End IF
                End IF
            End IF
            
            '--- поиск у/з'
            objCommand.CommandText = _
                "<LDAP://" & idDC.value & ">;" &_
                "(&" & sFndCategory & "(" & sFndExpr & ")" &_
                ");distinguishedName;Subtree"
            'протоколируем строку поиска'
            uf_outWrite "<hr><b>Поиск у/з:</b><br>" & Replace(Replace(objCommand.CommandText,">","&gt;"),"<","&lt;") & "<br>"
            Set objRSet = objCommand.Execute
            uf_outWrite "Количество записей в результате поиска: <b>" & CStr(objRSet.RecordCount) & "</b><hr>"
            If objRSet.RecordCount <> 1 Then
                MsgBox "Не найдена учетная запись - " & sAcc & vbCr & vbCr & objCommand.CommandText,_
                    vbExclamation, "Неудачный поиск в AD"
                    uf_verifyCN = False
            else
                objRSet.MoveFirst
                idAcc.value = objRSet.Fields("distinguishedName").Value
                uf_verifyCN = True
            End IF
            
            Set objRSet = Nothing
            Set objCommand = Nothing
            Set objConnection = Nothing
        End IF
    
    End IF 'Len(sAcc)'

End Function
'--------------------------------------------------------------

' нажатие на Enter в поле ввода у/з -- старт запроса групп у/з (и поиска самой у/з)'
Sub ue_keyAccName
    If window.event.keyCode = 13 Then uf_Reload
End SUB

' нажатие на Enter в поле ввода имени группы -- старт поиска группы'
Sub ue_keyGrpName
    If window.event.keyCode = 13 Then uf_search
End SUB

' переформируем Select новым AD-запросом с указанием фильтра с именем группы'
Sub uf_search
    idGrpName.value = LTrim(idGrpName.value)
    If Len(idGrpName.value)>0 Then
        idSpnGrpSelector.InnerHTML = SelectFindedGrp( "AND (name='" & idGrpName.value & "*')" )
        document.all.selectGrp.focus()
        document.all.selectGrp.selectedIndex = 1
        If document.all.selectGrp.length = 2 Then ' была найдена единственная группа - её и добавляем'
            ue_prompt
        End IF
    End IF
End SUB


' нажатие на Enter в Select-е -- добавление выбранной в списке группы'
Sub ue_keySelect
    If window.event.keyCode = 13 Then ue_prompt
End SUB

' добавляем выбранную группу в строку для возврата результата'
Sub ue_prompt
    If Len(FindedGrp.value)>0 Then
        If Len(ta_addedGrp.value)>0 then ta_addedGrp.value = ta_addedGrp.value & vbCrLf
        ta_addedGrp.value = ta_addedGrp.value & FindedGrp.value
        document.all.idGrpName.focus()
        document.all.idGrpName.select ' выделяем текст в поле ввода имени искомой группы'
        document.all.idDivQuit.style.display = "none" ' выключаем видимость кнопки выхода'
    End IF
End SUB

' нажатие на Enter в Select-е -- добавление выбранной в списке группы'
Sub ue_keySelectUsrGrp
    If window.event.keyCode = 13 Then ue_promptUsrGrp
End SUB

' добавляем выбранную группу в строку для возврата результата'
Sub ue_promptUsrGrp
    If Len(idSelectorUsersGroups.value)>0 Then
        If Len(ta_addedGrp.value)>0 then ta_addedGrp.value = ta_addedGrp.value & vbCrLf
        ta_addedGrp.value = ta_addedGrp.value & idSelectorUsersGroups.value
        document.all.idDivQuit.style.display = "none" ' выключаем видимость кнопки выхода'
    End IF
End SUB

' --- запись у/з в группы или удаление из групп'
Sub ue_btDone
    idDC.value  = Trim (idDC.value)
    idAcc.value = Trim (idAcc.value)
    If NOT uf_verifyCN() Then EXIT SUB ' не пройден контроль указанного значения у/з'
    
    uf_outWrite "<i>Writing to AD...</i>&nbsp;"
    id_btWrite.focus()
    
    ' --- настройка работы с группами AD - учет включенного признака удаления из групп
    Const ADS_PROPERTY_APPEND = 3
    Const ADS_PROPERTY_DELETE = 4
    Dim iType, sType
    iType = ADS_PROPERTY_APPEND
    sType = "Added"
    If idChkDel.checked Then
        If MsgBox("Удалять у/з из групп ?", vbQuestion + vbOkCancel, "Подтверждение удаления")=vbOK then 
            iType = ADS_PROPERTY_DELETE
            sType = "Removed"
        else
            EXIT SUB
        End IF
    End IF
    If iType = ADS_PROPERTY_APPEND Then
        uf_outWrite "<b>Старт добавления в группы</b>&nbsp;" & Time() & "<hr>"
    Else
        uf_outWrite "<b>Старт удаления из групп</b>&nbsp;" & Time() & "<hr>"
    End IF
    
    On Error Resume Next
    
    Dim iCntOK, iCntErr : iCntOK = 0 : iCntErr = 0 'Счетчики успешности записи в группы'
    
    ' --- Формируем массив с выбранными группами и проходим по ним циклом'
    Dim grpArray, i, sLDAP, objGroup, strReturn
    strReturn = ta_addedGrp.Value
    grpArray = Split(strReturn, vbCrLf)
    
    For i=0 To UBound(grpArray)
        sLDAP = grpArray (i)
        If Len(sLDAP)>0 Then
            ' Добавляем имя DC, если таковое было указано'
            If Len(idDC.value)>0 then sLDAP = Replace(sLDAP, "://", "://" & idDC.value & "/")
            uf_outWrite sLDAP & "<br>"
            
            Err.Clear
            Set objGroup = GetObject ( sLDAP ) 
            objGroup.PutEx iType, "member", Array( idAcc.value)
            objGroup.SetInfo
            If Err.Number=0 Then
                uf_outWrite "&nbsp;&nbsp;<font color='green'><b>" &sType& ".</b></font><br><br>"
                iCntOK = iCntOK + 1
            Else
                uf_outWrite _
                    "&nbsp;&nbsp;<font color='red'>Error! #" & CStr(Err.Number) & " Desc.: <b>" & Err.Description & "</b></font><br><br>"
                iCntErr = iCntErr + 1
            End IF
        End IF
        ' Включаем отображение кнопки "Quit", но саму ее до окончания записи в группы дизейблим'
        document.all.idDivQuit.style.display = "block"
        document.all.quitbutton.disabled = True
        document.all.quitbutton.focus()
    Next
    
    On Error GoTo 0
    If document.all.idDivQuit.style.display = "block" Then
        document.all.quitbutton.disabled = False
    End IF
    uf_outWrite "<span style='width:50%'><b>Завершение работы с группами</b>&nbsp;" & Time() &"</span><i>" &_
        "&nbsp;Запись успешна: <font color='green'><b>" & CStr(iCntOK) & "</b></font>" &_
        "&nbsp;Ошибок записи: <font color='red'><b>" & CStr(iCntErr) & "</b></font>" &_
        "</i><hr>"
    uf_Reload() ' формируем список имеющихся у пользователя групп'
    If document.all.idDivQuit.style.display = "block" Then
        document.all.quitbutton.focus()
    End IF
End Sub


</SCRIPT>
</HEAD>

<BODY bgcolor="#E5ECF9">
<table>
    <tr>
        <td width="22%" align="Right">Искомая группа (маска):<br>
        (Enter - поиск)</td>
        <td>
            <input type="text" id=idGrpName size="20" value = "si sql " 
                title="Ввод начала имени искомой группы. &#13;&#13;<Enter>- поиск введенного выражения"
                onkeypress="ue_keyGrpName();">
                <input class="button" type="button" value="Search in AD"
                onClick="uf_search();" TITLE="Поиск по маске">
        </td>
        <td>
            <input class="button" type="button" id="id_btReload" value="F3 - Reload memberships"
                style="background-color: powderblue;"
                onClick="uf_Reload();" TITLE="Список существующих у учетной записи групп">
            <input class="button" type="button" value="Select All"
                onClick="uf_allUsersGroups();" TITLE="Выбрать все группы учетной записи">
        </td>
    </tr> 
    <tr>
        <td align="Right">Выбор из списка групп<br>
        (Enter / dblclick)</td>
        <td><SPAN ID=idSpnGrpSelector>&nbsp;</SPAN></td>
        <td><SPAN ID=idSpnUsersGrpSel>&nbsp;</SPAN></td>
    </tr> 
    <tr>
        <td align="Right">
            <b>Выбранные группы:</b> 
        </td>
        <td colspan="2" align="Right">
            <input class="button" type="button" value="Clear List" 
                onClick="document.getElementById('ta_addedGrp').value='';" TITLE="Очистка списка выбранных групп">
        </td>
    </tr>
    <tr>
        <td colspan="3" align="center">
            <TEXTAREA cols=115 name="ta_addedGrp" rows=10
            style="background-color: lavender; font-family: Courier New; font-size: 11;"
            title="Список выбранных групп">
            </TEXTAREA>
        </td>
    </tr>
    <tr>
        <td align="Right">Учётная запись</td>
        <td colspan="2"><input type="text" id="idAcc" size="60" value = ""
                onkeypress="ue_keyAccName();"
                style="background-color: lavender; font-family: Courier New; font-size: 11;"
                title="У/З добавляемая в выбранные группы (или для удаления из выбранных групп). &#13;&#13;Указывать компьютер - по имени, пользователя - по его логину, и-мейлу, имени_фамилии, фамилии_имени. &#13;&#13;<Enter> - поиск у/з.">
            &nbsp;&nbsp;&nbsp;&nbsp;
            <LABEL title="Тип у/з - пользователь (настройка для 'Reload memberships' и для поиска значения, указаного не формате distinguishedName)">
                <INPUT type="radio" name="rbtTypeAcc" value="user" checked="checked"> User
            </LABEL>
            <LABEL title="Тип у/з - компьютер (настройка для 'Reload memberships)'">
                <INPUT type="radio" name="rbtTypeAcc" value="comp"> Computer
            </LABEL>
        </td>
    </tr> 
    <tr>
        <td align="Right">Контроллер домена<br>(для команд записи)</td>
        <td colspan="2"><input type="text" id="idDC" size="15" value = "" 
                style="background-color: lavender; font-family: Courier New; font-size: 11;"
                title="Контроллер, на котором будет выполняться добавление в группы">
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
            <LABEL title="Тип запроса к AD:[_] - добавление в группу / [x] - удаление из группы"
            style="color:darkred;" id="idLbl_ChkDel">
            <input type=checkbox id="idChkDel" > F8 -- Switch feature removing of the groups 
            </LABEL>
      <input class="button" type="button" id="id_btWrite" value="F2 -- Write to groups"
            style="background-color: lightskyblue;"
            onClick="ue_btDone();" TITLE="Внесение изменений в выбранные группы">
        </td>
    </tr> 
    <tr>
        <td colspan="3" align="center">
            <!-- ----- Слой со стилем прокрутки протокола работы ---- -->
            <div style="font-family: verdana; font-size: 10;"
                title="Протокол работы"
                class="frameLog">
                <div id=idDivLog align=left></div>
                <div id=idDivQuit style="display:none" align=center>
                    <input id=quitbutton class="button" type="button" value="Quit" name="quit_button" 
                        onClick="window.close();" TITLE="Выход из диалога">
                </div>
            </div>
        </td>
    </tr> 
            
</table>

</BODY>
</HTML>


WBR. Roman