Журнал Системный Администратор, Февраль 2006

Журнал Системный Администратор

Февраль 2006

Цена: $4.5 US

  Подписаться

Зарегистриванные пользователи, пожалуйста следуйте этой ссылке

Версия для печати Вернуться к оглавлению

Вы всё ещё не используете WMI? Часть II: пишем сценарии

Часть II: пишем сценарии

Константин Леонтьев

Продолжаем изучение технологии WMI и способов ее применения в повседневной практике системного администрирования. Настало время перейти к теме написания скриптов, использующих WMI для управления нашей распределенной сетевой инфраструктурой.

Пишем сценарии с использованием WMI

Еще раз взгляните на изображение архитектуры WMI, которое я поместил в начало первой части статьи [1], рис. 1. Обратите внимание, что доступ к WMI может осуществляться через интерфейсы COM+ и .NET Framework. Это означает, что любой язык программирования, который поддерживает взаимодействие с Microsoft Windows COM+ и .NET Framework, может использоваться для работы с WMI. К перечню таких языков, в частности, относятся: VBScript, Visual Basic и Visual Basic .NET, Java Script, Python, Perl, PHP, C#, C++, Pascal, TCL и другие.

Обращение к объектам и методам WMI в разных языках может немного отличаться из-за специфики синтаксиса работы с объектами и типами для каждого конкретного языка, но в целом все приемы очень сходны.

Для иллюстрации приведу несколько примеров (см. листинги 1-6). Ознакомившись с ними, очевидно, что неважно, какой язык вы выберите, работа с WMI из любого языка программирования не вызывает затруднений. Поэтому используйте тот язык написания сценариев, который вам лучше известен и которым вам больше нравится пользоваться.

Листинг 1. VBScript

 

strComputer = "."

Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\CIMV2")

Set colItems = objWMIService.ExecQuery( _

    "SELECT * FROM Win32_NetworkAdapterConfiguration",,48)

For Each objItem in colItems

    Wscript.Echo "-----------------------------------"

    Wscript.Echo "Win32_NetworkAdapterConfiguration instance"

    Wscript.Echo "-----------------------------------"

    If isNull(objItem.IPAddress) Then

        Wscript.Echo "IPAddress: "

    Else

        Wscript.Echo "IPAddress: " & Join(objItem.IPAddress, ",")

    End If

Next

 

Листинг 2. VB.NET

 

Imports System

Imports System.Management

Imports System.Windows.Forms

 

Namespace WMISample

 

    Public Class MyWMIQuery

 

        Public Overloads Shared Function Main() As Integer

 

            Try

                Dim searcher As New ManagementObjectSearcher( _

                    "root\CIMV2", _

                    "SELECT * FROM Win32_NetworkAdapterConfiguration")

 

                For Each queryObj As ManagementObject in searcher.Get()

 

                    Console.WriteLine("-----------------------------------")

                    Console.WriteLine("Win32_NetworkAdapterConfiguration instance")

                    Console.WriteLine("-----------------------------------")

 

                    If queryObj("IPAddress") Is Nothing Then

                        Console.WriteLine("IPAddress: {0}", queryObj("IPAddress"))

                    Else

                        Dim arrIPAddress As String()

                        arrIPAddress = queryObj("IPAddress")

                        For Each arrValue As String In arrIPAddress

                            Console.WriteLine("IPAddress: {0}", arrValue)

                        Next

                    End If

                Next

            Catch err As ManagementException

                MessageBox.Show("An error occurred while querying for WMI data: " & err.Message)

            End Try

        End Function

    End Class

End Namespace

 

Листинг 3. C#

 

using System;

using System.Management;

using System.Windows.Forms;

 

namespace WMISample

{

    public class MyWMIQuery

    {

        public static void Main()

