Как заметил в одном из бюллетеней Technet сотрудник российского представительства Microsoft Андрей Бешков, лень — одна из добродетелей ИТ-специалиста. Далее Андрей пояснил, что именно поэтому он обожает все автоматизировать с помощью VBS и PowerShell.

Позволю себе выразить сомнение. Если Андрей любит сам все автоматизировать, и, судя по его докладам на семинарах Technet и блогу, неплохо разбирается во всех инженерных тонкостях, лень — никак не его добродетель.

Я в прошлом сам инженер, а ныне — руководитель ИТ-службы одного из крупных поволжских холдингов, и могу подтвердить, что ИТ-специалисты, как правило, действительно большие лентяи. Однако автоматизировать свои задачи они тоже не хотят. В основном системные администраторы предпочитают брать готовые решения, которые можно найти, например, в центре сценариев на сайте Microsoft.

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

Однако не всегда получается найти в репозитории готовый сценарий или же необходимо адаптировать стандартное решение под свою конкретную задачу. Вот здесь «ленивый» специалист вряд ли сможет разобраться во всех тонкостях этой сложной технологии, поскольку волей-неволей приходится тратить время на изучение теории. Сценарии Windows не работают сами по себе, а опираются на другие технологии Microsoft, такие как COM, ActiveX, WMI, ADSI, и для их самостоятельного написания одного знания языков программирования недостаточно. Литературы, в которой были бы подробно описаны эти технологии, мало, да и выпущены книги ограниченными тиражами. Основными местами, где можно найти теорию, остаются Internet-ресурсы MSDN и Центр сценариев Windows.

Из-за хаотичности информации по написанию сценариев многие ИТ-специалисты путаются в этих дебрях. Приведу случай из своей практики. После того как я выложил кое-какие из своих разработок на http://forum.sysadmins.ru/, ко мне стали обращаться люди с просьбами помочь в автоматизации своих повседневных задач. Один молодой человек попросил меня помочь написать сценарий для поиска и перемещения файлов определенных расширений на клиентских компьютерах под управлением Windows XP, причем, как он сразу сказал, на PowerShell. На мой вопрос, почему он не хочет это сделать, используя более привычный для сценариев VBScript, он ответил: «VBScript — старье. Зачем жить вчерашним днем?» Пришлось немного рассказать ему о механизмах работы сценариев Windows.

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

Немного истории

В ранних версиях Windows NT единственными средствами автоматизации были пакетные (.bat) файлы, выполняемые командным интерпретатором cmd.exe. Их возможности были ограниченны и не позволяли написать полноценное решение по автоматизации. Пакетные файлы могли реализовать только базовую функциональность при работе с файловой системой, не могли работать с реестром (за исключением внесения в реестр данных из reg-файлов) и со службой каталогов Windows. Практически полностью отсутствовала возможность удаленного управления другими компьютерами с Windows NT. В качестве альтернативы cmd.exe в первых версиях Windows NT иногда применялся позаимствованный из OS/2 командный интерпретатор ReXX.

Попытки Windows-администраторов того времени что-либо автоматизировать путем написания пакетных файлов или сценариев на языке ReXX вызывали постоянные насмешки со стороны коллег — администраторов UNIX, изначально имевших в своем распоряжении разнообразный набор командных оболочек. За системными администраторами Windows NT 90-х годов прочно закрепилась репутация посредственных специалистов, не умеющих ничего, кроме как перетаскивать значки и щелкать мышью.

Нельзя сказать, что компания Microsoft ничего не предпринимала, чтобы хоть как-то улучшить положение. Набор Windows NT Resource Kit включал в себя большое количество дополнительных утилит, значительно расширяющих возможности командной строки. В Windows NT 4.0 Resource Kit был включен интерпретатор языка сценариев, KiXtart. Этот язык сценариев хорошо знаком администраторам Windows старшего поколения. По сравнению с cmd.exe, KiXtart давал администратору достаточно большую свободу действий, но выполнение сценариев на этом языке также было связано с рядом сложностей. Поскольку KiXtart не являлся стандартным компонентом Windows NT, требовалась либо установка Resource Kit на все клиентские компьютеры, либо разного рода ухищрения (широко применялось помещение файлов библиотек интерпретаторов в сетевой ресурс Netlogon, который служил также местом хранения сценариев в Windows NT).

