Perl для системного администрирования

ADSI (Интерфейсы служб активных каталогов)


В последнем разделе этой главы мы обсудим платформо-зависимый интерфейс службы каталогов, разработанный с учетом только что рассмотренного материала.

В Microsoft создали сложную службу каталогов, основанную на LDAP, под названием Active Directory для использования ее в сердцевине интерфейса администрирования Windows 2000. Служба Active Directory является репозиторием для всей важной конфигурационной информации (пользователи, группы, системные политики, поддержка установки программного обеспечения и т.д.), применяемой в сети манит с Windows 2000.

При разработке активных каталогов в Microsoft осознали, что для этой службы необходимо создать программный интерфейс более высокого уровня. Для этого был разработан интерфейс ADSI (Active Directory Service Interfaces, интерфейсы служб активных каталогов). Надо отдать должное разработчикам Microsoft, им удалось понять, что новый интерфейс нужно будет расширить и охватить такие области системного администрирования, как принтеры и службы NT. Подобный размах делает ADSI чрезвычайно полезным для тех, кто пишет сценарии, автоматизирующие выполнение задач системного администрирования. Перед тем как ознакомиться с его работой, приведем несколько основных концепций и терминов, которые нам понадобятся.

Основы ADSI

ADSI можно считать оболочкой вокруг произвольной службы каталогов, действующей в рамках ADSL В среде интерфейса есть провайдеры (providers),

которые представляют собой реализации ADSI для LDAP, WinNT4.0 и Novell Directory Service. Говоря на «языке» ADSI, каждая из этих служб каталогов (WinNT не является службой каталогов) и домены данных (data domains) называются

пространством имен (namespaces). ADSI предоставляет единый способ запроса и изменения данных, найденных в этих пространствах имен.