        {

            try

            {

                ManagementObjectSearcher searcher =

                    new ManagementObjectSearcher("root\\CIMV2",

                    "SELECT * FROM Win32_NetworkAdapterConfiguration");

 

                foreach (ManagementObject queryObj in searcher.Get())

                {

                    Console.WriteLine("-----------------------------------");

                    Console.WriteLine("Win32_NetworkAdapterConfiguration instance");

                    Console.WriteLine("-----------------------------------");

 

                    if(queryObj["IPAddress"] == null)

                        Console.WriteLine("IPAddress: {0}", queryObj["IPAddress"]);

                    else

                    {

                        String[] arrIPAddress = (String[])(queryObj["IPAddress"]);

                        foreach (String arrValue in arrIPAddress)

                        {

                            Console.WriteLine("IPAddress: {0}", arrValue);

                        }

                    }

                }

            }

            catch (ManagementException e)

            {

                MessageBox.Show("An error occurred while querying for WMI data: " + e.Message);

            }

        }

    }

}

 

Листинг 4. Perl

 

use strict;

use Win32::OLE('in');

 

use constant wbemFlagReturnImmediately => 0x10;

use constant wbemFlagForwardOnly => 0x20;

 

my $computer = ".";

 

   my $objWMIService = Win32::OLE->GetObject("winmgmts:\\\\$computer\\root\\CIMV2") or die "WMI connection failed.\n";

   my $colItems = $objWMIService->ExecQuery("SELECT * FROM Win32_NetworkAdapterConfiguration", "WQL", wbemFlagReturnImmediately | wbemFlagForwardOnly);

 

   foreach my $objItem (in $colItems) {

      print "-----------------------------------"\n";

      print "Win32_NetworkAdapterConfiguration instance";

      print "-----------------------------------\n";

      print "IPAddress: " . join(",", (in $objItem->{IPAddress})) . "\n";

   }

 

Листинг 5. JScript

 

var wbemFlagReturnImmediately = 0x10;

var wbemFlagForwardOnly = 0x20;

 

var strComputer = ".";

 

   var objWMIService = GetObject("winmgmts:\\\\" + strComputer + "\\root\\CIMV2");

   var colItems = objWMIService.ExecQuery("SELECT * FROM Win32_NetworkAdapterConfiguration", "WQL", wbemFlagReturnImmediately | wbemFlagForwardOnly);

 

   var enumItems = new Enumerator(colItems);

   for (; !enumItems.atEnd(); enumItems.moveNext()) {

      var objItem = enumItems.item();

 

      WScript.Echo("-----------------------------------");

      WScript.Echo("Win32_NetworkAdapterConfiguration instance");

      WScript.Echo("-----------------------------------");

 

      try { WScript.Echo("IPAddress: " + (objItem.IPAddress.toArray()).join(",")); }

         catch(e) { WScript.Echo("IPAddress: null"); }

   }

 

Листинг 6. WMIC

 

wmic nicconfig get IPAddress /value

Строим moniker string

Теперь более четко определимся, что такое moniker string? Дословный перевод этого словосочетания мог бы звучать примерно так: строка-прозвище или строка-кличка. По смыслу же это некое специальное имя (ссылка) для обращения к объектам или классам объектов WMI. Формат этой строки фактически представляет собой разновидность URL (Universal Resource Locator). Все moniker string начинаются с отличительного идентификатора Winmgmts:. Эта часть moniker string является обязательной. Далее формат зависит от того, как мы хотим обращаться к объекту.

Например, вот так:

Winmgmts://server01/root/cimv2:Win32_OperatingSystem

В приведенном примере server01 – это, как вы уже догадались, сетевое имя компьютера, на котором мы хотим получить доступ к объекту WMI. Если указать вместо сетевого имени символ «.» (точка), то подключение будет происходить к локальному компьютеру. Root/CIMv2 – это пространство имен репозитория WMI (по аналогии очень похоже на виртуальный каталог веб-сервера). Подробнее пространства имен я опишу чуть ниже. После двоеточия идет наименование класса WMI, к объектам которого мы хотим обратиться. В данном примере это класс Win32_OperatingSystem. Следует заметить, что в moniker string можно использовать не только прямую наклонную черту, но и обратную, так что строки «winmgmts://server01/root/cimv2» и «winmgmts:\\server01\root\cimv2» одинаковы.

