Иллюстрация Бруно Мэлларта/Images.com Один человек не способен отлеживать все эти изменения вручную, однако с помощью сценария AccountTracker.vbs можно облегчить себе задачу.

Сценарий AccountTracker.vbs делает снимок определенных объектов каталога AD, таких как группы и члены групп, и прописывает отличительное имя (DN) каждого объекта, а также дату и категорию в файл формата .xml в форме базы данных ActiveX Data Objects (ADO). При каждом повторном запуске сценарий сравнивает новую базу данных с предыдущей. Используя простой процесс сравнения, можно обнаружить новые объекты каталога AD, а также объекты, исчезнувшие из него с момента последнего запуска сценария.

Как вы вскоре увидите, я создал этот сценарий для опроса определенных групп, однако вы сможете без труда добавить в код программы собственные запросы и начать мониторинг выбранных объектов. Сценарий работает с широким набором объектов AD и создает удобные, всесторонние отчеты.

Сценарий AccountTracker.vbs поможет анализировать ежедневную активность службы AD, но, что более важно, он даст вам возможность следить за появлением новых и удалением старых учетных записей и таких групп безопасности, как Enterprise Admins, Domain Admins и Administrators. С помощью этого сценария можно будет увидеть новые, перемещенные, отключенные или удаленные учетные записи пользователей и компьютеров, отслеживать изменения в подразделениях и в составе таких групп, как Server Operators и Account Operators.

Опрос наборов категорий AD

Основой алгоритма работы сценария является опрос двух типов категорий AD. Набор первого типа относится к запросам групп и классов, которые помогают находить объекты AD с помощью несложных запросов LDAP, перечисленных ниже.

  • Категория AdminGroups: любые группы с именами, содержащими строку Admin;
  • категория ComputersDisabled: отключенные учетные записи компьютеров;
  • категория ComputersEnabled: активные учетные записи компьютеров;
  • категория Groups: все группы;
  • категория GroupsNoMembers: пустые группы;
  • категория OUs: все подразделения;
  • категория Servers: все объекты-компьютеры, у которых значение атрибутa operatingSystem содержит строку Server;
  • категория ServiceAccounts: все учетные записи, у которых значение атрибута description содержит строку Service;
  • категория ServiceGroups: все группы, у которых значение атрибута sAMAccountName содержит строку Service;
  • категория UserAccountsDisabled: отключенные учетные записи пользователей.

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

  • Account Operators;
  • Administrators;
  • Backup Operators;
  • Domain Admins;
  • Enterprise Admins;
  • Replicator;
  • Schema Admins;
  • Server Operators.

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

Алгоритм работы сценария AccountTracker.vbs

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

Начиная с первого запуска сценария любые изменения, произошедшие с объектами заданных категорий, могут быть обнаружены путем простого сличения баз данных, созданных в результате двух последовательных запусков сценария. Сценарий проверяет каждую запись в «старой» базе и определяет, присутствует ли она в только что созданной. Если запись отсутствует в одной из баз, она заносится в таблицу Microsoft Excel. После обработки всех записей в книге Excel создается сводная таблица, отображающая изменения в каталоге AD (новые объекты и объекты, удаленные из каталога), и формируется точная картина изменений, произошедших за время между двумя запусками сценария.

Оптимальная частота использования сценария должна определяться на основании интенсивности изменений в составе домена. Чем выше интенсивность, тем чаще следует запускать процесс. Я выполняю поиск изменений каждый день, но при снижении активности могу изменить свой график и осуществлять проверку раз в неделю. Поэтому я написал сценарий таким образом, чтобы его можно было запускать как запланированную задачу. Я отказался от использования окон сообщений, созданных с помощью функции MsgBox из VBScript, — вместо этого я задействовал всплывающие окна, основанные на механизме WshShell.Popup из Windows Script Host (WSH). Окна сообщений не применяются в сценариях, запускаемых по расписанию, потому что они не исчезают после нажатия пользователем кнопки. В свою очередь, всплывающие окна появляются только на несколько секунд. Дополнительное преимущество всплывающих окон заключается в том, что вы сможете увидеть сообщения, даже если запустите сценарий вручную.

Базы данных, создаваемые и используемые в моем сценарии, содержат четыре названных выше поля: Rundate — дата запуска сценария, Category — элемент одного из двух описанных наборов категорий (например, UserAccountsDisabled), DN — отличительное имя объекта AD, CatDN — комбинация из значений полей Category и DN. Объединение значений двух полей необходимо для выполнения функций ADO при использовании процедуры Find для поиска записи в базе данных.

