Vadims 的个人资料Vadims Podans's former b...照片日志列表更多 ![]() | 帮助 |
|
|
2008/10/29 Saruna par PowerShell by default
Citāts PowerShell by default
честно украдено с блога Ричарда Сиддэвэя (Richard Siddaway). Суть простая: во всех последующих версиях Windows (включая Windows Server 2008 R2, Windows 7, etc) консоль PowerShell будет установлена по умолчанию. Исключение составят серверные инсталляции в режиме Server Core, где PowerShell будет доступен в качестве опционального компонента (во всяком случае разработчики активно работают в этом направлении, дабы превратить этот факт в реальность). Это не может не радовать. В этом есть несколько плюсов:
И ещё несколько слов:
Это с одной стороны и приятно и с другой стороны вполне ожидаемо. Всё-таки меньше чем за 2 года Windows PowerShell наверстал упущенные возможности развития командной оболочки Windows. Да, раньше с этим было значимо хуже, зато за этот период (со времени релиза PowerShell) мы имеем то, что в альтернативных ОС разрабатывалось десятилетиями. Но это уже детали, т.к. мы живём здесь и сейчас. Опенсорс зажимают со всех сторон. Но эт уже не проблемы Windows :) 2008/10/26 Преобразование типов доступа в ACL PowerShellСегодня на форуме TechNet задали вопрос о том, как преобразовать числовое значение типа прав доступа к объекту в его текстовое значение (см. тут). В предыдущих постах, посвящённых управленю ACL из PowerShell я использовал этот приём, но не акцентировал на этом внимание. Поэтому я подумал, что пора поставить на этом вопросе жирную точку. Итак, как я не раз писал ранее, для управления списком доступа к объектам (ACL) используются различные классы .NET, например:
Если у нас есть числовое значение права доступа (как у автора топика на форуме), то преобразовать его в текстовый вид очень просто:
где 1179817 - числовое значение, которое описывает тип доступа. В данном случае это число соответствует праву ReadAndExecute и Synchronize. Если ввести другое число, например 721343:
то мы получим текстовое значение прав, а именно - Modify и TakeOwnership. Бывают случаи, когда не допускается указания прав в текстовом виде и требуется указание только в числовом виде. Обратное преобразование выполняется при помощи свойства Value__ : Это единственное свойство, которое хранится в данном объекте. Посмотрим, как это работает на практике: Вот так мы получили числовое значение права FullControl. По этой аналогии можно преобразовать типы доступа и к другим объектам, как реестр: Enjoy! 2008/10/14 Собираем базовые сведения о железе системы с помощью PowerShellРичард Сиддэвей (Richard Siddaway) в своём блоге ведёт на мой взгляд интересный цикл постов Windows 2000 Scripting Guide (W2KSG) с применением PowerShell и WMI. В них рассказываются достаточно интересные и полезные возможности классов WMI для сбора различных сведений как программной части системы, так и аппаратной. Так же недавно на форуме TechNet была поднята (да, на форумах всегда найдутся археологи, которые выкопают темы полу- и годичной давности, а то и ещё старше :) ) тема про скрипт, который бы собрал данные об аппаратной составляющей компьютеров. Подобные темы периодически всплывают на различных форумах. Я подумал, что неплохо было бы решить данный вопрос с помощью PowerShell. Итак, отправной точкой для меня послужила ссылка на Computer System Hardware Classes, где я посмотрел какие классы можно применить. Изучив весь список я отобрал лишь самые необходимые для решения задачи классы, а именно:
Достаточно сходить по ссылкам и можно посмотреть множество свойств каждого класса, которые детально описывают себя. Но при прочтении очень важно следить за поддерживаемыми свойствами в ОС, которые были выпущены до Windows Vista/2008. Я старался эти моменты учитывать, чтобы получить оптимальную совместимость как с предыдущими ОС, так и с текущими. Ничего сверхсложного в этом нету, сперва я определил требуемые классы WMI в переменные и определил набор необходимых свойств каждого класса следующим образом:
После определения всех классов WMI и переменных я начал писать секцию вывода. Например, вывод имени компьютера и ОС, под которой он управляется:
Здесь можно не обращать на знаки регулярных выражений, т.к. они несут только одну функцию, а именно - удобное для воспроиятия форматирование вывода. Т.е. первой строкой пойдёт название поля Computer Name, после чего будет переход на новую строку (`n). Чтобы все строчки не сливались я значения полей отделил табулятором (`t). И в конце так же добавил знак возврата каретки (`n), чтобы отделить между собой поля. Вывод в данном случае будет таким:
Т.е. поля идут с левого края и между собой отделены двумя пустыми строками. А значения полей отделены табулятором относительно того же левого края. Мне кажется, что такой выход весьма читабелен и данные не сливаются в кучу. Когда у поля только одно значение - всё просто. Но когда значений возможно несколько, то задача форматирования выхода таких данных стала для меня небольшой проблемой. Почему небольшой - потому что ответ посмотрел в блоге у Ричарда, а именно в посте W2KSG: Free Disk Space. Суть заключается в очень простом: берётся переменная с массивом данных и по конвейеру при помощи команды Format-Table подготавливается табличный выход и с указанием основного элемента, по которому будет этот выход формироваться. Чтобы лучше понять этот процесс я покажу его на примере вывода сведений о процессоре. Сейчас многопроцессорные системы не редкость и скрипт должен поддерживать показ сведений о более чем одном процессоре: К сожалению, ноутбук у меня однопроцессорный, поэтому эту часть скрипта я запустил на стационаре (в котором, кстати, тоже только 1 физический процессор. Но технология Hyper-Threading эмулирует именно настоящую мультипроцессорность, а не логические ядра, как это сделано в многоядерных процессорах). Итак, давайте разберём строку:
Переменная $CPU содержит сведения о всех установленных процессорах в системе. Я эту переменную передал по конвейеру сразу на команду форматирования - ft (сокращённый алиас от Format-Table) и указал по какому свойству форматировать (DeviceID, который перечисляет ID номера всех процессоров начиная с 0). А дальше я использовал несколько хэш-таблиц для отображения дополнительных свойств каждого объекта. Параметр Label задаёт название новой графе, а Expression указывается значение параметра (как Architecture и Model). Если посмотреть справку по классу Win32_Processor, то можно увидеть, что свойство Architecture содержит лишь числовое значение (от 0 до 9) и в справке приведена расшифровка этих значений. Числовые значения в данном случае, согласитесь, не самый читабельный вариант. Поэтому в Expression я вместил конструкцию Switch, которая автоматически будет числовым значениям сопоставлять понятные текстовые значения. И если свойство Architecture вернёт числовое значение 0, то Switch в нашем случае сопоставит ему более понятное значение x86. И в последней строке я добавил ещё одну хэш-таблицу, которая добавляет ещё одну графу - Model, которая будет содержать свойство Name класса Win32_Processor - название процессора. Такое форматирование выхода я делал для каждого поля, которое может содержать несколько значений. Например, объём каждого установленного модуля памяти:
DeviceID будет содержать номер каждого установленного модуля памяти начиная с нулевого ряда (разбор понятий Ряд и Банк памяти выходит за рамки данного поста) или первого слота. StartingAddress и EndingAddress показывают адресное пространство, за которое отвечает каждый модуль начиная от первого байта. И простым вычитанием начального адреса из конечного мы получим ёмкость каждого модуля:
Так же, как и с процессорами я содержимое переменной $RAM перенаправил по конвейеру на форматирование по столбцу DeviceID. И через запятую добавил ещё одну хэш-таблицу, которая будет показывать объём каждого модуля. Если просто произвести операцию вычитания, то мы получим объём памяти в килобайтах. Чтобы показать объём в мегабайтах я просто разделил полученную разность на килобайты - 1KB (Очень удобная штука :) ). Здесь важно было не ошибиться, т.к. по логике может показаться, что нужно делать на 1MB, чтобы получить размер в мегабайтах. Но, как я уже сказал выше, у нас разность будет уже в килобайтах. Поэтому, чтобы получить в мегабайты, то нам нужно разделить только на 1KB. И посмотрим, что мы будем иметь на выходе: Вот так мы получили номера модулей памяти и объём каждого из них. После деления у нас не получится целое число, поэтому я сконвертировал это число в строку (ToString) и указал количество знаков после запятой - 0 знаков (F00). В данном случае дробное число просто округляется до ближайшего целого числа. Количество знаков после запятой можно изменить, например указав F01, которое округлит число до ближайшего целого числа с точностью до 1 знака после запятой. Это всё не я придумал, а честно взял из поста W2KSG: Free Disk Space :) Вот, в принципе, я рассказал о всех используемых в скрипте приёмах, которые доступны в PowerShell и при дальнейшей разработке скрипта я только выдёргивал нужные свойства каждого класса и формировал более-менее приличный и понятный вывод. Хотя в итоге я добавил ещё 2 вещи:
Таким образом извне можно подавать в функцию имена компьютеров (например, из текстового файла со списком имён компьютеров), что значительно расширяет функциональность скрипта. Ну и финальный скрипт:
Здесь видно, что в конце я снова применил конструкцию Switch, которая расшифровывает числовое значение статуса сетевого адаптера в его текстовое значение. Согласен, что скрипт выглядит не очень опрятно, но в PowerGUI он выглядит вполне сносно. На основе данного скрипта каждый может его с лёгкостью расширить и изменить под свои нужды, я лишь старался показать образец решения задачи, а так же показал некоторые интересные приёмы в PowerShell. Кстати говоря, данный скрипт можно отправить в HTML формат командой ConvertTo-Html. Это будет удобно, когда потребуется собрать подобные сведения с нескольких компьютеров. Тогда HTML формат будет весьма полезен для последующего анализа. На сегодня всё, а теперь спать. 2008/10/2 Foreach, Foreach-Object и оптимизация циклов в PowerShellКоллега, Вася Гусев в посте Непонятные штуки - $_ и % рассказал интересную и полезную тему про циклы. Однако я хочу немного дополнить этот рассказ. Я хочу показать некоторые технические нюансы использования цикла вида Foreach-Object и Foreach на практических примерах. 1) задача: произвести простое последовательное переприсвоение каждого элемента большого массива в другую переменную. Для решения этой задачи мы сгенерируем массив случайных чисел:
И каждый элемент массива $a переприсвоим в переменную $X. А так же измерим время выполнения цикла. Для измерения времени работы цикла я буду использовать команду Measure-Command, которая показывает время исполнения команды с достаточно высокой точностью. Сперва переменную $a перенаправим по конвейеру в цикл Foreach-Object.
Я повторил команду 3 раза и мы получили среднее время обработки цикла равным примерно 4,7 секунд. Теперь повторим ту же операцию, но с использованием Foreach.
Здесь же мы видим, что цикл в среднем выполняет ту же операцию за 0,046 секунды! Или иными словами Foreach почти в 100(!) раз быстрее, чем Foreach-Object. При проведении простой обработке большого массива мы видим заметное преимущество Foreach. Однако, от типа данных и типа их обработки это число (100 раз) может изменяться как в сторону увеличения, так и уменьшения. Для этого обратимся к примеру 3 и 4. 2) Задача: выбрать все записи в журнале System (в моём случае в журнале находится 42855 записей) журнала событий и записать сообщения в текстовый файл. Данную задачу можно решить при помощи цикла Foreach-Object и Foreach. В отличии от предыдущего примера мы сталкиваемся с новым фактором влияния - запись на диск. И снова сперва будем использовать Foreach-Object.
И вот результаты выполнения данного скрипта: Здесь я добавил команду Get-Process, которая покажет нам некоторые моменты, которые в первых двух примерах были неактуальны. Итак, выгрузку тела сообщений журнала System в файл цикл Foreach-Object выполнил за 249 секунд, при этом показав незначительное увеличение потребляемой памяти. К слову говоря, размер файла составил 21,5МБ. Хочу отметить, что цифры потребления памяти очень сильно расходятся с показаниями Task Manager, который во время инициализации консоли показывает 17,5МБ потребления памяти и 22МБ после завершения работы.
Скрипт выполняет точно такую же операцию, только я добавил ещё один временной штамп, когда переменная $events будет заполнена и будет подана в foreach на разбор. И вот, что я получил: Итак, что мы здесь видим? Мы видим, что время считывания журнала System в переменную $Events составило чуть больше 10 секунд. А вот уже обработка тела эвентов (запись каждого тела в файл) заняла 340 секунд! Если сравнивать с примером использования Foreach-Object мы не то, чтобы получили уменьшение времени выполнения, но даже увеличение на 91 секунду или 1,5 минуты и это без учёта 10 секунд чтения журнала в переменную. При этом мы так же наблюдаем внушительный прирост потребления памяти почти в 4 раза! По сведениям Task Manager потребление памяти увеличилось с 17,5МБ до 95МБ (даже в 5 раз). На показательных примерах я продемонстрировал особенности работы цикла Foreach-Object и Foreach при обработке различных типов данных и различных операциях с ними - только простое последовательное переприсвоение и запись достаточно большого объёма данных в файл. Выводы:
Изучив особенности работы каждого цикла мы порою можем значительно оптимизировать работу скрипта с использованием циклов и выбирать тот или иной метод по таким критериям, как скорость работы и объём потребляемой памяти при работе скрипта, который выполняет реальные задачи на реальном сервере. Удачи! © One 2008/9/28 Аудит входов на сервере терминалов с использованием PowerShellОчень часто на форумах спрашивается скрипт или приложение, которое позволит в удобном виде предоставить администратору сведения о логоне пользователей на терминальный сервер. При этом есть задача показать только время входа, имя пользователя и IP адрес клиента, с которого терминально подключаются к сервер. Как известно эту информацию как правило берут из журнала Security журналов событий. Событие успешного терминального логона имеет ID=528. Однако под этим ID регистрируются все входы, как интерактивные с консоли, так и терминальные. Для того, чтобы отличить типы входов (консольные и терминальные) в сообщении эвента используется поле Logon Type. Вот так выглядит типовой текст эвента с ID=528 в Windows XP/Windows Server 2003:
и мы видим строку: Logon Type: 10, где цифра 10 имеет самые частые типы входов:
Поэтому при поиске событий, которые относятся только к успешным входам с использованием Termianl Services (или вход с консоли) мы должны кроме ID события включить дополнительный параметр поиска по типу входа. Можно сделать так:
Но вы получите нулевой результат, поскольку второе условие не выполнится. Спросите почему? А всё потому что между заголовком поля (Logon Type: ) и значением поля (10) нету пробелов, а используется табуляция! Скажу, что я убил несколько часов на то, чтобы понять, почему у меня ничего не ищется по пробелам. Когда понял, что там используется [Tab], то я быстро строку переделал в:
обратите внимание на выделенный участок `t - таким образом в регулярных выражениях обозначается табулятор - [Tab]. Если так задать поиск, то команда найдёт все сообытия успешного входа с использованием терминальных служб. Чтобы с ними начать работать запишем все эти события в переменную:
Прежде чем начинать разбор эвентов я решил создать форму шаблона для предоставления удобного вывода и чтобы отсечь лишнюю инофрмацию. Я решил выбирать дату и время логона, имя пользователя, который залогинился на сервере терминалов и исходящий IP адрес клиента, с которого было произведено подключение:
В постах, посвящённых управлению сетевыми папками из PowerShell я показывал пример, как создаётся объект и обвязывается нами уже нужными для нас свойствами и методами. В данном случае я создал объект $Data, который будет иметь 3 свойства
Итак, теперь пора приступить к разбору всего лога. Первая засада (а их будет несколько) нас ждёт вот в чём: [user name] ((Get-EventLog security | ?{$_.eventid -eq 528 })[0].message).GetType().FullName System.String Этой командой я посмотрел тип свойства Message события в надежде, что это будет массив строк (которые мы увидели в отформатированном виде на первом листинге), но это оказалась одна целая строка с напичканными раздилителями внутри, которые форматируют текст для удобного восприятия. Значит нам придётся самостоятельно поделить строку на массив строк, чтобы мы смогли читать только нужные нам строки. Если поля и значения разделены табулятором, то отдельные секции внутри строки разделены символом возврата каретки, который в регулярном выражении имеет вид - `n. Убедимся, что это правда: [user name] (Get-EventLog security | ?{$_.eventid -eq 528 })[0].message | %{$_ -match "`n"} True Команда вернула нам True, что позволяет нам разделить строку эвента на подстроки. Выше мы уже узнали, что кроме возврата каретки для форматирования используется так же и табулятор. С использованием этих разделителей мы сможем достаточно гибко разбивать строку Message на более мелкие составляющие. Для начала мы разделим эту строку на подстроки, а так же уберём в каждой подстроке разделители, которые расставлены с начала, с конца и посредине каждой строки. Для этого мы воспользуемся тремя методами, которые есть у объекта String:
И получится у меня такая строка:
Я заранее собранные эвенты пропустил через через конвейер. Т.к. время не содержится в теле события (с которым мы и будем только работать), поэтому я его сразу перенаправил в свойство Time переменной $Data, которая будет содержать выходной отчёт. Далее я создал переменную $Message, которая будет хранить подстроки тела эвента. Попутно я его методом Split разделил на подстроки и методами TrimStart/TrimEnd я так же поудалял табуляторы с начала и концов строк. Теперь нам осталось записать имя пользователя и IP адрес. Для этого нас будет интересовать только поле User Name и Source Network Address. Делается это очень просто:
я пропускаю тело эвента через фильтр, который начинается с поля User Name и имеет произвольное продолжение. Но если так и оставить, то мы получим примерно такой выход:
Как вы видите в $UserName попало не только имя пользователя, но и имя поля (на машине с 2003 сервером у меня используется логин user name и вышло вот такое совпадение :) ) Чтобы избавиться от заголовка поля нужно воспользоваться регулярным выражением, которое отделит заголовок поля от значения поля. Как я уже выше говорил, между полем заголовка и значением используется разделитель [Tab], поэтому пропустим строку через регулярное выражение, которое сделает за нас всю работу. Выражение будет таким: $_ -replace "^.+`t *" . Ну и подключим это выражение к нашей строке:
Данное регулярное выражение говорит:
После исполнения этой команды мы получим уже такой выход:
И мы уже в колонке UserName мы будем получать только полезные значения полей, а именно - имена пользователей. Теперь повторим ту же процедуру и для адреса. На первом листинге видно, что IP адрес содержится в строке Source Network Address:
Как видите, мы почти повторили команду, изменению подвергнулась лишь маска поиска нужной строки. Отфильтровав регулярным выражением мы так же отбросили заголовок поля и записали в $Data.Address только полезные данные - IP адрес. Если вы приготовились ещё на пол часа утомительных разборов эвентлога, то можете расслабиться, т.к. поставленная задача решена. Осталось только вывести на экран переменную $Data и закрыть все нужные скобки. А выход у нас дополнился последним полем IP адреса:
В личных целях я стёр последние 2 октета своего IP, но в целом понятно, что там будет адрес. Даватйте теперь соберём всё это в единый скрипт:
И напоследок покажу ещё раз выход данного скрипта: Как видите, парсинг событий эвентлога - занятие далеко не самое приятное. Но тем не менее любую задачу парсера можно разбить на 3 этапа:
В принципе, я считаю, что данный скрипт можно взять за основу и незначительными изменениями скрипта можно значительно расширить или изменить функционал парсера. Может быть на его основе получится сделать парсер аудита доступа к объектам, т.к. чтение логов аудита - одно из самых скучных и утомительных занятий. Если есть что сказать - пишите в коментариях. И в завершении хочу сказать, что данный скрипт не подходит для систем Windows Vista/Windows Server 2008, поскольку по причинам, которые описаны здесь: в новых системах значительно изменили работу с эвентлогами. Но об этом как-нибудь в другой раз. 2008/8/12 param-пам-пам и немного про $argsЗаметил, что при использовании PowerShell у некоторых администраторов появляются разные потребности по интерактивной работе скриптов и с автоматическим приёмом параметров с командной строки. Упрощённый пример: есть скрипт, который создаёт папку на диске. При этом скрипт должен принимать в качестве аргумента командной строки, так и user interface. В принципе, это реализовать несложно. А вот если ещё захотеть сделать так, что при отсутствии аргументов с использованием user interface было принято некое значение по умолчанию, то тут могут возникнуть маленькие трудности. Давайте рассмотрим несколько вариантов. Итак, тело простого скрипта, который принимает параметры только с командной строки. Как изветно, для передачи аргументов из командной строки в скрипт используется команда param:
если этот скрипт сохранить и запустить из командной строки, то формат запуска будет такой:
данный скрипт будет работать только при запуске с командной строки и обязательной передачей аргументов. Следует запомнить, что param должен быть первой рабочей строкой в скрипте, кроме комментариев. Если при отсутствии аргументов хотим использовать заранее предопределённое значение (default), то нужно добавить проверку переданного параметра:
здесь я во второй строке проверяю переменную $path. Обратите внимание, что я проверяю её отрицание (выделил восклицательный знак). Данная строка говорит, что если переменная $path равна $null (т.е. ничему), то переменной $path присваивается дефолтное значение C:\Temp. Здесь вопросов как бы не возникает. Теперь посмотрим, как сделать интерактивное окно, с использованием командлета Read-Host.
При запуске этих строк появится приглашение ввести путь. Всё очень просто. Теперь сделаем предустановленное значение:
Здесь я первой строкой сделал значение по умолчанию, второй строкой приглашение ввести путь и третьей строкой отображаю содержимое переменной. Вот выход:
Тут хорошо видно, что переменная $path принимает всё, что мы вводим. Однако, как мы видим в последнем случае, даже если просто нажать Enter, то заранее определённая переменная $path будет перезаписана командой Read-Host, а именно будет присвоено значение $null. Для того, чтобы определить, содержит ли переменная $path какое-то значение (т.е. не был нажат пустой Enter) нужно в коде делать проверку переменной:
для этой проверки пришлось вводить новую переменную. А теперь посмотрим, как это всё можно скомпоновать под задачу: передача аргументов из командной строки и через интерфейс пользователя. Если параметры не переданы, то в качестве аргумента принять значение заранее определённой переменной. В любом случае первой строкой будет команда param, которая в любом случае перезапишет объявленную переменную во второй строке. То же самое касается и команды Read-Host. Однако с Read-Host дела обстоят немного проще, чем с param, по причине, что Read-Host не обязан быть первой строкой в скрипте. Задачу можно решить примерно таким образом:
Вот здесь пришлось задействовать 3 переменные и заняло 5 строчек. Не очень-то и удобно. Но в PowerShell есть есть одна очень приятная во всех отношениях специальная переменная, а именно: $args. Данная переменная всегда хранит переданные аргументы с командной строки (неважно, приняты они через param или нет. Правда есть некоторые нюансы, о которых я расскажу чуть позже). Сейчас покажу как можно эффективно использовать эту переменную:
Я выкинул из первой строки param и переменную $ArgPath. Во второй строке я проверяю содержимое переменной $args, т.е. скрипт запущен с аргументами (несопровождаемый режим) или без аргументов (интерактивный режим). Если интерактивный режим, где пользователю предлагается ввести запрашиваемое значение, то делается последняя проверка - ввёл пользователь что-то или просто нажал Enter. В любом случае оба действия будут весьма корректно и ожидаемо обрабатываться. Ну и всё уместилось в 3 строчки плюс строчка создания папки :)
Переменная $args может содержать не один, а несколько переданных аргументов: Как здесь мы можем наблюдать, переменная $args принимает аргументы не только для скрипта в целом с использованием param, но и для функций тоже. Ну и в завершении хочу сказать несколько слов про $args. Если в скрипте используется param для приёма аргументов из командной строки, или фнукция с заданными принимаемыми аргументами, то в переменную $args будут помещены только те аргументы, которые не определены в списке принимаемых аргументов: при создании функции я указал, что для вызова функции потребуется передать 3 аргумента. Когда я передал ровно 3 аргумента, то $args мне ничего не вернула. Когда же я при вызове передал больше аргументов, чем опредено в функции, то лишки помещаются в переменную $args. Если же не используется param или в функции не определены аргументы, то $args будет содержать все аргументы, которые были переданы с вызовом функций. Ну и как вы заметили $args является полноценным массивом и с ней можно проделывать практически все те же операции, что и с обычными массивами. Ну вот, на сегодня и всё :) 2008/8/8 Странности Get-EventlogНи для кого не секрет, что PowerShell умеет очень замечательно работать с системным журналом событий. И, так же, ни для кого не секрет, что для этого используется командлет Get-Eventlog. Если в WindowsXP/Windows Server 2003 он работает замечательно, то в Windows Vista результаты несколько странноватые. Давайте посмотрим, что у нас есть. Для примера попробуем выбрать аудит неудачных входов в систему. В WindowsXP и Windows Server 2003 Failed user logon имеет код события EventID = 529, а в Windows Vista - 4625. Пробуем выбрать эвенты из Windows Server 2003:
Вот у меня тут есть 3 события. Давайте посмотрим текст первого эвента:
Ну вот, вроде как всё понятно тут. А теперь попробуем вытащить ту же самую информацию из Windows Vista:
Смотрим текстовое сообщение первого эвента:
Как видно из первой части - мы не можем найти описание эвента. Далее мы получаем информацию в совершенно нечитабельном виде. В GUI это выглядит вот так:
Всё красиво и понятно. Но это ещё пол беды. На многих эвентах вообще ничего прочитать через PowerShell нельзя:
2 и 3-й эвенты тоже предсказывают стать нечитабельными. Например 3-й эвент с кодом 1:
В консоли MMC по факту всё красиво и гламурно:
1, 4 и 5-й эвенты уже отсюда видно, что они читабельны:
Вот теперь всё очень даже понятно и читабельно. Подведя резюме к экспресс тесту командлета Get-Eventlog можно констатировать, что по сути мы не имеем возможности парсить логи журналов событий в Windows Vista и Windows Server 2008 при использовании как минимум Windows PowerShell 1.0 RTM, что есть не очень хорошо. Кстати говоря, для примеров использовался Windows PowerShell 1.0 RTM без установленных расширений под управлением Windows Server 2003 Enterprise Edition SP2 R2 и Windows Vista Business SP1 (обе x86). 2008/7/18 Полезная безделушка Hash SHA1 на PowerShellВ сети есть уйма маленьких консольных и графических утилит, которые позволяют считать хэши MD5/SHA1/SHA256/etc для файлов. Это иногда очень полезно. Расслабляясь после ряда статей по управлению Share Permissions решил от нечего делать написать свою утилитку полностью на PowerShell, которая интегрируется с контекстным меню Windows Explorer. Ну и самописный инсталлятор на PowerShell, который всю эту интеграцию и выполнит. Конечно же, это будет выглядеть продуктом, который выполнен на коленке в поезде, но тем не менее, это неплохой пример для начала работы с GUI в PowerShell. Ну что-ж, давайте разбираться. У нас в Windows Explorer при правом клике на файле (только файле) будет контекстное меню вот такого вида:
Делается это очень просто, мы просто создадим ветку в реестре и зададим ей значение следующего вида: Ключ реестра: Данная команда запускает PowerShell без логотипа и без профиля. Ключ -command показывает какие команды следует исполнить. В нашем случае это будет вызов скрипта hash.ps1 с аргументом в виде переменной %1 . Данная переменная будет содержать путь к файлу и использоваться как аргумент для скрипта. Как я уже рассказывал раньше про передачу аргументов из командной строки в скрипт, то при использовании пробелов в аргументах (а путь к файлу очень даже может содержать пробелы), то его нужно заключить в одинарные кавычки. Теперь давайте вспомним, как создавать путь в реестре и присваивать им занчения из PowerShell:
В конце строки я использовал ключ -Force, чтобы путь создался полностью, даже если реальный путь обрывается посередине. Без этого ключа PowerShell вернёт ошибку, что ключ реестра не существует и не сможет создать последующие ключи. Теперь пора внести запись в реестр. Выше я писал ключ реестра, который находится в ветке HKEY_CLASSES_ROOT, а создаю ключ в ветке HKLM (HKEY_LOCAL_MACHINE). Всё очень просто. Тут нужно понимать структуру реестра в Windows NT системах. На самом деле на диске физически хранятся лишь HKLM и множество кустов HKCU (по одному кусту на каждого пользователя), которые в итоге составляют один общий раздел HKU. Остальные же ветки реестра, которые мы видим в классическом реестре являются лишь ссылками на соответствующие ключи этих двух основных файлов кустов реестра. Для удобочитаемости кода я длинные значения присвоил переменным и в основном коде использую уже переменные:
После выполнения этих команд мы получим в контекстном меню элемент Hash SHA1. А теперь уже можно начинать писать основной код, который будет считать хэш файла. Мне из хэш-методов больше нравится SHA1 (кому-то может и MD5 или ещё что), поэтому писать буду применительно под него (что характерно от используемого хэш-метода код изменится только в названии класса и всё). Для SHA1 в .NET есть класс System.Security.Cryptography.SHA1. Давайте создадим новый инстанс с этим классом. Если посмотреть список членов класса SHA1, то найдём там Create. В принципе, этого хватает вполне:
Хорошо, инстанс создали, что дальше? Дальше нужно подобрать метод для данного класса. Из списка методов нам для решения поставленной задачи пригодится метод ComputeHash(Stream). Stream в данном случае означает, что будет создаваться поток байтов и пропускаться через метод ComputeHash для вычисления хэша. Значит, нам нужно создать поток байтов. В описании метода ComputeHash в описании параметров указано как создавать поток байтов, а именно с использованием System.IO.Stream. Давайте создадим объект с этим классом и в качестве параметра ему передадим путь к файлу. Путь файла сожержится в переменной $file:
теперь файл будет преобразован в виде потока байтов. Ну что, теперь можно этот поток байтов можно передать в метод ComputeHash класса System.Security.Cryptography.SHA1. Сказано - сделано:
Теперь завершаем поток байтов:
В принципе, если вместо переменной $file подставить путь к файлу (пока без вызова из контекстного меню, а просто из GUI, то мы увидим результат работы скрипта:
На выходе мы получили массив посчитанных байтов. Чтобы собрать этот массив в строку мы воспользуемся классом System.Text.StringBuilder. Создадим объект с этим классом:
Теперь посмотрим, какие методы у него есть - StringBuilder Members. Здесь нас заинтересует метод Append Method (Byte). Т.к. у нас массив байтов, то нужно данный метод использовать в цикле foreach-object и необходимо его привести в HEX формат:
В первой строке мы только сложили массив байтов в строку. Теперь нужно весь этот результат куда-то разместить. Для этого второй строкой мы создаём собственный абстрактный объект и добавляем ему свойство HashValue (можно и на своё усмотрение название придумать) и присваиваем ему значение. Давайте посмотрим, что после исполнения кода мы будем иметь в переменной $output:
Жирным я выделил содержимое переменной, а именно - итоговый хэш-код файла notepad.exe по системе SHA1! Отлично, сам конструктив уже готов (код, который для файлов считает хэш). Теперь немного GUI. Объявляем часть GUI:
В принципе, эта команда используется всегда перед использованием GUI элементов. Давайте создадим простенькую небольшую форму и зададим ей какие-нибудь свойства:
Первой строкой мы создали объект Windows.Forms.Form а в последующих строках заполнил свойства этого объекта. Касательно размеров я не действовал наугад, а создал примерное окно в Visual Studio с нужными размерами. И уже из Visual Studio выбирал нужные значения для свойств объекта. Напрямую из PowerShell не очень удобно рассчитывать размеры и компоновать элементы внутри формы. Я считаю, что тут комментарии излишни по коду, т.к. значения свойств понятны из самих названий. Например, свойство Text для формы определяет заголовок формы. У меня в заголовке динамически подставляется путь из переменной $file и добавил текст SHA1 Hash. На форме у меня будет ещё два элемента - Метка (Label) и кнопка (Button). Начнём с метки:
местоположение метки относительно верхнего левого угла формы я взял из Visual Studio. В свойство Text я вписал содержимое переменной $output.hashvalue, которая динамически для каждого файла будет содержать SHA1 хэш. От созерцания хэша легче мне не станет, поэтому его нужно как-то изъять из метки простым способом (метка не поддерживает выделение и копирование текста. Даже при использовании элемента EditBox пришлось бы выделять текст и вручную копировать, что не сильно удобно). Для этого я на форме разместил кнопку, которая при нажатии будет копировать содержимое метки в буфер обмена и закрывать форму:
Примечение: вот тут мне пришлось отступиться от чистого кода на .NET, т.к. PowerShell не умеет нативно использовать буфер обмена и для этого нужно писать собственную функцию. В принципе, она уже есть в PowerShell Community Extensions и позволяет как записывать в буфер обмена, так и извлекать оттуда данные. У меня же нету задачи по извлечению данных из буфера обмена, поэтому я схитрил и использовал системную утилиту clip.exe, которая без расходования лишних строк позволяет скопировать данные в буфер обмена. Вот и всё, больше ничего на моей форме не будет, поэтому можно активировать форму и объявлять в ней мои элементы:
Если создать нужные ключи реестра и сохранить этот код в файл, то при клике на контекстное меню Hash SHA1 появится вот такое окошко:
Конечно же тут есть один недостаток - на заднем плане нашей формы будет маячить чёрная консоль PowerShell. К сожалению я пока не придумал, как сделать по-тихому, чтобы на экране была бы только форма без окна консоли. Если придумаю, то обязательно напишу или, если у вас есть уже готовые наработки или идеи, оставьте их в комментариях. Теперь настало время подвести итог и написать конечный PS1 файл:
В конечном итоге я обвязал код некоторыми проверками, которые проверяют возможность записи в реестр (для работы скрипта нужно иметь право записи в HKLM) и в указанную пользователем папку. Если какое-либо действие не удастся, то код выдаст соответствующее сообщение об ошибке и прервёт дальнейшую операцию. Если обе операции записи прошли успешно, то происходит копирование рабочего кода (содержимое переменной $exefile), который уже не содержит инсталляционных и проверочных строк кода. Вот как бы и всё на сегодня :) . 2008/7/17 PowerShell - убийца WMI классов?В процессе изучения вопросов управления Share Permissions для сетевых папок я угробил 4 виртуальные машины (я ленивый и бэкап их не делал). Точнее ряд классов WMI, которые относились в той или иной степени к сетевым папкам. Я по разному тестировал код и в процессе оптимизации команд иногда забывал дописывать до конца команды. Я долго не мог понять в причину проблемы. И пока укладывал одну за одной виртуальные машины я обнаружил закономерность, что у меня отваливались классы WMI в процессе работы функции по удалению сетевых папок. После написания финального скрипта по управлению безопасностью сетевых папок я решил заняться исследованием проблемы и обнаружил крайне досадную вещь, а именно - неполное написание команды для получения WMI-класса и использование метода Delete для данного класса приводит к удалению WMI. Заранее предупреждаю: Не выполняйте на рабочих серверах и компьютерах нижеприведённые команды, а только в виртуальных машинах (при желании)!Вот как всё было и что мы получили в итоге:
В первой строке я не указал действие Get-WmiObject для класса Win32_Share, чтобы получить объект со списком сетевых папок на компьютере, а просто вызвал данный класс. В результате этого упущения в переменную $shares попал сам класс Win32_Share, как это видно в результате исполнения второй строки. Следующей командой я использовал метод Delete для класса Win32_Share. В ответ PowerShell мне промолчал. Ну что ж, давайте попробуем исправить эту ситуацию и вызовем класс Win32_Share, но уже с использованием команды Get-WmiObject:
Опаньки! И что это было? А это, оказывается, мы удалили (или ещё что сделали с ним) класс Win32_Share. Подумал немного, ну ладно, потеряли один класс, велика ли беда? А один ли? Теперь пробуем извлечь другой класс, скажем Win32_LogicalShareSecuritySetting, который я использовал для извлечения параметров безопасности из Security Descriptor:
Ну совсем прекрасно. И его мы потеряли тоже. Остальные классы вроде выглядят как рабочие (хотя, далеко не факт), но тем не менее проблема такая существует и она может доставить вам много неприятностей. Поэтому при работе с классами WMI в PowerShell нужно быть осторожными. 2008/7/16 Управление безопасностью общих папок (сетевых шар) в PowerShell (часть 4, заключительная)Ну что ж, настало время подвести итоги по материалу управления сетевыми шарами и управлению доступа к ним. Как и обещал, я написал скрипт, который выполнен в виде функций при помощи которого можно действительно легко и просто управлять самими сетевыми папками и их безопасностью из PowerShell. Интеграция данного скрипта в профиль PowerShell позволяет использовать уже готовые функции как простые командлеты. Я считаю, это действительно большим плюсом, т.к. до этого администратору для решения этих задач приходилось писать длиннющий код в каждом скрипте, который касался безопасности сетевых шар. Итак, какой же функционал заложен в данный скрипт:
Касательно последнего пункта, то хочу отметить, что импорт при отсутствии наличия папки для расшаривания создаст папку и расшрарит с данными из CSV файла. Сначала я приведу список команд, которые доступны при использовании скрипта и их синтаксис:
Ну и собственно сам скрипт с некоторыми комментариями по работе самих функций. Для желающих подробно изучить скрипт я не делал особо комментариев, т.к. построение кода описано в предыдущих частях по управлению безопасностью сетевых папок. Ссылки на предыдущие части:
Вот так это всё выглядит. На первый взгляд много и страшно, но если прочитать все предыдушие статьи по данной теме, то данный код уже будет обретать некий смысл. На этом я предлагаю поставить жирную точку в вопросе управления сетевыми папками и безопасностью (Share Permissions) сетевых папок в PowerShell. И на последок добавлю, что принимаются любые замечания, поправки предложения по данному скрипту, т.к. это лишь мой первый пробный многофункциональный скрипт по ACL и что-то может быть упущено или неоптимизировано. Вобщем, если есть что сказать, то отписывайтесь в комментариях. 2008/7/8 Управление безопасностью общих папок (сетевых шар) в PowerShell (часть 3)В предыдущей части мы рассмотрели чтение Share Permissions, их редактирование и удаление ACE из полного списка ACL. Здесь осталось рассмотреть вопрос добавления участников безопасности в DACL сетевой шары. Этот процесс, к сожалению, не такой и простой, как может показаться, но тем не менее его тоже нужно решать. Для решения этой задачи нам нужно создать такой же объект с такими же свойствами как и содержимое $ShareInfo. Давайте посмотрим, какими свойствами обладают элементы массива $ShareInfo:
Нас тут будут интересовать только NoteProperty. Давайте теперь исходя из этих данных создадим свой объект, который будет обладать вот этими свойствами. Тип объекта будет такой же - System.Management.Automation.PSObject (без Custom). Новый объект создаётся командной New-Object, а члены объекта создаются командой Add-Member:
Теперь можно посмотреть на результаты нашей работы:
Ну что ж, уже лучше, теперь можно заполнять эти поля в соответствии с нашими требованиями. Но чтобы не заполнять все поля вручную, предполагается, что для добавления нового участника безопасности в Share Permissions пользователь укажет только имя сетевой шары, имя пользователя, маску доступа и тип доступа (Allow/Deny). Поэтому нам потребуется вытащить информацию о текущей шаре (которая общая для всей шары, как имя, путь, описание и т.д.) и передать эти значения в новую переменную $AddInfo. Остальную часть информации мы обработаем на основании уже переданных параметров и запишем оставшиеся поля переменной $AddInfo, которая в конечном итоге будет содержать всю необходимую информацию. Т.к. уже неоднократно говорилось в предыдущих частях, что метод SetShareInfo перезаписывает полностью информацию о сетевой шаре, поэтому для сохранения существующих ACE мы произведём уже известным способом чтение существующих DACL и сделаем инкремент (добавим к существующим DACL нами созданный DACL). Когда все данные будут скомпонованы мы произведём запись обновлённого списка DACL в ACL шары. Итак, поехали:
Формат запуска данного скрипта из командной строки CMD или меню Run будет следующим:
эту команду следует выполнять в одну строчку. В качестве переменной %path% нужно указать путь к папке со скриптом, если он заранее не добавлен в системную переменную %path%. В качестве маски доступа нужно указать одно из 3-х значений, которое может быть Full, Change или Read. Более одного параметра указывать нельзя. Ну и в качестве типа доступа указать либо Allow, что даст доступ, либо Deny, что явно запретит доступ. Ну вот как бы и всё на данном этапе. Здесь я много чего не пояснял, т.к. достаточно (в моём понимании) разобрал в предыдущих 3-х частях о работе с сетевыми шарами в PowerShell. Но это ещё не всё. Главный девиз PowerShell - быть удобным для использования и кратким для написания (это я сам придумал :) ), однако при работе с сетевыми шарами это совершенно не прослеживается и даже может создаться впечатление громоздкости (хотя тот, кто считает этот код громоздким может написать скрипт короче на VBS с использованием только WMI/.NET :) ), поэтому в следующей части я постараюсь исправить сей момент путём написания единого (относительно компактного по возможности) и представить его как готовое решение, которое в работе будет действительно удобным. Не отключайтесь, продолжение обязательно будет :) p.s. Данный скрипт не обязательно является самым простым решением, т.к. возможно, что данную операцию можно провести более удобным и изящным способом (хотя, после чтения документации на MSDN мне так не кажется, что это возможно), но в любом случае адекватные замечания/поправки/дополнения к этому скрипту всячески приветствуются. 2008/7/7 Управление безопасностью общих папок (сетевых шар) в PowerShell (часть 2)В первой части мы рассмотрели использование PowerShell для добавления DACL (Discretionary Access Control List) к сетевой шаре как для создания списка ACL и получения возможности в дальнейшем работать с этим списком из PowerShell. Данный метод полностью заменяет текущий список DACL сетевой папки, т.е. все текущие участники безопасности будут удалены и заменены теми, которые были указаны в теле скрипта. Теперь стоит поговорить об извлечении и чтении DACL для сетевых папок как для анализа, так и для выборочного добавления/удаления участников безопасности в списке ACL. Класс Win32_Share имеет лишь один метод для добавления ACL списка - SetShareInfo, который полностью заменяет текущий список DACL и не имеет нативного метода добавления частичного DACL к имеющемуся списку DACL. Поэтому для добавления или удаления участников безопасности из этого списка нам потребуется сначала считать этот список, проанализировать, внести необходимые изменения и записать этот список с внесёнными изменениями в ACL сетевой папки. Процесс извлечения DACL из ACL сетевой шары будет происходить примерно в обратном порядке записи. В предыдущей части я говорил про процесс упаковывания ("инкапсуляции") массива в массив, и сейчас нам придётся обратно расшивать (извлекать) массивы из массивов. Как уже говорилось в первой части, для чтения DACL сетевой папки используется класс Win32_LogicalShareSecuritySettings:
Так мы получим все ShareSecuritySettnigs для всех расшаренных папок. Давайте отфильтруем только ту, конкретно с которой будем работать и выберем её в переменную:
Если теперь теперь посмотреть содержимое переменной, то ничего интересного там мы не увидим:
Давайте покопаем поглубже. В процессе создания DACL для шары мы записывали уже готовый SecurityDescriptor, в котором хранится DACL и внутри которого уже хранятся ACE и Trustee - вот эти объекты нам и нужны. Для извлечения этого класс Win32_LogicalShareSecuritySettings имеет метод GetSecurityDescriptor
И мы увидим две строчки (я специально отфильтровал вывод ненужной для нас в данный момент информации, которая начинается с символа подчёркивания "_"):
Вот здесь нас будет интересовать параметр Descriptor:
Вот мы уже добрались до DACL:
Вот здесь мы видим 3 ACE с разными AccessMask. Но имён почему-то не видно..а не видно, потому что имена участников безопасности запрятаны ещё глубже, а именно в Trustee (вы помните, как мы записывали имя участника безопасности в класс Trustee). Давайте посмотрим первый элемент:
И команда нам вернёт имя пользователя. У меня это Everyone. AccessMask = 1179817 означает Read&Execute + Synchronize. Узнать это очень легко, достаточно набрать в консоли: [C:\] [System.Security.AccessControl.FileSystemRights]1179817 Теперь посмотрим имя второго участника безопасности, который имеет маску доступа 1245631 (Change):
И т.д. можно перебирать все элементы массива. Давайте для начала выведем весь этот список в CSV. Здесь я не буду изобретать велосипед, а уже буду использовать готовое решение от /\/\o\/\/:
На выходе мы получим CSV файл, где в простеньком (но вполне удобочитаемом) формате есть вся информация о имеющихся сетевых шарах на локальном компьютере. К слову говоря, этот CSV файл потом можно будет импортировать обратно для восстановления Share Permissions и другой информации о шарах. Давайте посмотрим на содержимое переменной $ShareInfo:
Остальное я не стал показывать, т.к. будет содержать идентичную информацию для остальных ресурсов. Обладая этой информацией мы можем гибко изменять наши настройки, например добавлять или удалять выборочно единичных участников безопасности. Давайте начнём с удаления. К примеру, удалим из ACL шары UserShare группу Everyone. Не уверен, что этот метод является самым правильным, но мы сделаем переприсвоение массива с фильтрацией по имени шары и имени пользователя, которого будем удалять:
Теперь можем посмотреть содержимое переменной $ShareInfo:
Как мы видим, переменная $ShareInfo уже не содержит ACE группы Everyone. Таким образом можно фильтровать нужные элементы массива по любым критериям, как группа пользователей, имя сетевой шары, тип доступа и т.д. Теперь можно импортировать эту переменную в нашу шару. Для импорта этой информации воспользуемся тем же методом, каким мы создавали Share Permissions с нуля. Разница будет лишь в том, что для создания нужных объектов (как User, SID, AccessMask) мы будем использовать значения из переменной $ShareInfo:
Если посмотреть на скрипт из предыдущей статьи, то можно проследить практически идентичную процедуру записи новых SharePermissions. Вот так мы получили готовый шаблон для изменения SharePermissions, который включает 3 этапа:
Кстати говоря, для изменения одного конкретного ACE (без удаления) можно использовать следующий метод. Он заключается в определении местоположения необходимого элмента в массиве и редактировании конкретно этого элемента. Делается это следующим образом, пишем функцию (за функцию отдельное спасибо Васе Гусеву):
Теперь определяем порядковый номер элемента в массиве, который содержит группу Domain Users и вместо Change дадим этой группе право только Read:
Вот теперь Группа Domain Users имеет маску доступа не Change, а Read. Теперь осталось только записать изменённые данные в ACL шары по вышеприведённому примеру, где мы удаляли группу Everyone из ACL. В этой части мы рассмотрели чтение информации о сетевых шарах, в частности чтение Share Permissions, экспорт этих данных в CSV файл, а так же рассмотрели вопросы удаления единичных ACE из списка ACL и их редактирования. В следующей части я расскажу, как добавлять участников безопасности к существующему списку ACL и в качестве итогового резюме мы приведём в порядок весь материал, который изучили, чтобы его можно было удобно использовать в производственной среде. Так что продолжение следует. 2008/7/4 Управление безопасностью общих папок (сетевых шар) в PowerShell (часть 1)В предыдущей статье я сделал вводну часть по управлению сетевыми папками в PowerShell. В ней я не рассматривал вопрос управления Share Permissions, т.к. это дело весьма непростое. Итак, что нам для этого потребуется? А потребуются нам следующие классы WMI:
Основной класс, который позволит нам извлечь параметры безопасности - Win32_LogicalShareSecuritySettings. Как я говорил в предыдущей статье, упрощённый вариант создания новой сетевой папки - не самый лучшй вариант создания для последующего управления. Сейчас я продемонстрирую вам проблему:
Итак, первой строчкой мы создали новую шару. В выводе команды я выделил последнюю строчку ReturnValue и его значение 0. Ноль нам говорит, что шара создалась успешно. Чтобы в этом убедиться в следующей строчке я ввёл команду, которая показывает список всех расшаренных папок на локальной машине. Я так же выделил жирным строчку, которая показывает нашу шару. Третей командой я вывел параметры безопасности всех доступных сетевых ресурсов на локальной машине. И что мы видим? Точнее, чего мы не видим - а не видим мы административных шар (которые заканчиваются на знак $) и, так же, не увидели созданной нами сетевой папки UserShare. И это означает, что мы в последующем через классы WMI не сможем извлекать параметры безопасности для последующей обработки. Поэтому более правильным будет создание новой сетевой папки с явным назначением Share Permissions для неё. Если посмотреть в описание метода Create класса Win32_Share, то там видно, что в качестве последнего параметра выступает класс Win32_SecurityDescriptor. Посмотрим, что нужно для него. Я для него нужны классы Win32_Ace и Win32_Trustee. Ну что ж, приступим к разбору. Итак, у нас есть уже шара с дефолтными правами. Чтобы создать новый набор прав нам нужно знать следующее:
Теперь нужно объявляем переменные для них:
Теперь объявляем вышеуказанные классы WMI:
Теперь преобразовываем имя пользователя/группы в его SID. Преобразование я описывал вот в этой статье:
В переменной $SID теперь хранится SID данного пользователя. Правда, здесь он содержится в строковом формате. Но если мы посмотрим в свойства класса Win32_Trustee, то увидим, что тип данных для свойства SID должен быть: Data type: uint8 array. Получить такой массив можно следующей конструкцией:
Здесь мы объявили переменную $SIDArray с длиной равной BinaryLength. Длину BinaryLength можно узнать выполнив преобразование имени пользователя в SID, как это рассказано в ссылке приведённой выше. Вот теперь у нас переменная $SID содержит уже байтовый массив из SID пользователя/группы, который получили из $SIDArray. С этим мы закончили. Теперь эти данные можно передать в класс Win32_Trustee, который является абстрактным классом для описания пользователя или группы. Он нам потребуется для дальнейшей передачи этих данных в класс Win32_Ace.
Сам класс Win32_Trustee уже объявлен, поэтому мы просто добавляем значения свойств для этого класса. Примечание: На первый взгляд это может показаться очень сложным материалом (мне он тоже дался не очень легко). По сути здесь присутствует множественная инкапсуляция данных и преобразование этих данных из одного типа в другой. Т.е. из исходных данных у нас есть разве что имя пользователя/группы и тип устанавливаемых прав. Если внимательно изучить ссылки, которые приведены в самом начале, то можно увидеть следующую последовательность преобразований:
Вот так выглядит общая схема последовательности действий, которая является шаблоном для данной операции. А теперь продолжим. Теперь нам нужно преобразовать права доступа в класс FileSystemRights. Преобразование типа прав так же было ранее мною описано в первой части Управление ACL в PowerShell. Пара слов о том, чем отличаются права Read, Change и Full Control в контексте разрешений сетевого ресурса от контекста разрешений NTFS. Этим правам в NTFS сопоставляются Read + Execute, Modify и Full Control соответственно и плюс право Synchonize. Поэтому давайте запишем маску доступа в переменную $ace:
Ещё в начале мы объявили переменную $mode и вписали текстовое значение типа доступа. Здесь я его использовать не буду, а лишь преобразую его сразу в аналог FileSystemRights. Теперь в этот класс нужно передать тип доступа (Allow/Deny) в класс Win32_Ace. первой части Управление ACL в PowerShell я показывал команду, которая позволяет перечислять доступные значения свойств класса. Применим его снова, но в контексте AceType
И в этом списке нас будут интересовать только первые 2
Их можно указывать по порядковым номерам. Например, 0 будет означать AccessAllowed, а 1 - AccessDenied. Теперь заканчиваем второй этап и делаем третий этап нашей "инкапсуляции":
Далее, следуя нашему шаблонному алгоритму нужно сформированную переменную $ace передать в класс Win32_SecurityDescriptor. Здесь нам нужно будет заполнить только свойство DACL, которое будет содержать массив параметров безопасности из переменной $ace:
Ну что ж, вот теперь у нас полностью готов объект SecurityDescriptor для применения его в качестве параметра для метода SetShareInfo. Для начала нам нужно указать шару, для которой будет применяться данный метод:
Теперь нужно получить шаблон набора свойств для метода SetShareInfo:
Так мы получили форму с необходимыми свойствами и типами данных, которые мы должны заполнить. Метод SetShareInfo использует следующий синтаксис (приведено из ссылки MSDN):
Это нам говорит, что требуется именно параметр Access, который нужно заполнить данными из класса Win32_SeceurityDescriptor. У нас уже есть переменная $SD, в которой заполнен параметр DACL. Вот теперь эту всю переменную запишем в качестве параметра Access в форму набора свойств:
Ну вот и всё. Теперь нам только осталось записать всё это в нашу сетевую папку:
Здесь я указал шару и использовал PsBase.InvokeMethod, чтобы вызвать метод SetShareInfo и после указания метода перечислил передаваемые прараметры Ну и конечный результат всех наших терзаний в едином целом:
Ну и, собственно, вторая цель, которой мы добивались:
Вот теперь мы увидели нашу шару через Win32_LogicalShareSecuritySetting и благодаря чему в последующем можно будет работать с DACL списком этого сетевого ресурса. Как говорит /\/\o\/\/ - Enjoy! :) Кстати, хотелось бы отметить, что по этому вопросу /\/\o\/\/ в своё время проделал очень большую работу, как часть вопроса управления безопасностью объектов в Windows и значительная часть материала была написана с использованием его наработок. Давайте, теперь поговорим о том, как добавить несколько участников безопасности. Суть процесса не изменится. Для решения данной задачи я воспользуюсь следующими приёмами:
Если посмотреть код выше, то для добавления других пользователей/групп в Share Permissions, то нетрудно предположить, что нам нужно записать в $SD.DACL столько ACE, сколько пользователей/групп нам требуется завести. Следовательно, тут нужен цикл. Имена пользователей/групп и назначемые им права будут выбираться из hashtables. Hashtables - по сути является массивом сопоставлений. Он представляется двумя столбцами - именем параметра в первом столбце и его значением во втором. В нашем случае в качестве параметра будет выступать пользователь/группа, а его значение будет - тип предоставляемого доступа. Hashtables, на мой вгляд, хорошо описаны в книге PowerShell in Action (сегодня постараюсь не забыть прикрепить к блогу список полезной литертуры по PowerShell). Т.к. набор прав у нас будет для каждого пользователя/группы разный, поэтому я применю конструкцию Switch, которая будет сопоставлять текстовому значению типа доступа его значению в классе FileSystemRights. Итак, давайте проведём анализ, какую часть кода нам нужно поместить в цикл для многократной обработки и получения для пользователя собственного ACE. У нас индивидуальным для каждого пользователя будет SID, Trustee, ACE. Остальное же будет универсальным (общим) для всех пользователей. Вот его и поместим в цикл foreach. Но для foreach нужно передать параметры - имя пользователя/группы и маску доступа (тип предоставляемого доступа). Для этого, как я уже говорил, нам потребуется Hashtables. Общий формат хэш-таблиц выглидт следующим образом:
Поэтому изменив предыдущий код мы заменим переменные $user и $mode на одну переменную $user с указанием имён пользователей и маски предоставляемого доступа согласно формату хэш-таблиц:
Если после этой строки в консоли набрать $user, то мы получим нашу хэш-таблицу:
Теперь готовим цикл foreach:
Мы передали содержимое хэш-таблицы в цикл. Теперь нужно изменить несколько строк кода. А именно: мы передадим в цикл сперва имя пользователя/группы. Для этого нужно изменить строчку преобразования аккаунта, чтобы туда при каждой итерации цикла попадало новое имя пользователя:
В комбинацию "$_" будут попадать только значения столбца Name из нашей хэш-таблицы (если указать, как и раньше $user, то туда попадёт вся хэш-таблица и скрипт даст ошибку преобразования аккаунта). Далее, у нас будет передаваться индивидуальная маска доступа. В предыдущем примере мы использовали всего лишь одну строчку, которая начиналась с $ace.AccessMask. В том примере было всё просто - один пользователь = одна маска. Теперь пользователей/групп уже 3 и маски тоже 3. Здесь мы воспользуемся конструкцией Switch, которая будет принимать наше текстовое значение искать ему сопоставление внутри конструкции и выполнять только одно действие:
Я выделил $($user[$_]), для того, чтобы показать, как выбирать только значения из столбца Value нашей хэш-таблицы. Т.к. эта часть кода находится в теле цикла foreach, то сюда будет подставляться новое значение с каждой итерацией. Ну и последний, завершающий штрих - запись нескольких ACE в массив DACL. Если в предыдущем примере мы делали просто
То теперь такой вариант не подойдёт. А почему? А потому, что операция присвоения каждый раз будет заменять значение DACL последним ACE. Т.к. DACL у нас является массивом данных Win32_Ace, то для добавления новых ACE воспользуемся оператором добавления - "+="
Вот теперь в SD.DACL каждый новый ACE будет добавляться как новый элемент массива. Кстати, говоря, именно этой строчкой мы завершаем цикл foreach, т.к. SecurityDescriptor у нас уже готов и его уже можно как обычно отправлять дальше в код. Подытожив всю полученную информацию мы в конечном итоге получим вот такой код:
Вот так немного модифицировав код мы добились более широкого функционала, а именно - возможность добавления множества ACE в едином теле скрипта. Здесь я переступил логику. По логике следовало сначала разобрать чтение Share Permissions, а потом уже запись, но ввиду одного момента, о котором я рассказал в начале пришлось сначала изучить возможность установки DACL, а потом уже чтение их из Share Permissions. Об этом я уже расскажу следующий раз. Ждите продолжения :) 2008/7/3 Управление общими сетевыми ресурсами (шарами) в PowerShellВ предыдущих постах я рассказывал про управление списками ACL для объектов файловой системы и реестра. Для этих целей использовались встроенные команддеты Get-Acl и Set-Acl. Весьма тесно с этой темой выступает тема управления правами доступа общего ресурса (т.н. Share Permissions). PowerShell не имеет встроенного механизма управления сетевыми ресусрами, только средствами классов WMI. Управление шарами достаточно очень простое в PowerShell до тех пор, пока это не коснётся вопроса управления Share Permissions. Но начнём с самого простого - создание и удаление сетевого ресурса, а так же маппингом сетевых дисков. Для этого используется WMI класс Win32_Share. По ссылке можно узнать какими свойствами и методами обладает этот класс, поэтому здесь перечислять всё это не буду. Для создания нового сетевого ресурса (шары) нам потребуется метод Create с указанием необходимых параметров (которые так же перечислены по ссылке). Попробуем:
В первой строчке я вызвал класс Win32_Share и второй строчкой использовал метод Create, чтобы создать новый сетевой ресурс из папки C:\Test с именем UserShare, тип сетевого ресура - Disk Drive, максимальным количеством подключений равным 100 и описанием (поле Descritpion) Network share for users. В Windows Server 2003 и выше при создании нового общего ресурса права на ресурс назначаются только группе Everyone - Allow Read. Этот код можно записать и одной строчкой вообще без переменных:
Эта строка выдаст результат в виде таблицы. Последняя строчка ReturnValue будет содержать код возврата, который содержит информацию об операции. Если это 0, то значит, что команда исполнилась успешно и сетевая шара создалась (таблица сопоставления кода возврата текстовому значению находится в описании метода Create). Чтобы посмотреть список сетевых ресурсов на локальном компьютере достаточно выполнить команду Get-WmiObject Win32_Share. Используя метод Delete можно удалять сетевые ресурсы:
или одной строчкой:
Здесь вместо параметра Name можно указать любой другой, который содержится к классе Win32_Share. Диски так же можно мапить, причём сделать это можно двумя путями. Первый вариант создаст новый диск PowerShell с именем User и который будет доступен только из PowerShell (из Explorer'а и других приложений он не будет доступен. Иногда это очень удобно) и классический маппинг дисков, который будет доступен из проводника:
Как же отмапить диски обратно? С точностью до наоборот:
В принципе, этот материал достаточно обширно расписан в интернете на англоязычных ресурсах и я опубликовал этот пост в качестве вводной статьи для использования метода SetShareInfo, который является достаточно полезным, интересным и, в то же время, достаточно сложным. Так же в следующей статье расскажу о некоторых недостатках упрощённого создания сетевых папок, как показано в примерах этой статьи. Вобщем, ждите продолжения :) 2008/6/27 Ещё несколько слов об управлении ACL в PowerShell и полезных деталяхВо второй части серии статей про управление списками ACL в PowerShell я опубликовал скрипт, который создаёт общую папку для папок перенаправления и расставляет необходимые ACE в списке ACL. Перечитывая свой пост я обнаружил, что забыл рассказать об одной вещи. А именно - установка набора разрешений для группы/пользователя. Итак, нашем примере группе Everyone требовались следующие права:
В скрипте мы каждой строчкой добавляли разрешения по одиночке для пользователя и потом записывали их в ACL при помощи метода AddAccessRule. Не самое лучшее решение. А если одной строкой перечислить все права?
Вот так одной строчкой мы смогли добавить набор прав для группы. Здесь хочу лишь отметить, что пользователей так перечислять нельзя. Для каждого пользователя нужно создавать отдельный ACE. Теперь немного поговорим о практической стороне вопроса. А именно - передача параметров запуска в тело скрипта. В примере я явно указал путь и имя домашней папки. Во всяком случае я использую такой путь. Кто-то хочет другой путь, кто-то хочет других пользователей использовать и т.д. Это придётся в каждом частном случае править скрипт по новой. Например, путь к папке в скрипте используется 3 раза. А если их там будет 5, 10? Это ни разу не прибавляет гибкости скрипту. А что если мы будем использовать один скрипт и передавать праметры (например, параметр пути) из командной строки CMD? А давайте! Строка запуска очень простая:
Разберём, что мы тут имеем:
В скрипте эти параметры принимаются командой Param. Синтаксис тут тоже очень простой:
Команда Param в последовательном порядке выбирает параметры и по очереди присваивает их переменным, которые уже будут работать в скрипте. В качестве примера я передам в скрипт путь к папке и группу Domain Users:
отмечу, что если скрипт запускается из консоли PowerShell то имя исполняемого файла указывать не надо, а начинать сразу с пути к скрипту. и сам скрипт: Я выделил жирным участки кода, где будут использовать переменные с переданными в них параметрами командной строки. 2008/6/25 Особенности конвертирования hex-значений в PowerShellКак известно, PowerShell позволяет очень просто конвертировать hexadecimal значения в decimal. Достаточно в консоли набрать 0xhex_значение. Вот несколько примеров:
Кажется тут всё очень просто и говорить как бы не о чем даже. Однако при работе с относительно большими величинами нужно учитывать тип вводимых данных. К типам данных относятся следующие типы:
и много других типов (Decimal, Single, Double, etc). В контексте hexadecimal чисел нас будут интересовать только эти 4 типа представления чисел. К чему я это? А к тому, что при конвертировании определённых чисел из hex в decimal вы можете получить не совсем ожидаемый результат. Например:
и если мы прибавим единичку к этому выражению, то получим hex значение 0x80000000 и которое должно равняться 2147483648 в десятичном выражении. Посмотрим, что скажет нам PowerShell:
откуда появился знак минуса? А появился он очень просто. Я специально выше указал диапазоны значений для каждого типа данных. Дело в том, что 0x7FFFFFFF является самым большим положительным числом для типа Int32 и 0x80000000 в десятичной форме будет самым большим отрицательным числом для Int32. Диапазон отрицательных чисел для Int32 находится между -2147483648 (0x8000000) и -1 (0xFFFFFFFF). По умолчанию PowerShell старается присвоить число к типу Int32 или Int64. Как в этом можно убедиться? А очень просто:
Здесь видно, что PowerShell число 0xDead присвоил к типу Int32, а число 4294967296 присвоил к типу Int64. И если мы работаем только с арифметикой (только положительными числами), то при использовании типов Int32/Int64 мы будем получать весьма неожиданные результаты в виде отрицательных чисел. Поэтому нам нужен механизм, который бы подсказал PowerShell'у тот тип данных, который нам нужен. Это делается следующим образом - [тип_данных]данные:
UInt32/UInt64 не содержит отрицательных чисел в десятичном представлении, поэтому при работе только с положительными числами нужно использовать тип UInt32/UInt64, который избавит нас от проблем с появлением отрицательных чисел:
Как же так? Мы явно указали, что следует использовать тип UInt32!!! И вот я вас подвёл к тому, что хотел сказать - на самом деле PowerShell всю процедуру преобразования чисел в типы данных выполняет следующим образом:
Согласно этой последовательности действий PowerShell сначала преобразовал число 0x8000000 в Int32 и ему это удалось (получил -2147483648), после чего PowerShell перешёл к шагу 2 и попытался преобразовать число -2147483648 в UInt32. Но, как я уже сказал, UInt32/UInt64 не могут содержать отрицательных чисел, поэтому мы и получили ошибку. Это определённый недостаток, что PowerShell сначала делает своё внутреннее преобразование числа, а только потом пытается преобразовать к тому типу, который мы указываем явно. Однако PowerShell имеет механизм (большое спасибо разработчикам PowerShell за очень быструю реакцию в решении этого вопроса), который переопределяет порядок преобразований типов данных. Для того, чтобы PowerShell чётко следовал нашим инструкциям и сразу преобразовывал число в указанный явно тип данных нужно данное hexadecimal число включить в одинарные кавычки.
вот теперь мы получаем ожидаемый результат. Исходя из всего выше сказанного при работе с hexadecimal числами следует взять за правило явное указание типа используемых данных и включение hex числа в одинарные кавычки, как это показано на последнем примере. Кстати говоря, использование кавычек (как одинарных, так и двойных) имеет достаточно много интересных и полезных особенностей. Для вводного курса по кавычкам советую почитать в блоге Василия Гусева: Иногда кавычки имеют значение ;)Хорошо всё то, что хорошо заканчивается :) 2008/6/23 Смена владельца (Owner) папки или файлаВ 3-й части стати Управление ACL в PowerShell я говорил, что не представляется возможным нативно через метод SetOwner изменить владельца объекта на кого-нибудь, кроме себя или группы Administrators (Администраторы). Эту операцию невозможно было сделать и в GUI в Windows 2000/XP. Но в Windows Server 2003 появилась возможность изменять владельца объекта на любого пользователя или группу из GUI. Для этой операции требуется лишь право Se_Restore - Restore directories and files в локальной политике безопасности, разделе User Rights Assignment. По умолчанию данное право дано администраторам (Administrators) и операторам архива (Backup Operators). Однако, данное право не включено по умолчанию. К сожалению .NET пока что не имеет "родного" метода, чтобы включить данное право, в результате чего используя скрипты PowerShell мы не сможем полноценно использовать новую в Windows Server 2003 функцию. Но не всё так плохо, как может показаться - есть обходное решение данной проблемы. Данную проблему решили в проекте PowerShell Community Extensions. Для использования этой привилегии необходимо установить пакет PowerShell Community Extensions 1.1.1 (там же и описание возможностей, которые включены в эти расширения). После установки пакета можно начинать использовать данное расширение:
Это было в расширенном варианте. Данный скрипт можно смело упростить до:
Решение в виде установки дополнительных расширений не является самым идеальным, но тем не менее позволяет использовать чистый PowerShell для операции (а ведь можно было из PowerShell запустить SubInacl и им уже сменить владельца). 2008/6/18 Преобразование имени пользователя в SID и обратноПри написании скриптов, которые автоматически назначают права NTFS или права доступа к реестру (ну и в других ситуациях) иногда требуется конвертация имени пользователя в его SID и наоборот, у нас есть SID пользователя и нужно узнать его имя в базе SAM или базе Active Directory. Для этих преобразований используется 2 класса - SecurityIdentifier и класс NTAccount. Делается каждая операция в 2 строчки:
Здесь я в первой строке передал в класс NTAccount имя пользователя (выделено жирным) и второй строкой перевёл (дословно Translate) это имя в SID. Разумеется, что если данного имени не будет обнаружено в базе учётных записей, то скрипт вернёт ошибку. Обратная операция выполняется точно так же, только меняются местами классы:
Здесь я указал один из well-known SID'ов, т.к. реальный SID грозился не влезть в одну строчку. Зато вы можете выполнить эти 2 строчки и узнать, какому аккаунту приналдежит данный SID. Вроде бы очень простенькие операции, но порой очень нужные. 2008/6/12 Управление ACL в PowerShell (часть 3, заключительная)
В предыдущих частях я рассказал как управлять ACL списками в PowerShell для файлов и папок NTFS. В этой, заключительной части я покажу:
Управление ACL списками реестраACL списки реестра в PowerShell управляются по такому же принципу, что и для объектов файловой системы NTFS. Отличия заключаются только в доступных разрешениях, уровнях наследования и именах используемых классов. Для установки разрешений на ключи реестра используется класс RegistryAccessRule. Список самих разрешений перечислен в RegistryRights Enumeration. Чтобы посмотреть этот список в PowerShell достаточно выполнить команду:
Получение списков ACL для ключей реестра производится так же как и для объектов файловой системы:
Создание, удаление и базовые операции с ключами реестра достаточно наглядно и доступно рассказано в TechNet, поэтому здесь я не буду рассказывать про эти операции. Предположим, у нас есть ключ реестра HKLM\Software\AutoCAD на который группа Users имеет право чтения и мы хотим группе Users дать право WriteKey. Для этого по аналогии с назначением прав на объекты файловой системы сделаем следующее:
Единственное отличие от варианта с файловой системой заключается в используемом классе, а именно: RegistryAccessRule вместо FileSystemAccessRule. Таким же образом и удаляются явно назначенные ключи реестра:
Здесь я выделил только изменившийся метод, который теперь стал RemoveAccessRule и удаляет необходимые разрешения. Наследование тоже отменяется схожим методом:
Т.е. на этих примерах видно, что управление ACL реестра из PowerShell абсолютно такое же, отличи тут только одно - используемые классы управления. И в завершении разговора об управлении ACL реестра из PowerShell добавлю лишь информацию о глубине действия устанавливаемых разрешений:
Смена владельца для объектаК сожалению по некоторым причинам управление владельцами объектов затруднено, но, тем не менее, некоторые возможности присутствуют - это назначение владельцем себя самого или своей группы. Предположим, есть некоторый ресурс, владельцем которого является рядовой пользователь и во время выполнения скрипта администратору необходимо перехватить владение объектом на себя, т.е. стать новым владельцем ресурса. Делается это по схожей аналогии как мы добавляли в предыдущем примере пользователя в список доступа к ресурса и назначали ему право Modify. Итак:
Вот таким образом можно спокойно перехватывать владение объектом. Но, при этом пользователь из под которого выполняется данный скрипт должен иметь следующие права:
Примечание: к сожалению в PowerShell пока что нету механизма установки в качестве владельца другого пользователя или группы. Можно только назначить владельцем либо себя, либо свою группу. С чем это связано мне пока точно неизвестно, но данный вопрос уже отправлен спецам по PowerShell, которые, возможно, смогут помочь как-то разъяснить ситуацию. Управление аудитом объектов файловой системы и реестра из PowerShellНазанчение правил аудита доступа к объектам файловой системы и реестра назначаются точно по таким же принципам, что и установка разрешений безопасности. Разница здесь лишь в используемых классах. Здесь коротко опишу класс и его методы. Для файловой системы основным классом является FileSystemAuditRule, который содержит в себе такие методы как (перечислю наиболее часто используемые): AddAuditRule - добавляет правила аудита на объект файловой системы; SetAuditRule - устанавливает правила аудита на объект файловой системы (заменяет имеющийся); SetAuditRuleProtection - устанавливает наследование правил аудита от родительского объекта; RemoveAuditRule - удаляет правила аудита доступа; PurgeAuditRules - очищает весь список аудита доступа к объектам. Вообще, весь список методов описан в классе FileSystemSecurity и который можно просмотреть здесь: Для аудита реестра используется класс RegistryAuditRule, который содержит похожие методы, что и для файловой системы и полный список методов описан в классе RegistrySecurity: На предыдущих примерах я достаточно наглядно показал управление разрешениями доступа к объектам файловой системы и реестра, поэтому трудностей в управлении аудитом доступа к объектам с использованием тех же самых принципов не должен вызвать затруднений. 2008/5/25 Управление ACL в PowerShell (часть 2)В первой части я рассказал про простое использование командлетов Get-Acl и Set-Acl, которые в PowerShell позволяют работать со списками ACL для объектов, как реестр, файловая система NTFS. Во этой части я расскажу про:
И в конце напишу скрипт с практическим применением. Удаление ACE из списков разрешений ACLВ продолжении рассмотрим удаление разрешений как всех разрешений для пользователя, так и выборочных рарешений для пользователя. Если в предыдущем примере мы создавали новые разрешения для пользователя при помощи метода SetAccessRule, то для удаления этих разрешений будет использоваться метод RemoveAccessRule. Здесь я даже комментировать ничего не буду, а просто отменю все выданные в предыдущем примере разрешения:
Здесь я только выделил ту часть, которая изменилась по сравнению с предыдущим скриптом. И по совету Васи Гусева убрал временную переменную и напрямую передал в объект необходимые параметры. Для удаления всех ACE из ACL объекта (в нашем примере из ACL папки) мы применим метод ObjectSecurity.PurgeAccessRules, для работы которого в качестве параметра нужно передать имя пользователя или группы. Если перейти по ссылке на описание, то можно будет прочитать, что в качестве параметра будет использоваться IdentityReference. Вот так примерно получится удаление всех разрешений для группы Users (равносильно нажатию кнопки Remove в GUI интерфейсе). Вот такой получится скрипт на удаление:
Достаточно немного переработать этот скрипт и мы сможем удалять абсолютно все ACE из ACL объекта:
Как я уже сказал - переменную $Users я ввёл только для получения наглядности работы цикла. PurgeAccessRules можно сразу передать аргумент $_.IdentityReference и тогда у нас получится всего лишь 2 строчки:
И в качестве заключительного комментария для данной секции добавлю, что таким образом можно удалять только явно назначенные разрешения. Управление наследованием разрешений в PowerShellТеперь можно поговорить о более глубоком управлении списками ACL, а именно - управление наследованием и установкой необходимой области действия разрешений. Если смотреть скрипт, который был опубликован в первой части, то данный скрипт устанавливает разрешения только для конкретного объекта и дочерние объекты установленных разрешений не наследуют. Но сперва займёмся отменой наследования от родительского объекта. По умолчанию при создании объекта он наследует разрешения от родительского объекта. Если мы хотим создать объект (например, папку) при этом запретив ему наследование разрешений от родителя, то можно выполнить вот такой несложный скрипт:
Хочется ещё добавить по SetAccessRuleProtection, что второй параметр $false кроме явно заданных разрешений ещё оставит группу в которой состоит пользователь. Поэтому, возможно, потребуется дополнительная чистка ACL для данной группы. Управление глубиной действия разрешенийКогда мы научились добавлять и удалять различные ACE для списка ACL, можно поговорить про установку глубины действия разрешений для папок. За управление глубиной действия разрешений (раздел Apply onto в окне просмотра расширенных прав доступа отвечают классы InheritanceFlags и PropagationFlags. Их значения могут быть знакомы тем, кто работал с утилитами командной строки CACLS и ICACLS. Класс InheritanceFlags содержит 2 представителя класса:
Класс PropagationFlags так же содержит 3 представителя класса:
комбинацией этих членов можно получить различные значения наследования раздела Apply onto в окне просмотра расширенных прав. Не пускаясь в долгие рассуждения приведу таблицу сопоставления значений и требуемых для этого флагов:
Теперь, как и обещал, получив необходимые знания, можно приступать к решению практической задачи. В качестве примера задачи будет - установка разрешений для корневой папки папок перенаправления, как это требуется в рекомендации Microsoft - How to dynamically create security-enhanced redirected folders by using folder redirection in Windows 2000 and in Windows Server 2003
Вот так используя уже полученные знания можно начинать решать даже нетривиальные задачи по управлению списками ACL из PowerShell. Уверен, что объём кода в VBS для решения этой же задачи будет далеко не таким компактным :) В следующей и заключительной по теме управления списками ACL в PowerShell части я расскажу как управлять списками ACL реестра и смена владельца объекта. |
|
|