При подключении к репозиторию WMI необязательно указывать имя локального сервера точкой. Строка может выглядеть и так: «WinMgmts:root/CIMv2». Если в строке moniker-string имя сервера не указано, то подключение произойдет и к локальному репозиторию WMI. Точно так же, как и в случае использования строки «WinMgmts://./root/CIMv2».

Пространства имен WMI

Что же такое WMI namespace (пространство имен WMI)? Пространство имен WMI – это раздел (директория) репозитория WMI, которая призвана группировать классы и объекты WMI по назначению, а также определять атрибуты безопасности при доступе к классам и объектам в каждом таком контейнере. Фактически это, как уже отмечалось выше, полная аналогия с директориями на веб-сервере (см. рис. 1). Все пространства имен начинаются с корня, который в WMI обозначается ключевым словом root. После имени корня через косую черту указывается пространство имен. Пространства имен могут быть вложенными. Пример того, как выглядит пространство имен: root/mynamespace/subnamespace. Подавляющее большинство классов и объектов, которые интересуют вас, размещается в пространстве имен root/CIMv2.

Рисунок 1. Дерево пространств имен WMI

Одно из существующих в Windows пространств имен WMI может быть выбрано по умолчанию. Это означает, что если вы попытаетесь подключиться к этому хосту, не указав в moniker-string необходимое пространство имен, то вы автоматически будете подключены к выбранному по умолчанию. В стандартной инсталляции Windows по умолчанию выбрано пространство имен root\cimv2.

Знакомство с WMI Code Creator 1.0

Теперь давайте познакомимся с одной из наиболее удобных и функциональных утилит для системного администратора – WMI Code Creator 1.0 [2]. На мой взгляд, сочетаемый этой утилитой функционал и удобство превосходят все остальные описанные мной ранее утилиты. В упражнении с этой утилитой мы будем решать задачу управления опцией Remote Desktop в свойствах системы, которая позволяет разрешить или запретить удаленные подключения к рабочему столу для задач администрирования. Этот пример будет работать только на ОС Windows XP и новее, а также на  Windows 2000 Server с установленными службами терминалов.

Для начала скачайте и распакуйте эту утилиту. Запустите файл WMICodeCreator.exe и выберите в меню «Code Language Visual Basic Script». Затем проверьте в меню «Target Computer», что установлена опция «Local Computer». После этого удостоверьтесь, что у вас открыта закладка «Query for data from WMI class», и на этой закладке выберите класс «Win32_TerminalServiceSetting». В наборе свойств (properties) выделите «AllowTSConnections», как это показано на рис. 2.

Рисунок 2. Окно утилиты WMI Code Creator 1.0 с открытой закладкой Query

Свойство «Win32_TerminalService Setting.AllowTSConnections» определяет состояние опции «Remote Desktop» в свойствах системы. В  правом окне вы увидите скрипт, который сгенерировала утилита для того, чтобы вывести на экран выбранное вами  свойство. Нажмите кнопку «Search for Property Values». Вы увидите все варианты значений этого свойства для всех экземпляров объектов класса «Win32_TerminalServiceSetting». Нажмите кнопку «Execute Code». Откроется окно командной строки, где будет выведен результат работы скрипта. Если опция «Remote Desktop» включена, то будет отображена 1, в противном случае 0. Проверьте в свойствах вашей системы, что результат работы скрипта совпадает с тем, как установлена опция «Remote Desktop».

Теперь откройте закладку «Execute a method». Снова выберите класс «Win32_TerminalServiceSetting». В списке методов выберите «SetAllowTS Connections», как это показано на рис. 3.

Рисунок 3. Окно утилиты WMI Code Creator 1.0 с открытой закладкой Execute