Настоящим поворотным моментом в сценариях Windows стало появление в 1996 году технологии ActiveX. Созданная изначально для Web-приложений (напомню, что первоначально термин ActiveX обозначал Web-компоненты созданные на базе технологии COM), ActiveX в скором времени стала ключевой во всех продуктах Microsoft. Применение этой технологии в продуктах Microsoft Office, Internet Explorer, IIS потребовало специальных языков сценариев для управления объектами автоматизации. Такими языками стали VBScript и JScript.

Язык VBScript (Visual Basic Scriptin Edition) является урезанной, так сказать, «облегченной» версией языка Visual Basic, и по синтаксису он очень похож на VB или VBA. Язык JScript является реализацией Microsoft стандарта ECMA для языка сценариев (ECMAScript Edition 3). По синтаксису данный язык практически полностью идентичен JavaScript от Sun Microsystems и очень похож на СС++ (так называемые С-подобные языки — С-like languages).

Применение языков сценариев в Web-технологиях Microsoft в первое время никак не отразилось на положении дел с автоматизацией самих операционных систем Windows. Только в Windows 98 разработчики Microsoft впервые включили исполняемую среду сценариев, написанных на указанных выше языках, — сервер сценариев или Windows Scripting Host (WSH, позднее данная аббревиатура стала расшифровываться как Windows Script Host). С этого момента использование объектов ActiveX стало возможным непосредственно из операционной системы. WSH предоставляет системным администраторам собственный набор объектов ActiveX для выполнения разнообразных задач: операции с файловой системой, сетью, реестром, выполнение сценариев на удаленных компьютерах и другие возможности.

В дальнейшем WSH стал активно развиваться. После WSH 1.0, который был реализован в Windows 98, все последующие версии Windows также включали в себя более новый сервер сценариев: Windows 2000 — WSH 5.1, Windows XP и Windows Server 2003 — WSH 5.6, Windows Vista — WSH 5.7 (такая нумерация версий связана с тем, что начиная с Windows 2000 разработчики Microsoft решили привязать версию WSH к версиям языков VBScript и JScript). WSH можно установить и использовать в любой 32-разрядной операционной системе Windows. Установочные файлы доступны в центре загрузки Microsoft по ссылке: http://www.microsoft.com/downloads/Search.aspx?displaylang=en (нужно набрать в поиске Windows Script Host).

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

WSH 1.0 поддерживал только одиночные файлы сценариев на VBScript или JScript, по расширению определяя тип сценария: js — JScript, vbs — VBScript. В последующих версиях WSH появилась возможность использовать два языка одновременно, объединяя их средствами языка XML. Созданные таким образом сценарии имеют расширение wsf (WS-файлы). Строго говоря, WS-файлы являются уже не просто сценариями, а приложениями XML. Они основаны на схеме WS XML, которая в свою очередь опирается на схему W3 C XML, и представляют собой файл с разметкой XML. Код на VBScript и JScript является частью WS-файла, подобно коду, встроенному в динамические HTML-страницы.