При всех достоинствах баз данных ADO, вы не сможете использовать оператор AND в процедуре Find, а мой сценарий построен на процедуре поиска определенной комбинации категории и уникального имени. Альтернативой методу Find является процедура Filter, которая позволяет использовать оператор AND. Однако я обнаружил, что применение метода Filter в базах данных большого и среднего размера (содержащих более 500 записей) ведет к снижению производительности компьютера. Я решил увеличить размер поискового пространства, используемого сценарием для повышения производительности, и объединил два поля. Благодаря этому я смог использовать более быстрый метод Find.

Необходимо тщательно продумать место хранения баз данных. В зависимости от размера вашего домена, объем баз данных может достигать нескольких мегабайтов для каждого запуска сценария. На данный момент каждая из моих баз данных занимает приблизительно 3,5 Mбайт. Конечно, при необходимости вы можете сжать старые базы данных. Файлы .xml сжимаются достаточно хорошо: файл размером 3,5 Mбайт в архиве занимает на диске приблизительно 145 Кбайт. Для изменения папки, назначенной «по умолчанию» для хранения баз данных, найдите в теле сценария строку

DBPath = C:\Scripts\ADacctTrack\

и измените значение C:\Scripts\ADacctTrack\ на соответствующий путь.

При первом запуске сценария будут просто созданы базы данных XML, так как на данный момент ему еще нечего сравнивать. Новая база данных после завершения сценария сохраняется под именем NewestAcctTracker.xml. На втором запуске сценария старая база данных переписывается в файл PreviousAcctTracker.xml, а база, создаваемая при текущем запуске, получает имя NewestAcctTracker.xml. На третьем и всех последующих запусках база данных с именем PreviousAcctTracker.xml переименовывается в файл ArcAcctTrackerDateTime.xml (например, ArcAcctTracker09-26-20081305-
45.xml).

Строка DateTime всегда будет соответствовать значению атрибута даты последнего изменения DateLastModified файла PreviousAcctTracker.xml до его переименования. Я получаю это значение, используя метод GetFile объекта Scripting.FileSystemObject для доступа к свойствам файла PreviousAcctTracker.xml. Полученное значение помещается в переменную DateTime, где при необходимости к дате приписываются нули в старших разрядах (например, 07/07/2008), временная часть значения конвертируется в 24-часовой формат (например, 1307:54), а каждый из символов «слэш» (/) и «двоеточие» (:) заменяются на символ «дефис» (-). Такой порядок именования позволяет легко найти определенную базу данных по дате создания. Кроме того, он дает возможность более точно сортировать файлы по имени.

Перед тем как перейти к изучению кода, мне хотелось бы сделать еще одно замечание по алгоритму сценария. При запуске сценарий создает новый независимый набор записей ADO. После того как сценарий проведет опрос по каждой из категорий и сохранит результаты в базе данных ADO, он открывает созданную ранее базу данных, последовательно просматривает новые записи в базе ADO и пытается найти эти записи в старой базе. Если запись отсутствует в старой базе данных, она рассматривается как новая и заносится в лист Excel. При этом каждая запись, добавленная на лист электронной таблицы, включает в себя:

  • поле Status со значением New;
  • поле Category, значение которого ссылается на поле Category записи в текущей базе данных;
  • поле DN, значение которого ссылается на поле DN записи в текущей базе данных;
  • поле Note со значением Not in Previous List.

После того как будет достигнут конец файла текущей базы данных, сценарий начинает последовательно просматривать все записи в старой базе данных и для каждой пытается найти соответствие в базе текущей. Если соответствия для записи не оказалось, она рассматривается как не найденная, а данные из старой базы данных заносятся в лист электронной таблицы. В данном случае полю Status присваивается значение Not Found, а полю Note — значение In Previous — Not in Most Recent List.

Значение Not Found говорит о том, что рассматриваемый объект был удален, перемещен, переименован или отключен. В любом случае исходные отличительное имя и категория объекта более не актуальны. Если объект не был удален, то весьма вероятно, что он появится в одной из других категорий с пометкой New. В дальнейшем вы увидите, что я сортирую основной лист электронной таблицы по отличительному имени, а не по полям Status или Category — такой метод сортировки облегчает выявление перемещенных, отключенных или переименованных объектов, так как записи группируются по значению поля DN.

Изучение кода

Хотя большая часть кода достаточно понятна, я подробно остановлюсь на основных моментах, вместо того чтобы выполнять построчный анализ кода. Фрагмент сценария, приведенный в листинге 1, содержит код, создающий массивы, используемые сценарием AccountTracker.vbs для опроса категорий AD. Хотя коду, приведенному в листинге 1, должна предшествовать значительная часть тела сценария, представленных строк вполне достаточно для понимания идеи алгоритма.