Затем в списке «Method [in] parameters» установите значение «AllowTSConnections» противоположное от того, что мы получили на предыдущем шаге. Для этого нужно просто кликнуть мышью на входной параметр метода. Нажмите кнопку «Execute Code». Откроется консольное окно, в котором будет выдано значение, возвращенное методом SetAllowTSConnections. Если все прошло удачно – это будет 0. Теперь проверьте, изменилась ли опция «Remote Desktop» в свойствах вашей системы.

Обратите внимание на то, что вы можете очень быстро получить контекстную справку из библиотеки MSDN по выбранному классу WMI, щелкнув мышью по ссылке: «Get Documentation for this class from the online MSDN Library».

Краткий обзор вопросов безопасности WMI

Если внимательно изучить схему архитектуры WMI, которую я приводил в начале статьи, то легко заметить, что все взаимодействие с ядром WMI происходит с использованием интерфейсов COM+/DCOM. В свою очередь COM+ и DCOM в качестве транспортного протокола используют RPC. Эта архитектурная особенность накладывает определенный отпечаток на идеологию системы безопасности WMI. В частности, для людей, знакомых с технологиями DCOM и COM+, слова «имперсонация» и «делегирование» не новы. Однако среди читателей большинство – системные администраторы, а не программисты, поэтому я постараюсь уделить некоторое внимание этим вопросам.

Для того чтобы некая учетная запись имела возможность подключаться к репозиторию WMI, необходимо дать ей соответствующие права. Права, как вы уже могли догадаться, нужно дать как на пространство имен – WMI name space (воспользовавшись оснасткой wmimgmt.msc), так и на DCOM-приложения диспетчера WMI – CIM Object Manager (воспользовавшись оснасткой управления COM+ comexp.msc или утилитой dcomcnfg.exe). Вот минимальный список приложений DCOM, права на которые необходимы для удаленной работы с WMI: Windows Management and Instrumentation, Microsoft WMI Provider Subsystem Host. Некоторые сведения по вопросам настройки прав доступа к WMI и сетевой безопасности вы можете почерпнуть из статьи Microsoft Knowledge Base KB875605 или из библиотеки MSDN [3]. Права на другие DCOM-приложения могут понадобиться в зависимости от используемого режима имперсонации.

Имперсонация

Зачем нужна имперсонация? Все довольно просто, это метод, при котором для подключения к ресурсу процесс (поток) или система должны использовать не свой контекст безопасности, а учетные данные другого субъекта безопасности. Представьте, что некая служба, запущенная в контексте безопасности LocalSystem, должна выполнить действие от лица другой учетной записи (например, от лица текущего зарегистрированного на компьютере пользователя). В этом случае ей необходимо создать специальный маркер доступа (Access Token), описывающий контекст безопасности той учетной записи, под которой мы хотим выполнить указанное действие. Безусловно, для того чтобы создать такой маркер доступа, этой службе необходимо знать учетные данные этого пользователя или, если этот процесс происходит на локальной машине, получить копию маркера доступа зарегистрированного локально пользователя (безусловно, для этого контекст безопасности службы должен обладать привилегией создания маркеров доступа).

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

В случае с WMI делегирование может выглядеть так – мы, работая на станции администратора, подключаемся по WMI к некому серверу и запускаем на нем процесс с помощью метода Execute класса Win32_Process. Теперь представим, что этот процесс есть не что иное, как другой скрипт WMI, который подключается к еще одному хосту в сети для того, чтобы сделать какие-то действия. Если мы не воспользуемся делегированием, то на конечной машине скрипт будет запущен в контексте безопасности учетной записи промежуточного сервера, что далеко не всегда желаемо. С другой стороны, подобная ситуация с делегированием в реальной жизни требуется крайне редко.

В таблице 1 представлены поддерживаемые уровни имперсонации WMI.

Таблица 1. Уровни имперсонации

 

Уровень имперсонации

Описание

Anonymous

1

Анонимный уровень имперсонации COM, маскирующий учетную запись вызывающего. Вызов WMI с этим уровнем имперсонации может завершиться ошибкой

Default

0

Уровень имперсонации по умолчанию

Delegate