Начиная с Windows 2000 компания Microsoft активно применяет новую технологию управления — Windows Management Instrumentation (WMI). WMI была создана на базе модели управления предприятием на основе Web (Web-Based Enterprise Management, WBEM) рабочей группой по управлению распределенными системами Distributes Management Task Force (DMTF). В основе реализации WMI лежит все та же технология COM. WMI состоит из следующих компонентов.

  • Управляемые объекты/ресурсы WMI — любые логические и физические компоненты Windows, доступ к которым может быть получен с помощью WMI.
  • Ядро, которое, в свою очередь, состоит из репозитория классов WMI, менеджера объектов WMI и провайдеров WMI, и обеспечивает связь управляющих программ с управляемыми объектами. Одним из провайдеров WMI является библиотека поддержки сценариев WMI, которая позволяет получать доступ к объектам WMI с помощью сценариев WSH. Наряду с ней есть и другие провайдеры, которые обеспечивают доступ к WMI из приложений Win32 API и систем управления базами данных.
  • Управляющие программы — любые программные компоненты (приложения Win32, сценарии WSH и т. п.) с помощью которых обеспечивается доступ к управляемым объектам.

Технология WMI сложна и многообразна. Ее подробное описание потребует отдельной статьи, поэтому здесь мы не будем подробно на ней останавливаться. Всем, кто хочет детально ознакомится с WMI, советую обратиться к разделу MSDN «WMI Reference» по адресу: http://msdn.microsoft.com/en-us/library/aa394572.aspx

Другим важным моментом стало появление вместе с Active Directory новой технологии администрирования службы каталога — Active Directory Service Interface, ADSI. ADSI представляет собой набор объектов ActiveX, обеспечивающий единообразный доступ к службе каталога. Благодаря ADSI доступ к свойствам и методам объектов Active Directory стал возможным и из сценариев WSH.

Появление WMI и ADSI позволило реализовать на сценариях WSH самые разнообразные задачи по управлению и администрированию Windows. Работа со сценариями WSH стала неотъемлемой частью деятельности администратора систем Windows, как клиентских, так и серверных.

Следующим поворотным моментом в сценариях Windows несомненно стало появление.NET Framework и основанной на ней оболочки PowerShell (на этапе разработки называвшейся Monad). PowerShell является принципиально новой технологией, не зависящей от WSH. Сейчас компания Microsoft активно продвигает PowerShell, но говорить о полном отказе от старого доброго WSH пока еще рано. Несмотря на то что на этапе разработки Windows Vista предполагалось включить в ее состав PowerShell, в финальной версии эта среда отсутствует, хотя и доступна для загрузки и установки. Так что пока сценарии PowerShell могут найти применение только для решения каких-либо серверных задач, впрочем, на прошедших не так давно семинарах Technet Весна 2008 специалисты Microsoft уже открыто говорили, что PowerShell — серверная технология.

Немного программирования

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

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

Аналогом английского языка в мире WSH является VBScript. Этот язык программирования был принят Microsoft как стандарт де-факто для написания сценариев автоматизации WSH. Хотя WSH включает в себя два стандартных языка VBScript и JScript, в основном для написания сценариев используется именно первый. На нем написано и большинство сценариев, расположенных в центре сценариев Microsoft.

Причина такого «языкового шовинизма» VBScript прежде всего в том, что этот язык максимально адаптирован под задачи автоматизации и содержит множество встроенных средств для работы с внешними объектами COM (как мы покажем ниже) и графические элементы ввода данных.

Несмотря на это, для автоматизации практически любых задач может применяться и JScript. Но написать сценарий на JScript сложнее, чем на VBScript. Из-за отсутствия возможности напрямую выполнять методы внешних COM-объектов, как это можно делать в VBScript, в JScript приходится задействовать дополнительные механизмы. Также сложный С-подобный синтаксис реализации этих программных конструкций отпугивает тех ИТ-специалистов, которые имеют недостаточно опыта в программировании.

Есть, впрочем, и другое мнение. Поскольку я достаточно много писал на CC++ и успел полюбить именно эти языки, мне было непривычно писать на VBScript. Я по привычке постоянно ставил в конце строк «;», заключал в скобки параметры методов WSH, хотя в VBScript этого делать не требуется, забывал ставить Then после If и т. п. В результате на написание и отладку сценариев на VBScript у меня уходило больше времени, чем на реализацию тех же задач на JScript. Поэтому большинство моих сценариев написано именно на JScript. VBScript я применял там, где без него было ну никак не обойтись, например для реализации графического окна ввода данных (метод InputBox; к сожалению, в JScript нет аналога).