Код фрагмента A использует выражение Dim для объявления массива Categories, который содержит 11 элементов. Далее каждому элементу присваивается значение. При изменении данного блока будьте внимательны. Если вы добавляете или удаляете какие-либо элементы, при этом необходимо указать в операторе Dim новое количество элементов. Эти элементы формируют набор имен категорий первого типа, которые прописываются в базе данных вместе с уникальными именами объектов AD.

Код фрагмента B объявляет массив LDAPFilter, который содержит выражения, используемые в запросах LDAP для категорий, описанных в массиве Categories. Все без исключения выражения запроса должны соответствовать определенной категории.

Давайте взглянем на один из запросов LDAP — он хранится в нулевом элементе массива LDAPFilter. Этот запрос соответствует значению нулевого элемента (AdminGroups) массива Categories. Проанализировав выражение LDAP, вы можете увидеть, что запрос ищет объекты AD со значением атрибута objectCategory равным group, у которых в значении атрибута sAMAccountName содержится строчка admin.

Обратите внимание, что каждый элемент массива LDAPFilter создан в соответствии с определенным элементом массива Categories. Важно поддерживать связь между двумя массивами, потому что, как мы вскоре увидим, соответствующая категория прописывается в базу данных для каждого найденного объекта.

Код фрагмента C осуществляет сортировку набора записей, при этом база данных сортируется по полю CatDN в порядке возрастания. После этого с помощью цикла For… Next я обращаюсь к каждому элементу массива LDAPFilter и помещаю значение элемента в строковую переменную, которая будет использована для выборки объектов AD, попадающих под ту или иную категорию. Следующие выражения отвечают за создание строки запроса LDAP:

strQuery = ";" _
   & LDAPFilter (i) _
   & "; DistinguishedName; subtree"

Выполнение следующих строк приводит к активации запроса:

objCommand.CommandText = strQuery
Set objRecordSet =
   objCommand.Execute

После этого сценарий обрабатывает полученный набор записей и прописывает информацию о найденных объектах в базу данных ADO (см. тело цикла Do… Loop фрагмента C). Этот цикл повторяется для каждого элемента массива LDAPFilter.

Аналогичный процесс выполняется и для категорий второго типа за тем лишь исключением, что в ходе опроса категорий данного набора выполняется поиск членов групп. Во фрагменте D создается уже знакомая комбинация из массивов категорий и запросов, и элементы этих массивов обрабатывают циклом, аналогичным приведенным во фрагменте C. Однако для обработки каждой группы и занесения членов групп и категорий, к которым они относятся, в базу данных, процесс вызывает специальную подпрограмму.

Убедитесь, что значения массива DistinguishedName Query Array (DNQA) в точности совпадают с уникальными именами объектов. Некоторые группы могут быть перемещены в другие подразделения (OU) вами или другим администратором домена. К примеру, существует достаточно распространенная практика перемещать группы Domain Admins, Enterprise Admins и Schema Admins из контейнера Users в контейнер Builtin. Сценарий AccountTracker.vbs, не обнаружив группу в указанном контейнере, выведет на экран 15-секундное всплывающее окно с сообщением о том, что элементы массива DNQA не были найдены. Если вам необходимо изменить отличительное имя, отредактируйте выражение, заключенное в двойные кавычки. Например, если группа Domain Admins была перемещена в контейнер Builtin из контейнера Users, измените строку

DNQA (3) = "CN=Domain Admins,
   CN=Users," _
   & DNC

на

DNQA (3) = _
   "CN=Domain Admins, CN=Builtin," _
   & DNC

Выражение DNC должно остаться неизменным. Это контекст именования доменов, используемый «по умолчанию», — строка, которую необходимо добавить к части отличительного имени, заключенному в двойные кавычки.

Для данного типа категорий вызывается подпрограмма GetGroupMembers, приведенная в листинге 2. Код фрагмента A первым делом получает значение основного атрибута GroupToken для целевой группы и использует запрос LDAP для поиска учетных записей с соответствующим значением основного атрибута GrupID. Этот шаг необязателен для построения списка членов группы, однако он исключает возможность «потери» членов нестандартных основных групп, что особенно характерно для групп Domain Admin.

Как вы видите во фрагменте B, перед тем как занести каждый обнаруженный объект в базу данных, сценарий просматривает значение атрибута sAMAccountName и проверяет, присутствует ли это значение в словаре. Если в словаре данное значение не найдено, то объект заносится в базу, а значение атрибута добавляется в словарь. Обратите внимание, что процесс записи объекта в базу полностью повторяет алгоритм, использованный при работе с первым типом категорий. Элемент массива категорий — в данном случае MemberCats (j) — содержит имя обрабатываемой группы.