4

Уровень имперсонации COM – делегирование. Разрешает использовать другим объектам учетные данные вызывающего субъекта для обращения к третьим объектам.  Этот уровень может дать неоправданно высокие привилегии промежуточному объекту. Поддерживается только на Windows 2000 и выше

Identify

2

Уровень имперсонации COM – идентификация. Позволяет объектам вызова запрашивать учетные данные у вызывающего субъекта. Вызов WMI с этим уровнем имперсонации может завершиться с ошибкой

Impersonate

3

Уровень имперсонации COM – обычная имперсонация. Позволяет вызываемому объекту использовать учетные данные вызывающего субъекта для совершения только своих действий. Это рекомендуемый уровень имперсонации

Аутентификация

Аутентификация, целостность и конфиденциальность являются неотъемлемыми характеристиками безопасного взаимодействия систем по сети. При использовании WMI поддерживаются перечисленные в таблице 2 уровни аутентификации и проверки подлинности. Наиболее часто употребимый уровень – Connect (аутентификация и авторизация при вызове). Однако если вы хотите предотвратить возможное изменение передаваемых данных или их перехват методом men in the middle, то лучшим выбором могут являться режимы Pkt (проверка аутентичности клиента), PktIntegrity (проверка аутентичности клиента и целостности передаваемых данных) и PktPrivacy (проверка аутентичности клиента и шифрование передаваемых данных с проверкой целостности).

Таблица 2. Варианты аутентификации и проверки целостности

 

Уровень аутентификации

Описание

Call

Call

3

Call-level COM authentication.

Аутентификация в начале каждого вызова объекта WMI

Connect

Connect

2

Connect-level COM authentication.

Аутентификация только при установлении соединения с сервером WMI. Одни учетные данные используются для всего сеанса взаимодействия

Default

Default

0

WMI использует настройки аутентификации COM по умолчанию

None

None

1

Аутентификация COM не используется

Packet

Pkt

4

Packet-level COM authentication.

Аутентификация всех данных, получаемых от клиента, с подтверждением подлинности отправителя для каждого RPC-пакета

PacketIntegrity

PktIntegrity

5

Packet Integrity-level COM authentication.

Аутентификация и проверка целостности передаваемых данных для каждого RPC-пакета

PacketPrivacy

PktPrivacy

6

Packet Privacy-level COM authentication.

Аутентификация, проверка целостности и шифрование данных каждого передаваемого RPC-пакета

Привилегии

Администраторам Windows хорошо известны настройки безопасности системы и их раздел «User Right Assignments» (привилегии пользователей), доступные в консоли безопасности системы и групповых политиках домена. Ряд действий с операционной системой можно проделать только при наличии у пользователя или группы, куда он входит, той или иной привилегии. К таким действиям относятся, например, перезагрузка системы (завершение ее работы), восстановление состояния системы из резервной копии или смена системного времени.

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

Список привилегий и их численных кодов указан в таблице 3.

Таблица 3. Привилегии

 

Привилегии

Описание

wbemPrivilegeCreateToken

SeCreateTokenPrivilege

CreateToken

1

0x1

Привилегия требуется для создания основного токена безопасности процесса

wbemPrivilegePrimaryToken

SeAssignPrimaryTokenPrivilege

AssignPrimaryToken

2

0x2

Привилегия требуется для замены (назначения нового) основного токена безопасности процесса

wbemPrivilegeLockMemory

SeLockMemoryPrivilege

3

0x3

Привилегия требуется для закрепления соответствия между страницами физической памяти и логического адресного пространства

wbemPrivilegeIncreaseQuota

SeIncreaseQuotaPrivilege

IncreaseQuotaPrivilege

4

0x4

Привилегия требуется для назначения квот процессу

wbemPrivilegeMachineAccount

SeMachineAccountPrivilege

MachineAccount

5

0x5

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

wbemPrivilegeTcb

SeTcbPrivilege

Tcb

6

0x6

Привилегия обозначает ее владельца как часть Trusted Computer Base

