Доработка сценария позволяет использовать в нем вместо DN имена пользователей
В статье "Обновление атрибутов AD без проблем" (http://www.osp.ru/text/302/3634245/) был описан сценарий, с помощью которого можно обновлять атрибуты объектов Active Directory (AD), используя для этого данные из файлов формата CSV (значения, разделяемые запятой) или из электронных таблиц Excel. С момента публикации данной статьи мне пришло большое количество писем от администраторов из разных стран мира. В этих письмах говорится, что описанный в статье сценарий updateattribs.vbs позволил им сэкономить массу времени при выполнении повседневных работ по поддержанию актуальности данных о пользователях в AD при различных перемещениях и слияниях внутри их предприятий.Для того чтобы использовать updateattribs.vbs вы должны знать отличительные (DN) имена тех пользовательских объектов, атрибуты которых предполагается изменить. Эти данные легко получить с помощью таких утилит командной строки Microsoft как ldifde.exe, csvde.exe, или dsquery.exe. Тем не менее, в письмах многих администраторов содержались вопросы о том, как можно получить данные об именах DN пользователей, поэтому я решил несколько упростить данный сценарий, с тем чтобы в качестве входных данных для него можно было бы использовать обычные имена пользователей. Кроме этого я изменил неименованные параметры на именованные.

Исходная версия

Давайте на вернемся назад и вспомним, как работает исходный сценарий. Для его запуска необходимо, по меньшей мере, два параметра. Первый параметр – имя файла входных данных, имеющего формат CSV или таблицы Excel. Второй параметр – имя выходного файла, в котором сценарий будет фиксировать все выполняемые им действия.На Рис. 1 показан пример входного файла, который имеет строку заголовка, содержащую имена обновляемых атрибутов. Первым по порядку стоит имя DN. При обработке каждой строки входного файла (разумеется, за исключением строки заголовка) сценарий обращается к провайдеру LDAP (Lightweight Directory Access Protocol) для установления связи с объектом, имеющим заданное имя DN, после чего выполняется обновление каждого атрибута, теми значениями, которые указаны во входном файле. В целях экономии времени сценарий обновляет только те атрибуты, значения которых в AD отличаются от значений, заданных во входном файле. И, наконец, результаты работы сценария записываются в выходной файл, в котором содержатся статистические данные о том, сколько объектов было модифицировано, сколько изменено атрибутов, а также данные о том, сколько было ошибок в ходе выполнения сценария.

Новая версия сценария

Предыдущий сценарий работает хорошо и достаточно быстро, однако, как отмечалось выше, для его работы необходимо, чтобы были точно заданы имена DN всех тех пользовательских объектов, которые мы собираемся модифицировать. А поскольку формат имени DN не является тем типовым форматом, который мы обычно применяем для идентификации пользователей, я решил изменить код сценария таким образом, чтобы можно было использовать вместо DN обычные имена пользователей. Все мы знаем, как получать информацию об именах пользователей, поэтому вместо DN будет целесообразно использовать в качестве идентифицирующего поля эти имена. Однако для того чтобы вносить изменения в AD через LDAP, в сценарии имена пользователей все равно должны преобразовываться в имена DN.Значение username хранится в AD в атрибуте sAMAccountName пользовательской учетной записи. Таким образом, для каждого значения username из входного файла сценарий может запросить AD тот пользовательский объект, значение атрибута sAMAccountName которого равно заданному значению username. Атрибут sAMAccountName должен быть уникален для каждого пользователя, поэтому либо при выполнении данного запроса будет возвращен единственный результат, из которого сценарий сможет извлечь атрибут distinguishedName, либо не будет возвращено никаких данных, что соответствует случаю, когда пользователь с этим именем в домене отсутствует. После того как получено значение DN, оно может использоваться в сценарии как основа при вызове провайдера LDAP для выполнения обновления требуемых атрибутов пользователя.Еще одно изменение, которое было внесено в сценарий связано с использованием именованных аргументов вместо неименованных. При использовании именованных аргументов можно передавать их сценарию в любом порядке. Например, в типовом сценарии аргументы задаются в заданном порядке, как показано ниже:
myscript.vbs parameter1
parameter2
В случае же использования именованных аргументов сценарий может вызываться так:
myscript.vbs /p1:parameter1
/p2:parameter2
или так:
myscript.vbs /p2:parameter2
/p1:parameter1
В данном случае второй и третий варианты вызова являются идентичными, поскольку здесь все аргументы имеют соответствующие метки и поэтому могут обрабатываться сценарием в любой последовательности. Для того чтобы определить, были ли заданы при запуске сценария именованные аргументы, используется метод WScript.Arguments.Named.Exists, которому передается имя параметра, подлежащего проверке. Значение именованного параметра извлекается при помощи функции WScript.Arguments.Named.Item, которая считывает имя соответствующего аргумента и возвращает его значение. На Листинге 1 показан пример проверки наличия именованных параметров с извлечением соответствующих значений, которые затем записываются в переменные. Несмотря на то, что использование именованных аргументов не является насущной необходимостью для данного сценария, однако подобный синтаксис вызова будет более привычным для тех пользователей, которые привыкли указывать ключи при запуске утилит командной строки.

Подробное описание сценария

При вызове обновленной версии сценария должен использоваться следующий синтаксис:
updateattribs.vbs /i:inputfile
/o:outputfile
/basedn:DC=COMPANY,DC=COM
/v:yes
Здесь ключом /i задается входной файл, ключом /o определяется файл выходных данных, ключ /basedn задает имя DN контейнера (или узла в дереве AD), в котором будет производиться поиск учетных записей пользователей. Ключ /v определяет, следует ли выполнять детальный (verbose) вывод информации на консоль. В самом начале сценария производится считывание значений первых трех аргументов: входной файл, выходной файл и базовое имя DN. Четвертый параметр , /v, является необязательным. Имя входного файла должно указывать на файл CSV либо на таблицу Excel, как это показано на Рис. 2. В качестве первой строки данный файл должен иметь строку заголовка, в которой должны содержаться имена всех атрибутов, подлежащих обновлению, причем в первом столбце должно размещаться имя пользователя (username). В каждой последующей строке должны указываться требуемые значения соответствующих атрибутов пользователя, подлежащих изменению.Как вы можете видеть, новый входной файл существенно проще своего предшественника. Здесь, вместо того чтобы указывать полное имя DN по каждому пользовательскому объекту, достаточно просто задать имена пользователей, которым требуется изменить атрибуты. Еще одним преимуществом обновленных вариантов входного файла и сценария является то, что теперь можно корректировать изменения в службе AD вашей организации без необходимости внесения изменений во входной файл. Допустим, если пользователь с именем jsmith переносится из одного организационного подразделения (OU) в другое, то входной файл все равно остается корректным, поскольку имя пользователя (т.е. значение атрибута sAMAccountName) в данном случае не изменится, в отличие от его имени DN.В целях улучшения производительности в сценарии осуществляется привязка к LDAP://RootDSE, а данная ссылка сохраняется на протяжении всего сценария (см. Листинг 2). Этот прием позволяет сохранить открытый канал связи между провайдером LDAP и сервером, за счет чего последующие вызовы метода GetObject будут обрабатываться намного быстрее.Затем сценарий инициализирует выходной файл путем его создания, для чего используется функция OpenTextFile объекта Scripting.FileSystemObject. Здесь же сценарий создает экземпляр COM-объекта Excel.Application, с помощью которого открывается входной файл. После этого считывается первая строка данного файла, содержащая имена атрибутов, которые заносятся в массив attributeNames. Далее этот массив используется для задания имен тех пользовательских атрибутов, которые подлежат изменению. Далее запускается основной цикл сценария, обрабатывающий каждую строку входного файла. Начиная со второй строки (первой строки содержащей собственно данные), сценарий считывает значение первого столбца, т.е. username. Далее с помощью интерфейса ADO (ActiveX Data Objects) выполняется обращение к провайдеру ADs для выполнения поискового запроса к AD, начиная с уровня базового имени DN, заданного в соответствующем аргументе командной строки. Данный запрос выполняет поиск пользовательского объекта AD, значение атрибута sAMAccountName которого совпадает с заданным именем username, как это показано на Листинге 3. Если результирующий набор данных содержит некоторое значение, то это говорит о том, что требуемый пользователь найден, в противном случае сценарий выводит ошибку и переходит к следующей строке. Если совпадение обнаружено, тогда у найденного объекта считывается атрибут distinguishedName, после чего он записывается в переменную dn, а также осуществляется привязка к объекту с помощью метода GetObject. Данный метод возвращает ссылку на объект, которую сценарий сохраняет в переменной ADObject.Теперь можно модифицировать атрибуты тех пользовательских объектов, ссылки на которые определяются ADObject. Данная задача решается в сценарии путем циклической обработки массива attributeNames и сопоставления значений атрибутов, извлеченных из AD и имеющихся во входном файле. Если эти значения различаются, сценарий обновляет значения объекта AD с помощью метода Put. На самом деле метод Put заносит изменения не непосредственно в AD, а в локальный кэш. Чтобы актуализировать внесенные изменения, их необходимо зафиксировать (commit), для чего в сценарии используется метод Setinfo данного объекта.В ходе своего выполнения сценарий использует несколько переменных для хранения состояний счетчиков количества объектов и атрибутов, которые обрабатываются и изменяются. То же самое происходит с данными о количестве зафиксированных ошибок. Все эти данные передаются сценарием в суммарный отчет, который выводится на экран и заносится в выходной файл. Обратите внимание и на то, что когда атрибут обновляется, то оба его значения (и предыдущее, и обновленное) также записываются в выходной файл. Поэтому с помощью выходного файла вы всегда сможете проверить внесенные изменения и при необходимости вернуться к старым значениям.

Запуск сценария

Проверка функционирования новой версии сценария Updateattribs выполнялась на следующих клиентских операционных системах: Windows XP Service Pack 1 (SP1) и SP2, а также Windows 2000 Professional SP3 и SP4. Соответствующая среда AD, была реализована как на базе Windows Server 2003, так и Windows 2000 Server. Для работы сценария необходимо наличие на компьютере установленного пакета Excel 2000 или более поздней версии (это необходимо для обработки входного файла). Тестирование проводилось для версий Excel 2003, Excel 2002 и Excel 2000. Поскольку при использовании режима вывода информации сценарий будет выводить на экран значительный объем информации, я настоятельно рекомендую запускать его через cscript.exe.Ниже перечислены еще несколько нюансов, которые следует иметь в виду при использовании данного сценария:
  • При описании входного файла путь к нему должен указываться полностью (например, C:scriptsmyfile.csv). При наличии пробелов в описании пути, данную строку необходимо заключать в двойные кавычки (C:My Documentstest.csv).
  • Имена атрибутов, указанные во входном файле должны точно соответствовать именам из AD (для проверки можно воспользоваться оснасткой adsiedit.msc или аналогичным средством). При этом важно помнить, что имена атрибутов являются чувствительными к регистру.
  • Если сценарий не может разрешить имена username в имена DN, проверьте правильность задания базового DN. Ради простоты я обычно задаю здесь имя домена (скажем, DC=COMPANY,DC=COM; DC=CORP,DC=COMPANY,DC=COM), в результате чего я могу организовывать поиск по всему каталогу.
  • Если количество подлежащих обработке пользовательских объектов велико и при этом известно, что все пользователи, перечисленные во входном файле, размещаются в структуре определенного OU, тогда для ускорения процесса обработки можно указать более точное базовое имя DN (например, OU=SALES,DC=COMPANY,DC=COM или еще точнее – “OU=New York,OU=Sales,DC=COMPANY,DC=COM”).
Как и любой другой новый инструментарий, сценарий Updateattribs, прежде чем быть запущенным в промышленную эксплуатацию должен пройти проверку в вашей тестовой лаборатории. Данный сценарий выполняется очень быстро. В ходе тестов в инфраструктуре AD, содержащей более 100000 объектов, время обновления одной записи не превышало одной секунды.

РИСУНОК 1: Пример входного файла для исходного варианта сценария Dn,givenName,sn,title CN=Seguis, Steve,CN=Users,DC=scriptmation,DC=com”,Steve,Seguis,Admin CN=Guest,CN=Users,DC=scriptmation,DC=com”,Guest,User,Guest Account


РИСУНОК 2: Пример входного файла для обновленного варианта сценария Username,givenName,sn,title sseguis,Steve,Seguis,Admin jsmith,John,Smith,Desktop Technician 


Листинг 1: Код для проверки, извлечения и сохранения именованных параметров
Dim infile, outfile, basedn, verbose, argscol
Set argscol = WScript.Arguments.Named
‘ BEGIN COMMENT
‘Если входной файл не найден, отображается корректный синтаксис вызова.
‘ END COMMENT
If Not argscol.Exists(“i”) Then ShowSyntax
Else
infile = argscol.Item(“i”)
End If
 
‘ BEGIN COMMENT
‘Если выходной файл не найден, отображается корректный синтаксис вызова.
‘ END COMMENT
If Not argscol.Exists(“o”) Then ShowSyntax
Else
outfile = argscol.Item(“o”)
End If
 
‘ BEGIN COMMENT
‘Если не найден параметр baseDN, отображается корректный синтаксис вызова.
‘ END COMMENT
If Not argscol.Exists(“basedn”) Then
ShowSyntax
Else
basedn = argscol.Item(“basedn”)
End If
 
If argscol.Exists(“v”) Then
If LCase(argscol.Item(“v”)) = “yes” Then verbose = true
Else
verbose = false
End If
Else
verbose = false
End If

Листинг 2: Код для привязки к RootDSE и сохранения ссылки
Dim dso
Set dso = GetObject(“LDAP://RootDSE”)
If Err.Number <> 0 Then
WScript.Echo “Невозможно подключиться к: “ & “LDAP://RootDSE
WScript.Quit 1
End If

Листинг 3: Код обращения к провайдеру ADs для запроса поиска DN по заданному Username
Dim ADConn
Set ADConn = CreateObject(“ADODB.Connection”)
ADConn.Provider = “ADSDSOObject”
ADConn.Open “ADs Provider”
 
Dim query
query = “;(&(objectclass=user)(sAMAccountName=” _
& username & “));distinguishedName;subtree”
Dim ADrs
Set ADrs = ADConn.Execute(cstr(query))