Различие в реализации одной и той же задачи на разных языках программирования мы рассмотрим на примере. Для того чтобы прочитать или сменить владельца файла или каталога, в Windows до версий Windows Vista и Windows Server 2008 не было встроенных средств командной строки (в Windows Vista и Windows Server 2008 появилась команда icacls, имеющая возможность работы с владельцами файлов и каталогов). Использовать графический интерфейс для этой цели было не слишком удобно — очень уж много манипуляций мышью надо произвести, чтобы добраться до параметров, определяющих владельца файла. Так, в один прекрасный момент я, устав щелкать мышью, решил написать сценарий просмотра и смены владельца файла, чтобы выполнять эту операцию из командной строки. Конечно, можно было задействовать соответствующую утилиту из комплекта Resource Kit, но уж очень хотелось реализовать данную операцию самому.

Для определения владельца файла я использовал класс WMI Win32_LogicalFileSecuritySetting, который описывает настройки безопасности файла. Для того чтобы получить владельца файла, необходимо получить WMI-объект свойств безопасности файла и выполнить метод данного класса GetSecurityDescriptor, чтобы получить дескриптор безопасности файла и оттуда уже получить ссылку на объект, содержащий информацию о владельце файла. Вот здесь и есть основное различие в реализации данной задачи на языках JScript и VBScript.

Язык JScript не позволяет напрямую обращаться к методам объектов WMI. Для выполнения методов WMI необходимо воспользоваться специальным методом ExecMethod_ объекта WMI SWbemObject.

Как выглядит код на JScript для считывания дескриптора безопасности и получения данных о владельце файла, показано в листинге 1.

Аналогичный код на VBScript показан в листинге 2.

Обратите внимание, что при выполнении метода GetSecurityDescriptor в JScript возвращается объект WMI-класса SwbemObject, и дальнейшее определение дескриптора производится путем считывания значения свойства данного объекта. VBScript, позволяющий напрямую обращаться к методам WMI, сразу возвращает объект дескриптора безопасности. В данном коде на VBScript значение wmiSecurityDescriptor является возвращаемым значением метода GetSecurityDescriptor.

Аналогичным образом для установки нового дескриптора безопасности файла нужно использовать соответствующий метод класса Win32_LogicalFileSecuritySetting SetSecurityDescriptor. Установка нового владельца файла производится путем записи его SID в дескриптор безопасности. Для получения имени пользователя я применял WMI-объекты учетной записи пользователя Win32_UserAccount, а для SID пользователя — Win32_SID.

В листинге 3 приведен код на JScript, реализующий данную задачу.

Аналогичный код на VBScript показан в листинге 4.

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

Особенности написания WMI-сценариев на языке JScript подробно описаны в статье MSDN «Writing WMI Scripts in JScript». Найти ее можно по ссылке: http://msdn.microsoft.com/en-us/library/aa394616(VS.85).aspx

Андрей Мишечкин (andy_mishechkin@hotmail.com) — заместитель директора по ИТ и связи, группа предприятий «Полад», г. Тольятти. Имеет сертификат MCSE


Листинг 1. Код на JScript для считывания дескриптора безопасности

//Файл, дескриптор безопасности которого будем считывать
var FileSpec = "C:oot.ini";
//Получение WMI-объекта дескриптора безопасности
var wmiSecSettings = GetObject("winmgmts:Win32_LogicalFileSecuritySetting.Path='"
+ FileSpec + " ' ");
//Выполнение метода GetSecurityDescriptor для получение дескриптора безопасности
var wmiOutParams = wmiSecSettings.ExecMethod_("GetSecurityDescriptor");
//Считывание дескриптора безопасности как свойства полученного WMI-объекта
wmiSecDesc = wmiOutParams.Descriptor;
//Объект владельца файла получен
wmiOwner = wmiSecDesc.Owner;