wbemPrivilegeSecurity

SeSecurityPrivilege

Security

7

0x7

Привилегия требуется для выполнения ряда функций, связанных с безопасностью, например просмотр журналов аудита. Привилегия определяет её владельца как Security Operator

wbemPrivilegeTakeOwnership

SeTakeOwnershipPrivilege

TakeOwnership

8

0x8

Привилегия требуется для получения права владельца объекта на объекты безопасности в отсутствии явных на то разрешений

wbemPrivilegeLoadDriver

SeLoadDriverPrivilege

LoadDriver

9

0x9

Привилегия требуется для загрузки и выгрузки драйверов устройств

wbemPrivilegeSystemProfile

SeSystemProfilePrivilege

SystemProfile

10

0xA

Привилегия требуется для сбора профилирующей информации всей системы

wbemPrivilegeSystemtime

SeSystemtimePrivilege

Systemtime

11

0xB

Привилегия требуется для изменения системного времени

wbemPrivilegeProfileSingleProcess

SeProfileSingleProcessPrivilege

ProfileSingleProcess

12

0xC

Привилегия требуется для сбора профилирующей информации для одного процесса

wbemPrivilegeIncreaseBasePriority

SeIncreaseBasePriorityPrivilege

IncreaseBasePriority

13

0xD

Привилегия требуется для увеличения базового проиритета процесса

wbemPrivilegeCreatePagefile

SeCreatePagefilePrivilege

CreatePagefile

14

0xE

Привилегия требуется для создания и (или) изменения файла подкачки

wbemPrivilegeCreatePermanent

SeCreatePermanentPrivilege

CreatePermanent

15

0xF

Привилегия требуется для создания постоянного общего объекта

wbemPrivilegeBackup

SeBackupPrivilege

Backup

16

0x10

Привилегия требуется для выполнения резервного копирования

wbemPrivilegeRestore

SeRestorePrivilege

Restore

17

0x11

Привилегия требуется для выполнения операции восстановления. Эта привилегия позволяет ее владельцу устанавливать для любого объекта произвольный существующий SID в качестве владельца объекта

wbemPrivilegeShutdown

SeShutdownPrivilege

Shutdown

18

0x12

Привилегия требуется для перезагрузки и завершения работы ОС

wbemPrivilegeDebug

SeDebugPrivilege

Debug

19

0x13

Привилегия требуется для отладки процессов

wbemPrivilegeAudit

SeAuditPrivilege

Audit

20

0x14

Привилегия требуется для записи в журналы аудита

wbemPrivilegeSystemEnvironment

SeSystemEnvironmentPrivilege

SystemEnvironment

21

0x15

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

wbemPrivilegeChangeNotify

SeChangeNotifyPrivilege

ChangeNotify

22

0x16

Привилегия требуется для получения нотификаций об изменении файлов и директорий. Так же эта привилегия отменяет перекрестную проверку доступа к файлам и папкам. Эта привилегия по умолчанию дана всем пользователям

wbemPrivilegeRemoteShutdown

SeRemoteShutdownPrivilege

RemoteShutdown

23

0x17

Привилегия требуется для завершения работы ОС по сети

wbemPrivilegeUndock

SeUndockPrivilege

Undock

24

0x18

Привилегия требуется для снятия компьютера с док-станции

wbemPrivilegeSyncAgent

SeSyncAgentPrivilege

SyncAgent

25

0x19

Привилегия требуется для вызова процедуры синхронизации службы каталога

wbemPrivilegeEnableDelegation

SeEnableDelegationPrivilege

EnableDelegation

26

0x1A

Привилегия требуется для доверия пользователям или группам при делегировании

wbemPrivilegeManageVolume

SeManageVolumePrivilege

ManageVolume

27

0x1B

Привилегия требуется для операций обслуживания дисковых томов

Для каждой привилегии указано три имени: первое имя – это константы для использования в скриптах VBScript, второе имя – это символические константы языка С++, третье имя привилегии – это имя, использующееся при составлении moniker string.

