Часть 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 (