После проверки основной группы сценарий начинает работать с множеством членов группы (фрагмент С). Сначала проверяется наличие имени члена или группы в словаре. Если в словаре имя присутствует, то процесс переходит к обработке следующего члена группы. Если имя в словаре не обнаружено, оно заносится туда.

Далее проверяется, является ли член группы объектом-контейнером. Если это так, то сам объект заносится в базу данных, а для получения списка его вложенных групп осуществляется рекурсивный вызов подпрограммы GetGroupMembers. Проверяя наличие в словаре имен групп и их членов, мы исключаем возможность появления бесконечных циклов, возникающих в случаях, когда вложенные группы ссылаются друг на друга. Если объект не является группой, то сценарий просто прописывает данные о нем в базу данных. Этот процесс повторяется для каждого элемента в массиве DNQA.

После того как сценарий обработает все категории и запишет результаты в базу данных, нам останется только сравнить новые данные с полученными ранее. Для сравнения используется тот же алгоритм, что и при работе с массивом Categories.

Анализ результатов

Иногда для получения отчета в формате Excel об изменениях за месяц я прибегаю к небольшой хитрости. Сначала я перемещаю базы данных NewestAcctTracker.xml и PreviousAcctTracker.xml в папку SafeKeep. После этого копирую файл ArcAcctTrackerDateTime.xml, выбранный в соответствии с нужным мне сроком давности, переименовываю его в NewestAcctTracker.xml и запускаю сценарий AccountTracker.vbs.

После завершения работы сценария я сохраняю полученную электронную таблицу — например, под именем Account changes for August.xls — и перемещаю оригинальные копии файлов NewestAcctTracker.xml и PreviousAcctTracker.xml из папки SafeKeep на прежнее место, перезаписывая при этом временные файлы.

Давайте просмотрим несколько простейших таблиц результатов. Допустим, сначала в группе Administrators, включающей группы Domain Admins и Enterprise Admins, были созданы следующие объекты:

  • в группе Domain Admins — учетные записи Administrator и Planning;
  • в группе Enterprise Admins Properties — учетная запись Administrator;
  • в группе Admins Properties — учетная запись Administrator.
  • Предположим, я запустил сценарий и добавил в эти группы новых членов:
  • в группу Domain Admins Properties добавил учетную запись David Wall;
  • в группу Enterprise Admins Properties добавил учетную запись Elizabeth Borg;
  • в группу Schema Admins Properties добавил учетную запись Shannon Green.

Таблица результатов работы сценария для приведенного выше варианта показана на экране 1.

Экран 1. Пример сводной таблицы результатов запуска сценария

Теперь допустим, что кто-то удалил группу Domain Admins из группы Administrators group. При повторном запуске сценария в таблице результатов мы увидим, что группа Domain Admins не была найдена (экран 2). Кроме того, будет показано, что члены этой группы больше не являются членами группы Administrators, — их статус Not Found. Однако эти пользователи все еще остаются членами группы Domain Admins.

Экран 2. Пример сводной таблицы результатов после удаления группы Domain Admins

Экран 3. Таблица результатов проведения нескольких изменений

И наконец, на экране 3 изображена таблица результатов для следующих изменений:

  • в группу Domain Admins добавлены группы Account Operator и Backup Operator, а также новая учетная запись;
  • в группу Server Operators добавлена группа с названием NewGroup, а также новая учетная запись;
  • отключена и удалена учетная запись.

В секции New, показанной на экране 4, отображены элементы, добавленные в AD, однако для получения полной картины изменений вам, скорее всего, придется обратиться к секции Not Found.

Экран 4. Сводная таблица результатов с экрана 3

Тестированиеи использование сценария

На сайте TechNet Virtual Lab в разделе «Microsoft Office PerformancePoint Server 2007 — Excel Dashboards» (go.microsoft.com/?linkid=8205426) вы можете скопировать код сценария в «песочницу» виртуального сервера, тогда вам не придется вносить изменения в реальную структуру AD. Введите код сценария в систему, щелкнув мышью на кнопке Action. Проверьте правильность написания кода — в процессе загрузки он будет фрагментирован. Я закомментировал выражение On error resume next и запускал сценарий до тех пор, пока не избавился от ошибок.

Для хранения историй изменений можно создать простые, удобные файлы баз данных .xml. Я использовал эти файлы для мониторинга принтеров и отслеживания идентификаторов безопасности (SID) всех учетных записей домена. Кроме того, я обращался к ним при проверке корзин Recycle Bin серверов.

Джим Тернер (jturnervbs@gmail.com) — системный администратор и разработчик приложений в компании Computer Sciences Corporation


Листинг 1. Код для создания массивов, применяемых при опросе двух наборов категорий AD

Листинг 2. Процедура GetGroupMembers