Каким образом активировать эти привилегии, показано в примерах из раздела «Подключение к удаленным компьютерам». Важно запомнить, что активировать привилегии нужно до подключения к репозиторию WMI, а не после.

Подключение к удаленным компьютерам

Подключение к другим компьютерам по сети чаще всего вызывает проблемы у тех, кто только начинает осваивать технологию WMI и программирование с ее использованием. Поэтому я считаю необходимым привести примеры кода на VBScript, которые позволяют решать эту задачу, и кратко пояснить их. Единственное, что необходимо запомнить, так это то, что запрещается подключаться к репозиторию WMI на локальном компьютере, используя другую учетную запись (не ту, под которой происходит обращение к CIM Object Manager). Это ограничение обычно решается использованием утилиты RunAs и ей подобных методов.

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

strComputer = "server01"

 

Set objLocator = CreateObject("WbemScripting.SWbemLocator")

objLocator.Security_.AuthenticationLevel = 3

objLocator.Security_.Privileges.Add(18)

 

Set objWMIService = objLocator.ConnectServer(_

    strComputer, "root\cimv2", "mydomain\administrator", "password")

 

objWMIService.Security_.ImpersonationLevel = 3

 

Set colItems = objWMIService.ExecQuery(_

    "SELECT * FROM Win32_OperatingSystem",,48)

 

For Each objItem in colItems

    Wscript.Echo "-----------------------------------"

    Wscript.Echo "Win32_OperatingSystem instance"

    Wscript.Echo "-----------------------------------"

    Wscript.Echo "Caption: " & objItem.Caption

    Wscript.Echo "Name: " & objItem.Name

 

    Rem Первый вариант вызова метода Reboot()

    Set objOutParams = objWMIService.ExecMethod(_

    "Win32_OperatingSystem.Name='" & CStr(objItem.Name) & "'", "Reboot")

 

    Rem Второй вариант вызова метода Reboot()

    objItem.Reboot()

Next

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

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

strComputer = "server01"

 

Set objWMIService = GetObject(_

"WinMgmts:{impersonationLevel=Impersonate, authenticationLevel=Call, (Shutdown)}!//" & strComputer & "/root/cimv2")

 

Set colItems = objWMIService.ExecQuery(_

    "SELECT * FROM Win32_OperatingSystem",,48)

 

For Each objItem in colItems

    Wscript.Echo "-----------------------------------"

    Wscript.Echo "Win32_OperatingSystem instance"

    Wscript.Echo "-----------------------------------"

    Wscript.Echo "Caption: " & objItem.Caption

    Wscript.Echo "Name: " & objItem.Name

 

    Rem Первый вариант вызова метода Reboot()

    Set objOutParams = objWMIService.ExecMethod(_

    "Win32_OperatingSystem.Name='" & CStr(objItem.Name) & "'", "Reboot")

 

    Rem Второй вариант вызова метода Reboot()

    objItem.Reboot()

Next

В данном примере мы познакомились с еще одной возможностью, заложенной в moniker string. Мы можем включать в строку moniker string в фигурных скобках «{ }» параметры имперсонации, аутентификации и набор используемых привилегий. Таким образом, полный формат moniker string можно было бы записать так:

Winmgmts:{ <security settings>, (<privileges>) }!//<server>/<namespace>:<Class | Object>

Есть и еще не описанные мной поля в формате moniker string, но поскольку они довольно редко нужны на практике, то я рекомендую желающим ознакомиться с ними по оригинальной документации на сайте Microsoft [3] самостоятельно.

События WMI и уведомляющие запросы

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

Обработка событий может быть синхронной и асинхронной. Синхронная обработка событий – это когда процесс ожидает события и более ничем не занят. Обычно это ожидание – бесконечный цикл проверки условия: поступило событие или нет. Асинхронная обработка подразумевает, что процесс регистрирует обработчик события (подписывается на событие) и далее продолжает выполнять различные задачи. Когда событие возникает, нормальная работа процесса прерывается, запоминается место, где произошло прерывание, а управление передается на зарегистрированный обработчик событий. После обработки события обработчиком, управление возвращается на то действие основного процесса, которое было прервано.