Чтобы понять ADSI, необходимо иметь представление об объектной модели компонентов COM (Component Object Model), на которой построен интерфейс ADSL О модели СОМ существует много книг, но следует остановится на таких ключевых понятиях:




  • Все, с чем работают в СОМ, - это объекты (objects).




  • Объекты имеют интерфейсы (interfaces), обеспечивающие набор

    методов (methods), применяемых для взаимодействия с этими объектами. Из Perl можно использовать методы, предоставляемые или наследуемые от интерфейса под названием IDispatch. К счастью, большинство методов ADSI, предоставляемых интерфейсами ADSI и их производными (например, lADsUser, lADsComputer, lADsPrint-Queue), унаследованы от IDispatch.


  • Значения, инкапсулируемые объектом, запрашиваемые и изменяемые посредством этих методов, называются свойствами (properties). В этой главе будут рассматриваться два типа значений: свойства, определяемые интерфейсом (interface-definedproperties), и свойства, определяемые схемой (schema-defined properties). Иными словами, первые будут определяться как часть интерфейса, а вторые - в объекте схемы. Подробнее об этом говорится чуть ниже. Если в данной главе не будут явно упоминаться «свойства схемы», значит, подразумевать следует свойства интерфейса.


  • Все это относится к стандартным понятиям объектно-ориентированного программирования. Сложности начинаются, когда сталкиваются терминологии ADSI/COM и других объектно-ориентированных миров, подобных LDAP.

    Например, в ADSI рассматривается два различных типа объектов: лист (leaf) и контейнер (container). Объект-лист содержит данные; объект-контейнер содержит другие объекты, т. е. является для них родительским (parent). В LDAP самыми точными определениями для этих терминов были бы «элемент» и «точка ветвления». С одной стороны, мы говорим об объектах со свойствами, а с другой - об элементах с атрибутами. Как разобраться с подобными разногласиями, если учесть, что оба названия определяют одни и те же данные?

    Вот как можно это понимать: в действительности, сервер LDAP обеспечивает доступ к дереву элементов и связанных с ними атрибутов. Когда интерфейс ADSI используется вместо LDAP для получения элемента дерева, ADSI вытягивает элемент из сервера LDAP, заворачивает его в несколько слоев блестящей оберточной бумаги и передает вам в качестве СОМ-объекта. Для получения содержимого этой посылки следует применять нужные методы, которые теперь называются «свойствами». Если внести изменения в свойства данного объекта, можно вернуть объект ADSI, чтобы последний распаковал информацию и передал ее обратно в дерево LDAP.



    Вполне разумным выглядит вопрос: « А почему бы не обратиться напрямую к серверу LDAP?» На этот вопрос есть два хороших ответа: если мы знаем, как использовать ADSI для работы с одним типом служб каталогов, то мы знаем, как работать со всеми ними (или, по крайней мере, с теми, которые имеют ADSI-провайдеры). Второй ответ будет дан чуть позже, когда станет ясно, как можно упростить программирование служб каталогов при помощи инкапсуляции ADSL

    Необходимо ввести понятие AdsPaths, чтобы перейти к ADSI-програм-мированию в Perl. ADsPaths предоставляет нам уникальный способ ссылаться на объекты из любого пространства имен.

    Вот как выглядят примеры ADsPath из документации ADSI SDK:

    WinNT://MyDornain/MyServer/User

    WinNT://MyDomain/JohnSmith,user

    LDAP://ldapsvr/CN=TopHat,DC=DEV,DC=MSFT,DC=COM,[^Internet

    LDAP://MyDomain.microsoft.com/CN=TopH,DC=DEV,DC=MSF[,DC=COM.0=Internet

    Это не совпадение, что они похожи на URL, т. к. и URL и ADsPath служат одним и тем же целям. Они оба пытаются обеспечить недвусмысленный способ сослаться на данные, предоставляемые различными службами данных. В случае с AdsPath из LDAP используется синтаксис LDAP URL из RFC, упомянутых в приложении В (RFC2255).

    Более подробно AdsPath будет рассматриваться при обсуждении двух уже упомянутых пространств имен - WinNT и LDAP. Но сначала разберемся, как, в общих чертах, ADSI используется из Perl.



    Использование ADSI из Perl



    Семейство модулей Win32::OLE, поддерживаемое Жаном Дюбуа (Jan Dubois) и Гурусами Сарати (Gurusamy Sarathy), предоставляет мост от Perl к ADSI (который построен на СОМ как часть OLE). После загрузки основного модуля он используется для запроса ADSI-объектов:

    use Win32::OLE;

    Sadsobj = Win32::OLE->GetObject($ADsPath) or

    die "Невозможно получить объект для $ADsPath\n";

    Win32: :OI_E->GetObject() принимает моникер (moniker) OLE (уникальный идентификатор объекта, в данном случае это ADsPath) и возвращает объект ADSL Этот вызов также обрабатывает процесс связывания (binding) с объектом, уже рассмотренный при обсуждении LDAP. По умолчанию связывание с объектом производится от имени пользователя, запускающего сценарий.



    Этот совет может уберечь вас от испуга. Если выполнить эти две строчки кода в отладчике и изучить содержимое возвращаемой ссылки на объект, то можно увидеть нечто подобное:

    DB<3> x Sadsobj

    О Win32::OLE=HASH(Ox10feOd4)

    empty hash

    He волнуйтесь. Win32: :OLE использует все могущество связанных переменных (tied variables). Кажущаяся пустой структура данных, как по волшебству, передаст информацию из объекта, если верно к ней обратиться.

    Для доступа к значениям свойств интерфейса объекта ADSI используется ссылка на хэш:

     Инструменты ADSI

    Для использования материала из этой главы необходимо установить ADSI хотя бы на одной машине в сети. Эта машина может служить (через DCOM) ADSI шлюзом для остальных машин. Посетите сайт Тоби Эверета (Toby Everett), ссылка на который приведена ниже, чтобы узнать подробнее, как настроить ADSI для работы с DCOM.

    Любая машина с Windows 2000 имеет встроенный в операционную систему интерфейс ADSL Для всех остальных Win32-Ma-шин придется загрузить и установить бесплатный дистрибутив ADSI 2.5, находящийся в http://www.microsoft.com/adsi. По этой же ссылке вы найдете документацию по ADSI, включая ad-si25.chm,

    сжатую помощь в формате HTML, содержащую лучшую доступную документацию по ADSL

    Даже если вы работаете с Windows 2000, я советую загрузить ADSI SDK с сайта Microsoft по указанной ссылке, поскольку в него входит эта документация и удобный броузер объектов ADSI под названием AdsVW. SDK поставляется с примерами программирования ADSI на нескольких языках, включая Perl. К сожалению, примеры из текущего дистрибутива ADSI полагаются на устаревший модуль OLE.pm, так что, в лучшем случае, вы сможете получить несколько советов, но не надо использовать эти примеры в качестве стартовой точки.

    Перед тем как начать писать программы, стоит загрузить броузер объектов ADSI Тоби Эверета (написанный на Perl) с http:// opensource.activestate.com/authors/tobyeverett. Он научит вас перемещаться по пространствам имен ADSL Обязательно посетите этот сайт, начиная карьеру программиста ADSI, поскольку он является одним из лучших доступных сайтов по применению ADSI из Perl.



    $value = $adsobj->{key}

    Например, если этот объект имеет свойство Name, определенное как часть его интерфейса (а так и есть), вы можете применить:

    print $adsobj->{Name}."\п";

    При помощи такой же записи можно присваивать значения свойствам интерфейсов:

    $adsob]->{FullNanie}= "Oog":

    Свойства объекта ADSI хранятся в кэше (называемом кэшем свойств (property cache)). Первый запрос к свойствам объекта заполняет дан-

    ный кэш. Последующие запросы к тем же свойствам позволяют получить информацию из этого кэша, а не из службы каталогов. Если вы хотите вручную заполнить кэш, можно вызвать методы Getlnf"() или GetInfoEx() (расширенная версия Getlr.foо) для данного экземпляра объекта, применяя синтаксис, который скоро будет рассмотрен.

    Из-за того что первое считывание информации происходит автоматически, методы GetlnfoO и GetInfoEx() часто остаются незамеченными. Существуют ситуации, когда эти методы следует употреблять, хотя в книге такие случаи рассматриваться не будут. Вот две подобные ситуации:

  • Некоторые свойства объектов можно получить, только явно вызвав GetInfoEx(). LDAP-провайдер Microsoft Exchange 5.5 представляет собой самый характерный пример, поскольку многие из его свойств не доступны, если не вызвать сначала GetInfoEx(). Детальную информацию об этой несовместимости можно найти на http://ope.nso-urce.activestate.com/authors/tobyeverett.


  • Если несколько человек имеют право изменять в каталоге данные, то вызванный вами объект может быть кем-то преобразован, пока вы с ним работаете. Если это произойдет, данные в кэше свойств этого объекта устареют. Getlnfо() и GetInfoEx() обновят этот кэш.


  • Для обновления службы каталогов и источников данных, предоставляемых через ADSI, после изменения объекта нужно вызвать специальный метод Setlnfo(). SetlnfoO сбрасывает изменения из кэша свойств в службу каталогов и источники данных. (Это должно напомнить вам о необходимости вызывать метод upoate() в Mozilla: :LDAP. В данном случае идея та же.)



    Вызывать методы экземпляра объекта ADSI не сложно:

    $adsobj->Method($argijments. . .)

    Поэтому, если бы мы изменили свойства объекта, как это предлагалось сделать в предыдущем предупреждении, то могли бы использовать такую строку сразу же после кода, вносящего изменения:

    $adsob]->Set!nfo():

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

    Он возвращает ошибку, полученную в результате последней операции OLE. Применение ключа -и> с Perl (т. е. peri ~w script) также

    приводит к подробным сообщениям о неудачных попытках OLE-операций. Зачастую эти сообщения об ошибках - единственная помощь, которая вам доступна, так что попытайтесь с толком ее использовать.

    ADSI-код, который до сих пор рассматривался, выглядел как обычный код на Perl, поскольку внешне они похожи. Теперь перейдем к бо лее сложным вопросам.



    Работа с объектами контейнер/коллекция



    Ранее в этом разделе уже упоминались два типа объектов ADSI: лист и контейнер. Объект-лист представляет собой только данные, тогда как контейнер (известный еще как коллекция - в терминах OLE/COM) содержит другие объекты. Еще одно отличие двух типов объектов в контексте ADSI состоит в том, что объект-лист не имеет дочерних объектов в иерархии, а у контейнеров такие объекты есть.

    Объекты-контейнеры требуют специальной обработки, т. к. в большинстве случаев нас интересуют данные, инкапсулированные их дочерними объектами. Существует два способа обратиться к таким объектам из Perl. Win32: :OLE имеет специальную функцию под названием in(), которая недоступна по умолчанию, если модуль загружается стандартным способом. Если необходимо получить к ней доступ, надо в начале программы использовать следующее:

    use Win32::OLE 'in';

    in() возвращает список ссылок на дочерние объекты, хранящиеся в этом контейнере. Это позволяет писать легко читаемые программы на Perl:

    foreacn Schiid (in $adsobj){

    print $child->!Name}

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



    Win32: :OLF под названием Win32: :OLE: :Enum. Win32: :0! F.' : f.-",:'1 ->new()

    создает объект-перечислитель из какого-либо объекта-контейнера:

    use Win32 : : OLE: : Enuti:

    $onobj = Win32: :OLE: : Enurr->new($adsobj);

    Для этого объекта можно вызвать несколько методов и получить дочерние объекты $adscoj. Подобный подход должен напомнить вам способ, применяемый в операциях поиска с Moziila: : LOAP; процесс тот же самый.

    Идентификация объекта-контейнера

    Заранее нельзя узнать, является ли объект контейнером. Не существует способа из Perl «спросить» объект, не контейнер ли он. Максимум, что можно сделать, - попытаться создать объект-перечислитель и, если эта попытка не удастся, фиксировать данный результат. Вот короткий пример, который делает именно это:

    use Win32::OLE;

    use Win32::OLE::Enum:

    eval {$enobj = Win32::OLE::Enum->new($adsobj)};

    print "Объект " . ($@ ? "не " : "") . "является контейнером \п";

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



    Как же узнать что-нибудь об объекте?



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

    Нет единого места, в котором можно найти ответы на эти вопросы, но существует несколько источников, из которых можно почерпнуть нужную информацию для формирования практически всей картины. Первое место - это документация по ADSI, особенно та помощь, о которой говорилось во врезке «Инструменты для ADSI». В этом файле содержится огромное количество материала. Для ответа на наш вопрос о названиях свойств и методов нужно начать с Active Directory Service Interfaces 2.5—>ADSI Reference—>ADSI System Providers.



    Иногда имена методов можно найти только в документации, но существует другой, более интересный подход для поиска названий свойств. Можно использовать метаданные, предоставляемые самим ADSI. Именно здесь на сцену выходят свойства схемы, о которых говорилось раньше.

    Каждый объект ADSI имеет свойство под названием Schema, которое связывает ADsPath с его объектом схемы. В частности, следующий пример:

    use Win32::OLE:

    SADsPath = "WinNT://BEESKNEES. computer": Sadsob] = Win32::OLE->GetObject($ADsPath) or

    die "Невозможно получить объект для $ADsPatn\n";

    print "Это обьекг ". $adsob]->{Class}. . схема находится r.vi".

    $adsobj->{Schema}."\n"; выведет:

    Это объект Computer, схема находится в:

     WinNT://Do[r.ainName/Schorra/Co""p .

    Значение $adsobj->{Schema} - это путь ADsPath к объекту, описывающему схему для объектов класса Computer в этом домене. Здесь мы используем термин «схема» в том же смысле, что и в разговоре про схемы LDAP. В LDAP схемы определяют, какие атрибуты могут и должны присутствовать в элементах определенных классов объектов. В ADSI схема содержит ту же информацию об объектах определенного класса и их свойства схемы.

    При желании посмотреть на возможные имена атрибутов объекта следовало бы взглянуть на значения двух свойств объекта схемы: MauJatoryProperties и OptionalProperties. Изменим предыдущий оператор

    print:

    $schmobj = Win32::OLE->GetObject($adsobj->{Schema}) or

    die "Невозможно получить объект для $ADsPath\n";

    print join("\n",@{$schmob]->{MandatoryProperties}},

    @{$schmob]->{OptionalProperties}}),"\n";

    Тогда получится:

    Owner

    Division

    OperatingSystem

    OperatingSystemVersion

    Processor

    ProcessorCount

    Теперь известны возможные имена свойств схемы в пространстве имен WinNT для объектов Computer. Отлично.

    Свойства схемы получаются и устанавливаются несколько иначе, чем свойства интерфейсов. Свойства интерфейсов обрабатываются примерно так:



    получение и установка свойств ИНТЕРФЕЙСОВ

    lvalue = $obj->{property); $ob]->{property} = SvaTje;

    Свойства схемы получаются и устанавливаются при помощи специальных методов:

    # получение и установка свойств СХЕМЫ Svalue = $obj->Get("property"); $obj->Put("property" "value"):

    Все, что касается свойств интерфейсов, о чем говорилось до сих пор, остается справедливым и для свойств схемы (т. е. кэш свойств, и т. д.). Помимо необходимости применения специальных методов для получения и установки значений, единственное, что отличает данные свойства, - это их имена. Иногда один и тот же объект может иметь два различных имени для одного и того же свойства, одно для свойств интерфейса, другое для свойств схемы. Например, два этих свойства получают основные настройки для пользователя:

    $1еп = $userot)j-><PasswordMininoniLeiigir}: я свойство :ih :ерое/са

    $len = $userobj->Get("MinPasswordLength"); и то же самое свойство с>,о:-ь

    Наличие двух типов свойств обусловлено тем, что свойства интерфейса существуют в виде части модели СОМ. Разработчики, определяя интерфейс при создании программы, также определяют свойства интерфейса. Позже, если они хотят расширить набор свойств, им приходится изменять и СОМ-интерфейс, и любой код, использующий этот интерфейс. В ADSI разработчики могут изменить свойства схемы в провайдере без необходимости изменять лежащий в основе СОМ интерфейс этого провайдера. Очень важно разобраться с обоими типами свойств, т. к. иногда некоторые данные объекта доступны через свойства только одного типа.

    На практике, если вы ищете только названия свойств интерфейса или схемы и не собираетесь писать программы для их поиска, я рекомендую использовать ADSI-броузер Тоби Эверета, о котором я упоминал ранее. Вот пример этого броузера в действии.

    Как альтернативный вариант упомянем программу ADSIDump из каталога General примеров SDK, которая может вывести содержимое всего дерева ADSL



    Поиск



    Эта последняя сложность, которую следует обсудить, перед тем как двигаться дальше. В разделе «LDAP: сложная служба каталогов» мы провели достаточно времени в разговорах о поиске в LDAP. Но в мире ADSI мы вряд ли услышим хоть слово по этому поводу. Все из-за того, что в Perl (и любом другом языке, в котором используется тот же OLE-интерфейс автоматизации) поиск с ADSI очень сложен; более того, поиск поддеревьев и поиск, в котором используются не самые простые фильтры, мучительно сложен. (Все остальное не так плохо.) Сложный поиск проблематичен, т. к. для его выполнения необходимо выйти за пределы ADSI и использовать совершенно иную методологию для получения данных (не говоря уже о том, что придется выучить новые акроним от Microsoft).



    Но тот, кто занимается системным администрированием, привык смеяться над сложностями, так что начнем с простого поиска, а потом пе рейдем к более сложным вопросам. Простой поиск, затрагивающий один объект (пространство base) или его непосредственных потомков

    (пространство one), можно выполнить вручную при помощи Perl. Сделать это можно так:

  • Для одного объекта получите нужные свойства и используйте обычные операторы сравнения для определения соответствия:


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


  • Для того чтобы выполнить более сложный поиск, затрагивающий, скажем, все дерево каталогов или поддерево, вам придется переключиться на использование другой технологии «промежуточного уровня» под названием ADO (ActiveX Data Objects, объекты данных ActiveX). ADO предоставляет языкам сценариев интерфейс к базам уровня Microsoft OLE DB. OLE DB обеспечивает общий интерфейс, ориентированный на базы данных, к источникам данных, подобным реляционным базам данных и службам каталогов. В нашем случае ADO будет применяться для «разговора» с ADSI (который, в свою очередь, общается с самой службой каталогов). Поскольку ADO - это методология, ориентированная на базы данных, рассматриваемая программа предваряет материал об ODBC, о котором речь пойдет в главе 7.

    ADO работает только с провайдером LDAP ADSI. В пространстве имен WinNT она работать не будет.

    ADO - это отдельная тема, которая лишь затрагивает службы каталогов, поэтому будет рассмотрен только один пример с короткими пояснениями, остальные примеры работают с ADSL Дополнительную информацию об ADO можно найти на http://www.microsoft.com/ado.

    Вот пример программы, выводящей имена всех групп, найденных в данном домене. Детальное обсуждение программы приведено ниже.

    use Win32::OLE 'in';

    # получаем объект ADO, устанавливаем провайдер, открываем соединение

    $с = Win32::OLE->new("ADODB.Connection");



    $c->{Provider} = "ADsDSOObject";

    $c->OpenC'ADSI Provider");

    die Win32::OLE->LastError() if Win32::OLE->LastError();



    подготавливаем и выполняем запрос

    $ADsPath = "LDAP://ldapserver/dc=example,dc=com";

    $rs = $c->Execute("<$ADsPath>;(objectClass=Group);Name;SubTree");

    die Win32::OLE->LastError() if Win32::OLE->LastError();

    until ($rs->EOF){

    print $rs->Fields(0)->{Value},"\n"; $rs->MoveNext;

    $rs->Close; $c->Close;

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

    Затем выполняется собственно поиск при помощи Execute(). Поиск можно осуществлять средствами одного из двух «диалектов»: SQL или ADSL Диалект ADSI, как видно из программы, использует командную строку, состоящую из четырех аргументов, каждый из которых разделен точкой с запятой.2 Вот эти аргументы:

  • ADsPath (в угловых скобках), определяющий сервер и базовое DN-имя для поиска.


  • Фильтр поиска (применяется тот же синтаксис LDAP-фильтров, что упоминался раньше).


  • Имя или имена (разделенные запятыми) возвращаемых свойств.


  • Пространство поиска: либо Base, либо OneLevel, либо SubTree (в соответствии со стандартом LDAP).


  • Execute() возвращает ссылку на первый из объектов ADO RecordSe, получаемых в результате запроса. По очереди запрашивается каждый из объектов RecordSet, распаковываются объекты, которые в нем содержатся, и выводится свойство Value, возвращаемое методом Fields() для каждого из этих объектов. Свойство Value содержит значение, которое запрашивалось в командной строке (имя объекта Group). Вот как выглядит отрывок получаемых данных на машине с Windows 2000:

    Administrators

    Users

    Guests

    Backup Operators

    Replicator

    Server Operators



    Account Operators

    Print Operators

    DHCP Users

    DHCP Administrators

    Domain Computers

    Domain Controllers

    Schema Admirs

    Enterprise Admins

    Cert Publishers

    Domain Admins

    Domain Users

    Domain Guests Group Policy Admins RAS and IAS Serveis DnsAdmins DnsUpdateProxy



    Выполнение распространенных задач при помощи пространства имен WinNT и LDAP



    Теперь, когда мы разобрались со «списком» сложностей, можно перейти к выполнению некоторых распространенных задач системного администрирования, используя ADSI из Perl. Цель - дать понять, ка кие задачи можно решать при помощи представленной информации об ADSL Затем рассмотреть и использовать код, который пригоден для написания собственных программ.

    Для этих целей будет использоваться одно из двух пространств имен. Первое пространство - WinNT, которое предоставляет доступ к объектам Windows NT 4.0, таким как пользователи, группы, принтеры, службы и т. д.

    Второе - это наш старый знакомый - LDAP. LDAP мы выбираем про вайдером при переходе к Windows 2000 и ее службе Active Directory, основанной на LDAP. Большинство объектов WinNT также доступны через LDAP. Ведь даже в Windows 2000 существуют задачи, которые можно выполнить, только используя пространство имен WinNT (например, создание учетных записей на локальной машине).

    Программы, работающие с этими различными пространствами имен, похожи друг на друга (в конце концов, частично в этом и заключается смысл применения ADSI), но необходимо обратить внимание на два важных различия. Во-первых, формат ADsPath немного отличается. В соответствии с ADSI SDK, ADsPath в WinNT может иметь следующий вид:

    WinNT: [//DomainNarre[/Cor;puterName[/'Ou;iectNa!T!e[. classNarne] ]]]

    WinNT: [//DomainName[/ObjectNa.ne[, CjassNane]]]

    WinNT:[//ComputerName,computer] WinNT:

    ADsPath в LDAP выглядит так:

    LDAP://HostName[: PortNumberj[/DisTinguisneaNar;e]

    Обратите внимание, что при работе с NT 4 ADsPath в LDAP требует указывать имя сервера (в Windows 2000 это изменилось). Это означает, что пространство имен LDAP нельзя просмотреть с верхнего уровня, как пространство WinNT, т. к. необходимо указать начальный сервер. В пространстве имен WinNT любой может применить ADsPath или просто Wr'NT. для начала поиска в иерархии доменов.



    Также обратите внимание, что свойства объектов в двух пространствах имен похожи, но не идентичны. Например, можно обратиться к одним и тем же объектам из обоих пространств имен WinNT и LDAP, но обратиться к некоторым свойствам Active Directory конкретного объекта пользователя можно только через пространство имен LDAP.

    Особенно важно заметить различия между схемами в этих двух пространствах имен. Например, класс Use для WinNT не имеет обязательных свойств, тогда как класс User в LDAP требует наличия свойств  samAccountNarne в каждом объекте пользователя.

    Не забывая об этих различиях, посмотрим на сам код. В целях экономии места пропустим большую часть проверок ошибок, но рекомендуется запустить сценарий с ключом -w и добавить в текст программы примерно такие строки:

    die "Ошибка OLE: ".Win32: :OLE->LastError()

    if Win32: :OLE->Lar,tError():



    Работа с пользователями через ADSI



    Для получения списка пользователей домена применяется следующее:

    use Win32::OLE 'in':

    SADsPath = "WinNT://DomainName/PDCName,computer":

    $c = Win32::OLE->GetObject($ADsPath) or

    die "Невозможно получить $ADsPath\n":

    foreach $adsobj (in $c){

    print $adsobj->{Nane}."\n" if ($adsobj->{Class} eq "User"): }

    Для создания нового пользователя и установки его полного имени (свойство Full Name):

    use Win32::OLE;

    $ADsPatn="WinNT://Domain Name/ComputerName,computer":

    $c = Win32::OLE->GetOaject($ADsPatn) or

    die "Невозможно получить SADsPath г":

    tt создаем и возвращаем объект User $u = $c->Create("jser".$usernaTie);

    Su->SetInfc(): tt н,жно создать лолазона^еля. чеойд тем как меня~^ з^а-е-/-

    Name тедогус^.'м $u->{Fjll'Janei = З'Л'.г.;: ,;.-. $bj->3eil';fo()

    Если Cciroutf rfJri! r - это первичный контроллер домена (Primary Domain Controller), то мы создали пользователя домена. Если нет, этот пользователь будет локальным для данной машины.

    Эквивалентная программа создания глобального пользователя (при помощи LDAP нельзя создавать локальных пользователей) в Active Directory выглядит так:



    use Win32::OLE;

    SADsPath = "LDAP://ldapserver,CN=Users,dc=example.dc=coro";

    $c = Win32: :OLE->GetObject($ADsPath) or die "Невозможно получить SADsPafAr"

    # создаем и возвращаем объект User $u=$c->Create("user","cn=".Scommonname);

    Su->{samAccountName} = Susername;

    # нужно сначала создать пользователя в каталоге, а потом менять значения $u->Set!nfo();

    # пробел между "Full" и "Name" требуется при работе с пространством имен LDAP:

    $u->{'Full Name'} = $fullname; $u->Set!nfo();

    Для удаления пользователя нужно внести лишь небольшие изменения:

    use Win32::OLE;

    SADsPath = "WinNT://DomainName/ComputerName,computer";

    $c = Win32::OLE->GetObject($ADsPath) or die "Невозможно получить $ADsPath\n";

    # удаляем обьект User, заметьте, мы в границах контейнера

    $c->Delete("user",Susername);

    $u->Set!nfo();

    Изменить пароль пользователя можно при помощи единственного метода:

    use Win32::OLE;

    SADsPath = "WinNT://DomainName/ComputerName/".Susername;

    $u = Win32::OLE->GetObject($ADsPath) or

    die "Невозможно получить $ADsPath\n":

    $u->ChangePasssword($oldpassword,Snewpassword): $u->Set!nfo();



    Работа с группами через ADSI



    Для перечисления доступных групп достаточно лишь немного подправить программу, выводящую список пользователей. Меняется только такая строка:

    print $adsobj->{Name},"\n"

    if ($adsobj->{Class) eq "Group"):

    Создание и удаление групп выполняется при помощи тех же методов CreateO и Oelete(), которые применялись для создания и удаления учетных записей. Единственное различие - первый аргумент нужно изменить на «group». Вот так:

    $д = $с->Сгеа Sgro.ip- ary):

    Для добавления пользователя в группу (определяемую при помощи GroupName) после ее создания используется следующее:

    use Win32::OLE:

    SADsPath = "WinNT://DomainNane ''GroupNcine gro,,c"

    $g = Win32: :OLE->GetOuject($ADsPatii) or



    Здесь действуют те же правила относительно локальных пользователей и пользователей домена (глобальных), которые мы рассмотрели выше. Для того чтобы добавить пользователя домена в группу, $ийо<"-ADsPath должна указывать на пользователя на PDC для этого домена.

    Для удаления пользователя из группы применяйте:

    $c->Reniove($userADsPath);



    Работа с разделяемыми ресурсами через ADSI



    Теперь займемся более интересными задачами ADSI, адресованными посвященным. Можно применять ADSI, чтобы предоставить к совместное пользование часть локального дискового пространства на машине:

    use Win32::OLE;

    SADsPath = "WinNT://ComputerName/1 anira'iserver":

    $c = Win32: :ClEI->GotObjecT

    $s = $c->Create("filesnare".Sshareramo): $s->{path} = 'C:\directory1:

    Перед тем как перейти к другим задачам, хочу воспользоваться случаем и напомнить вам о необходимости обратиться к документации SDK перед работой с каким-либо из этих ADSI-объектов. Кое-какие не<>;ки-данности могут оказаться полезными. Если вы заглянете и раздел Acti

    ve Directory Service Interfaces 2.5—>ADSI Reference—> ADSI Interfaces—> Persistent Object Interfaces—> lADsFileShare

    файла помощи ADSI 2.5, то увидите, что объект f liesnare имеет свойство CurrentUser-Count, которое соответствует количеству пользователей, подсоединенных в настоящее время к разделяемому ресурсу. Этот нюанс может очень сильно пригодиться.



    Работа с очередями и заданиями печати через ADSI



    Вот как можно определить названия очередей на определенном сервере и модели принтеров, используемых для обслуживания этих очередей:

    use Win32::OLE 'in';

    $ADsPatn="WinNT://DomainName/PrintServerName, computer";

    $c = Win32::OLE->GetObject($ADsPath) or die "Невозможно получить $ADsPath\n";

    foreach Sadsobj (in $c){

    print $adsobj->{Name}.":".Sadsobj->{Model}."\n"

    if ($adsobj->{Class} eq "PrintQueue"); }

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



    use Win32::OLE 'in';

    # таблица получена из раздела

    # 'Active Directory Service Interfaces 2.5->ADSI Referen.ce->

    # ADSI Interfaces->Dynamic Object Interfaces->IADsPrintQueueOperations->



     lADsPrintOueueOperations Property Methods' (уф!) из ADSI 2.5 SDK

    %status =

    (0x00000001 => 'PAUSED', 0x00000002 => 'PENDING_DELETION',

    0x00000003 => 'ERROR' , 0x00000004 => 'PAPER_JAM',

    0x00000005 => 'PAPER_OUT', 0x00000006 => 'MANUAL_FEED',

    0x00000007 => 'PAPER_PROBLEM', 0x00000008 => 'OFFLINE',

    0x00000100 => 'IO_ACTIVE', 0x00000200 => 'BUSY',

    0x00000400 => 'PRINTING', 0x00000800 => 'OUTPUT_BIN_FULL',

    0x00001000 => 'NOTJWAILABLE'. 0x00002000 => 'WAITING',

    0x00004000 => 'PROCESSING', 0x00008000 => 'INITIALIZING'.

    0x00010000 => 'WARMING_UP'. 0x00020000 => 'TONER_LOW.

    0x00040000 => TJO_TONER'. 0x00030000 => 'PAGE_PUNT'.

    0x00100000 => 'USER_INTERVENTION', 0x00200000 => 'OUT_OF_MEMORY'.

    0x00400000 => 'DOOR_OPEFJ', 0x00800000 => 'SEflVERJNKNOWN'.

    0x01000000 => 'POWER_SAVE'):

    SADsPath = "^inNT' //PrintServerNafre/PrintQ-jeueNatne":

    $p = tvin32: :OLE->GetOoject($ADsPath) or aie "Невозможно полу-п-ть $ADsPat'.\^".

    print "Состояние принтера " . $c->{Name}

    ((exists $p--"i;jt au.s}; ? $status<$c-'{atatub) t . Nji ACilVL )

    Объект Printoutip имеет несколько методов для контроля очереди печати: PausoO. Это позволяет управлять действиями самой очереди. А что если мы захотим изучить или обработать конкретные задачи из очереди?

    Для того чтобы добраться до самих заданий, необходимо вызвать метод PnntJobs() объекта возвращает коллекцию, состоящую из объектов PrintJob, каждый из которых имеет ряд свойств и методов. Например, вот как можно показать список заданий из определенной очереди:

    use Win32::OLE

    и таблица получена из раздела

    и Active Directory Service Interfaces 2.5->ADSI Reference

    Я ADSI Interfaces->Dynamic Object Interfaces-MADsPrintJotiOporai зспз->



    fl lAOsPrintJobOperations Property Methods' (двойное уф) в ADSI 2.5 SDK

    %status = (0x00000001 => 'PAUSED', 0x00000002 => 'ERROR'.

    0x00000004 => 'DELETING',0x00000010 => 'PRINTING',

    0x00000020 => 'OFFLINE', 0x00000040 ^> 'PAPEROUT'.

    0x00000080 => 'PRINTED1, 0x00000100 -> 'DELETED1):

    SADsPath = "WinNT://PrintServerName/PrintQueueNamc":

    $p = Win32: :GLE->GetObject($AQsPath) or

    die "Невозможно лолучип, SADsPaV'\'i

    $jobs = $p->PrintJobs(); foreach $job (in $jobs){ print $]ob->{User( .

    $status{$job->{status}} . "\n"; }

    Каждое задание можно приостановить (Раиse()) и продолжить.



    Работа со службами NT/2000 через ADSI



    В последнем наборе примеров рассмотрим, как находить, запускать и останавливать службы на машине с NT/2000. Как и другие примеры из этой главы, эти короткие программки необходимо запускать с достаточными привилегиями для осуществления выполняемых действий.

    Для получения списка служб на машине и их состояний можно использовать такую программу:

    us о w.l п32 : . ol& 1 г П эта таблица получена из раздела

    Я 'Active Directory Service Interfaces 2.5->ADSI fieference->

    И ADSI Interfaces->Dynamic Object Inter 'acec

    » IADsServiceOperatio:iS Proper*.> Methods' AC/SI

    %status =

    (0x00000001 => 'STOPPED'. 0x00000002= START_PEMDIHG',

    0x00000003 => 'STGP_PENDING'. 0x00000004=:- RUNNING',

    0x00000005 => 'CONTINUE_PENDING' .0x00000006 => ' PAUSE_PEtJDING '.

    0x00000007 => 'PAUSED'. 0x00000008 => 'ERROR'):

    SADsPath = "W]nNT://DomainNarr,e/ConiputerNa!ne, computer":

    $C = Win32: :OLE->GctObj'oct($ADsPath) or die "Невозможно получить $ADsPatn\;i":

    foreach $adsobj (in $c){ print Sadsobj^tDisplayNarr.

    if ($adsobj->{Class) eq "Service"); }

    Для запуска, остановки, приостановки или продолжения работы службы вызываются очевидные методы (Star t(), Stop() и т. д.). Вот как можно запустить службу Network Time на машине с Windows 2000, если ранее она была остановлена:

    use Win32::OLE;

    SADsPath = "WinNT://DomainName/ComputerName/W32Tirne, service";

    $s = Win32::OLE->GetObject($ADsPath) or die "Невозможно получить $ADsPath\n";

    $s->Start();

    tf можно в этом месте проверять в цикле состояние

    пока служба не будет запущена

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

    use Win32::OLE;

    $d = Win32: :OLE->GetObject("WinNT://Do(r'ain");

    $c = $d->GetObiect( "Computer". Scornputername):

    $s = $c->GetOb]ect("Service". "W32Tiiie"):

    $s->Start():

    Эти примеры должны подсказать вам, какой контроль над системой можно получить при помощи ADSI из Perl. Службы каталогов и их интерфейсы могут быть весьма могущественной частью вашей компьютерной инфраструктуры.






    Содержание раздела