Листинг 2. Код для считывания дескриптора безопасности на VBScript

'Файл, дескриптор безопасности которого будем считывать
Dim FileSpec = "C:oot.ini"
'Получение WMI-объекта дескриптора безопасности
Set wmiSecSettings = GetObject ("winmgmts:Win32_LogicalFileSecuritySetting.path='"
& FileSpec & " ' ")
'Считывание дескриптора безопасности путем прямого выполнения метода GetSecurityDescriptor(
FOResult = wmiSecSettings.GetSecurityDescriptor(wmiSecurityDescriptor)
'Объект владельца файла получен
Set wmiOwner = wmiSecurityDescriptor.Owner


Листинг 3. Установка нового владельца файла, код на JScript

//Файл, дескриптор безопасности которого будем считывать
var FileSpec = "C:oot.ini";
//Считывание владельца файла
wmiSecSettings = GetObject("winmgmts:Win32_LogicalFileSecuritySetting.Path='" + FileSpec + " ' ");
//Создание WMI-объекта для текущего файла
wmiOutParams = wmiSecSettings.ExecMethod_("GetSecurityDescriptor");
//Выполнение метода получения дескриптора безопасности объекта WMI
wmiSecurityDescriptor = wmiOutParams.Descriptor;
//Парсинг выходного параметра метода объекта WMI
wmiFileOwner = wmiSecurityDescriptor.Owner;
//WMI обект для пользователя
wmiUser = GetObject("winmgmts:Win32_UserAccount.Domain='DOMAIN',Name='DomainAdmin'");
//WMI объект для SID данного пользователя
wmiSID = GetObject("winmgmts:Win32_SID.SID='" + wmiUser.SID + " ' ");
//Установка нового SID владельца и запись нового дескриптора безопасности
for(var i = 0; i < = wmiSID.SidLength; i++)
//Копирование SID объекта SID в SID объекта владельца файла
wmiFileOwner.SID(i)= wmiSID.BinaryRepresentation(i);
//Формирование входных параметров метода SetSecurityDescriptor
wmiMethod = wmiSecSettings.Methods_.Item("SetSecurityDescriptor");
wmiInParams = wmiMethod.InParameters.SpawnInstance_();
wmiInParams.Descriptor = wmiSecurityDescriptor;
//Выполнение метода ExecMethod_ — установка нового дескриптора безопасности
wmiOutParams = wmiSecSettings.ExecMethod_(wmiMethod.Name,wmiInParams);


Листинг 4. Установка нового владельца файла, код на VBScript

'Файл, дескриптор безопасности которого будем считывать
Dim FileSpec = "C:oot.ini"
'Создание WMI объекта установок безопасности файла
Set wmiSecSettings = GetObject ("winmgmts:Win32_LogicalFileSecuritySetting.path='"
& FileSpec & " ' ")
'Считывание дескриптора безопасности. wmiSecurityDescriptor — возвращаемое значение метода WMI 'GetSecurityDescriptor
FOResult = wmiSecSettings.GetSecurityDescriptor (wmiSecurityDescriptor)
'Создание объекта владельца файла
Set wmiFileOwner = wmiSecurityDescriptor.Owner
'Создание WMI объекта пользователя для учетной записи администртатора
Set wmiUser = GetObject ("winmgmts: Win32_UserAccount.Domain = 'DOMAIN',
Name = 'DomainAdmin'")
'Используя WMI объект пользователя, создаем WMI-объект для его SID
Set wmiSID = GetObject("winmgmts:Win32_SID.SID = '" & wmiUser.SID & " ' ")
'Присвоение нового SID владельцу файла
For i = 0 To wmiSID.SidLength
wmiFileOwner.SID(i) = wmiSID.BinaryRepresentation(i)
Next
'Выполнение метода SetSecurityDescription. wmiSecurityDescriptor — входной параметр метода WMI 'SetSecurityDescriptor
FOResult = wmiSecSettings.SetSecurityDescriptor (wmiSecurityDescriptor)