Оба приведенных ниже скрипта отслеживают запуск процесса с именем cmd.exe. Однако между ними все же есть различие.

Приведенный ниже скрипт (первый из двух) использует синхронную технику уведомляющего запроса для создания экземпляра объекта Win32_ProcessStartTrace, который отвечает за отслеживание событий запуска процессов о системе. Каждый раз, когда в системе порождается процесс с именем cmd.exe, метод NextEvent возвращает управление скрипту.

strComputer = "."

Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\CIMV2")

 

Set objEvents = objWMIService.ExecNotificationQuery _

("SELECT * FROM Win32_ProcessStartTrace WHERE ProcessName = 'cmd.exe'")

 

Wscript.Echo "Waiting for events ..."

Do While(True)

    Set objReceivedEvent = objEvents.NextEvent

 

    Wscript.Echo "CMD.EXE started"

 

Loop

Второй скрипт, который приведен ниже, использует немного другую синхронную технику. Этот метод более универсальный, так как не завязан на специальный класс WMI Win32_ProcessStartTrace. С помощью уведомляющего запроса отслеживается состояние всего пула объектов класса Win32_Process. Каждый раз, когда в системе порождается новый экземпляр объекта, принадлежащего классу Win32_Process и полем Description, содержащим строку cmd.exe, метод NextEvent возвращает управление скрипту.

strComputer = "."

Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\CIMV2")

 

Set objEvents = objWMIService.ExecNotificationQuery("SELECT * FROM __InstanceCreationEvent WITHIN 1 WHERE (TargetInstance ISA 'Win32_Process') AND (TargetInstance.Description = 'cmd.exe')")

 

Wscript.Echo "Waiting for events ..."

Do While(True)

    Set objReceivedEvent = objEvents.NextEvent

 

    Wscript.Echo "CMD.EXE started"

 

Loop

Следует отметить, что скрипты WSH и ядро WMI помимо синхронного ожидания событий позволяют асинхронно обрабатывать события и исключения. Вы можете не отдавать управления из скрипта менеджеру событий WMI и продолжать выполнение необходимых действий. В то же время, когда возникнет отслеживаемое вами событие, управление будет передано на специальную подпрограмму скрипта, и будут выполнены необходимые действия. Это работает так же, как прерывания или события форм в Visual Basic или Visual Basic for Application.

Использование техники регистрации асинхронных событий и их обработки требует детального изложения материала и не умещается в рамки этой статьи. Возможно, этот материал появится в виде отдельной статьи. Намекну лишь, что WMI Code Creator 1.0 позволяет легко создавать скрипты для асинхронной обработки событий WMI.

Есть еще один, принципиально иной способ обработки событий с использованием WMI. С помощью создания экземпляров объектов специальных классов (классов с общим названием Standard Event Consumers) отслеживать события и выполнять несколько типовых действий (отправка уведомления по SMTP, запись в журнал событий, запись в текстовый файл, запуск приложения, запуск скрипта) вообще без написания скриптов WSH. Однако об этом методе мы поговорим в другой раз.

Продукты, использующие WMI

После знакомства с технологией WMI у читателей может возникнуть желание начать разрабатывать собственные скрипты и системы управления и мониторинга. Безусловно, это похвальное желание, но, возможно, не стоит поступать столь опрометчиво. Возможно, стоит оглянуться по сторонам и обнаружить, что уже многое сделано в этом направлении другими людьми и их решения успешно развиваются. Для начала советую заглянуть в подборку скриптов в TechNet Script Center [4, 5]. Если все же вы не горите желанием объединять разрозненные скрипты в единую систему управления и мониторинга и фактически изобретать велосипед, советую вам обратить внимание на продукты Microsoft SMS 2003 (http://www.microsoft.com/smserver) и Microsoft Operations Manager 2005 (