Previous Page Page 2 of 4 in the PowerShellWMI category Next Page

Windows Management Instrumentarion или просто WMI в PowerShell V2 так же претерпел изменения, а точнее дополнения по сравнению с версией 1.0. Я планирую более подробно описать новые и обновлённые параметры командлета Get-WMIObject:

1) -List - ключ. Позволяет получать список классов для системы или список свойств и методов для конкретного класса:

[vPodans] Get-WmiObject -List


   NameSpace: ROOT\cimv2

Name                                Methods              Properties
----                                -------              ----------
__NotifyStatus                      {}                   {StatusCode}
__ExtendedStatus                    {}                   {Description, Operation, ParameterInfo, ProviderName...}
Win32_PrivilegesStatus              {}                   {Description, Operation, ParameterInfo, PrivilegesNotHeld...}
Win32_JobObjectStatus               {}                   {AdditionalDescription, Description, Operation, ParameterIn...
__SecurityRelatedClass              {}                   {}
__Trustee                           {}                   {Domain, Name, SID, SidLength...}
Win32_Trustee                       {}                   {Domain, Name, SID, SidLength...}
__NTLMUser9X                        {}                   {Authority, Flags, Mask, Name...}
....

По умолчанию, без указания других параметров, параметр -List выводит список классов, которые расположены в контейнере по умолчанию Root\CimV2. Но можно указывать и другие контейнеры при помощи параметра -NameSpace. Например:

Get-WmiObject -Namespace root\default -List

Или для конкретного класса:

[vPodans] Get-WmiObject Win32_share -List | select * | fl


Name             : Win32_Share
__GENUS          : 1
__CLASS          : Win32_Share
__SUPERCLASS     : CIM_LogicalElement
__DYNASTY        : CIM_ManagedSystemElement
__RELPATH        : Win32_Share
__PROPERTY_COUNT : 10
__DERIVATION     : {CIM_LogicalElement, CIM_ManagedSystemElement}
__SERVER         : THOR
__NAMESPACE      : ROOT\cimv2
__PATH           : \\THOR\ROOT\cimv2:Win32_Share
Path             : \\THOR\ROOT\cimv2:Win32_Share
Derivation       : {CIM_LogicalElement, CIM_ManagedSystemElement}
Methods          : {Create, SetShareInfo, GetAccessMask, Delete}
Scope            : System.Management.ManagementScope
Options          : System.Management.ObjectGetOptions
ClassPath        : \\THOR\ROOT\cimv2:Win32_Share
Properties       : {AccessMask, AllowMaximum, Caption, Description...}
SystemProperties : {__GENUS, __CLASS, __SUPERCLASS, __DYNASTY...}
Qualifiers       : {CreateBy, DeleteBy, dynamic, Locale...}
Site             :
Container        :



[vPodans]

здесь мы видим доступные методы (Create, SetShareInfo и другие), а так же свойства объекта класса (AccessMask, AllowMaximum, Caption и другие). Можно и более подробно посмотреть методы или свойства объекта:

Get-WmiObject Win32_share -List | %{$_.methods} - получение подробных сведений о методах объекта класса
Get-WmiObject Win32_share -List | %{$_.properties} - получение подробных сведений о свойствах объекта класаа

Кстати говоря, в PowerShell 1.0 параметр -List не работал для конкретных классов, а только для перечисления самих классов. Т.е. приведённые выше 2 команды в 1.0 не работают. Теперь этот недостаток исправлен.

2) -AsJob - ключ. Позволяет выполнять команду в фоновом режиме. Это новая возможность в PowerShell V2, которая позволяет запускать некоторые команды в фоновом режиме не блокируя при этом саму консоль. Коллега, Вася Гусев, уже делал скринкаст по фоновым работам в PowerShell (и не только о них) на TechDays.ru (потребуется авторизация с помощью LiveID или OpenID). И я так же планирую обсудить в своём блоге этот вопрос в обозримом будущем.

3) -Authentication - параметр. Позволяет задавать уровень аутентификации к удалённой машине. Возможные уровни аутентификации могут быть:

  • -1 (Unchanged) - я не смог найти описание этому уровню, но он в Windows XP/Windows Server 2003 используется для локальных подключений на себя.
  • 0 (Default) - необходимый уровень будет согласовываться между сервером и клиентом в зависимости от настроек Windows Authentication.
  • 1 (None) - не использует аутентификацию. Все уровни безопасности будут проигнорированы. Не следует использовать этот уровень, поскольку с ним аутентифицироваться не удастся (разве что в случае разрешённых анонимных подключений на сервере).
  • 2 (Connect) - клиент аутентифицируется только при установке подключения. После установки подключения аутентификационные данные не проверяются.
  • 3 (Call) - пересылаемые учётные данные проверяются при каждом запросе клиента. Учётные данные криптографически подписываются, но не шифруются. Пересылаемые данные во время сессии не подписываются и не шифруются. Гарантируется, что во время пересылки аутентификационных данных они не были никоим образом изменены.
  • 4 (Packet) - похож на уровень Call с учётом того, что сервер проверяет что данные пришли от ожидаемого клиента. Так же учётные данные подписываются и не шифруются. Данные не подписываются и не шифруются (используется по умолчанию при сетевых подключениях).
  • 5 (PacketIntegrity) - подписываются как учётные данные, так и сами пересылаемые данные. Данный уровень гарантирует, что с момента отправки от клиента до получения данных сервером любые данные не были изменены. Но данные (в том числе учётные) по прежнему не шифруются.
  • 6 (PacketPrivacy) - все передаваемые данные между клиентом и сервером подписываются и шифруются.

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

$a = Get-WmiObject Win32_share -filter "Name = 'sharename'"
$a.PSBase.Scope.Options.Authentication = 6
или PacketPrivacy

4) -Authority - параметр. Позволяет клиенту выбирать, кто будет его аутентифицировать (например, LSA удалённой машины или сервер Active Directory) при указании параметра -Credentials.

5) -DirectRead - ключ. Если указывается, то производится прямой доступ к классу без привязки к нему более высших или производных классов. Реальной пользы я тут пока что не вижу.

6) -Impersonation - параметр. В этом параметре можно указывать уровень имперсонализации при удалённых подключениях. Может иметь следующие значения:

  • 0 (Default) - так же не смог найти описания для него. Во всяком случае в самом WMI такой уровень нигде не определён и скорее всего это какой-то производный уровень из нескольких внутри самого PowerShell.
  • 1 (Anonymous) - При указании этого уровня учётные данные клиента скрываются. По факту WMI в современных версиях ОС скорее всего не поддерживает этот уровень, а переводит его в уровень Identify и использует его для имперсонализации клиента. В Windows 2000 было так, во всяком случае. Да и особого смысла в нём нету, поскольку с таким уровнем ничего сделать не удастся в общем смысле.
  • 2 (Identify) - позволяет объектам запрашивать учётные данные вызывающего пользователя, но не использовать их. Следовательно удалённый объект не сможет установить вас в контексте безопасности (т.е. подтвердить, что вы именно тот за кого себя выдаёте). С таким уровнем обычно можно только прочитать ACL списки объектов (и то не всегда), но не более.
  • 3 (Impersonate) - позволяет объектам использовать учётные данные вызывающего пользователя. Это в свою очередь позволяет пользователю использовать свои учётные данные для произведения любых операций над объектами в пределах прав пользователя на этот объект (обычно эти права задаются ACL списками). Данный уровень используется в PowerShell по умолчанию. Поэтому для использования уровня Impersonate данный параметр указывать не обязательно.
  • 4 (Delegate) - это уровень транзитивной делегации учётных данных. Это позволяет удалённым объектам передавать ваши учётные данные другим объектам на других машинах. Иными словами, когда пользователь с машины A подключается к машине B, то в этом случае используется уровень Impersonate между A и B. Но если для завершения операции объекту с машины B нужно получить ещё объект с машины C, то Delegate позволит машине B аутентифицироваться на машине C с учётными данными, которые были получены от машины A. Если совсем грубо, то пользователь просто разрешает запрашиваемым объектам использовать его учётные записи для своих нужд. Безусловно, это большой риск, когда объект может распоряжаться полученными учётными данными как хочет, поэтому все пользователи и компьютеры, которые будут участвовать в транзакции (например, пользователь и компьютеры A, B и C) должны быть помечены как Trusted For Delegation в Active Directory.

В версии 1.0 так же можно было изменять уровень имперсонализации с теми же сложностями, которые были и у параметра Authentication. А именно - нельзя было изменять этот уровень при первом подключении к WMI. Делалось это так:

$a = Get-WmiObject Win32_share -filter "Name = 'sharename'"
$a.PSBase.Scope.Options.Impersonation = 4
или Delegate

7) -EnableAllPrivileges - ключ. Данный ключ включает все привилегии безопасности (семейство SeSecurity Privilege), которыми обладает пользователь. Я уже не раз отмечал, что при работе в системе из графической оболочки Explorer, LSA (Local Security Authority) прозрачно для пользователя включает привилегии при необходимости. Однако, в целях безопасности для скриптов LSA по умолчанию не включает их. Привилегии безопасности нужны для многих операций, как смена владельца объектов, редактирования списков ACL множества объектов, выполнение критических для системы операции как удаление журналов событий, восстановления системы из точек восстановления и т.д. И только WMI нам предлагает интерфейс для их использования. Причём теперь это делается простым указанием ключа при использовании командлета Get-WmiObject. В общем смысле в версии 1.0 нельзя было из скриптов включать привилегии, хотя по факту данный функционал был заложен и активно использовался:

$a = Get-WmiObject Win32_share -filter "Name = 'sharename'"
$a.PSBase.Scope.Options.EnablePrivileges = $true

В этом отношении PowerShell является менее безопасным, чем, к примеру, VBS, который позволяет включать только необходимые привилегии безопасности (например для смены владельца объекта только SeRestorePrivilege и SeTakeOwnershipPrivilege). Причём VBS может очень гибко работать с подключаемыми привилегиями, что делает код более безопасным. В PowerShell они включаются все сразу и отдельные привилегии включать нельзя.

Вот, в принципе и рассмотрели основые полезные и интересные нововведения в командлет Get-WmiObject для работы с WMI в PowerShell V2 CTP3. Я допускаю, что эти параметры к релизу вряд ли претерпят существенные изменения и будут справедливы и в финальной версии V2.

Sunday, January 04, 2009 8:37:15 PM (FLE Standard Time, UTC+02:00)   Comments [0]    

 

По мотивам темы на форуме - http://forums.microsoft.com/TechNet-RU/ShowPost.aspx?PostID=4255895&SiteID=40. PowerShell без проблем может управлять восстановлением системы - SystemRestore средствами WMI. За это отвечает классы

Примечание: SystemRestore доступно только на клиентских версиях - Windows XP/Windows Vista. В серверных редакицях Windows Server нереализовано никак.

вот так выглядит GUI окно системы восстановления в Windows Vista:

systemrestore

Следует так же учесть, что эти классы не находятся в пространстве имён по умолчанию Root\Cimv2, а в Root\Default. Для просмотра всех точек восстановления нужно просто получить объект данного класса (если вы работаете под управлением Windows Vista, то потребуется запустить консоль с повышенными привилегиями!). При получении объекта класса не забудьте указать правильный путь размещения класса в пространстве имён:

[System32] gwmi -Namespace "root\default" -class systemrestore __GENUS : 2 __CLASS : SystemRestore __SUPERCLASS : __DYNASTY : SystemRestore __RELPATH : SystemRestore.SequenceNumber=473 __PROPERTY_COUNT : 5 __DERIVATION : {} __SERVER : THOR __NAMESPACE : root\default __PATH : \\THOR\root\default:SystemRestore.SequenceNumber=473 CreationTime : 20081220184403.157460-000 Description : Scheduled Checkpoint EventType : 100 RestorePointType : 7 SequenceNumber : 473 ......

у меня этих точек восстановления несколько, поэтому я показал только первую точку. Здесь нам будут интересны следующие свойства - Description (название точки восстановления), EventType (тип точки восстановления) и SequenceNumber (порядковый номер точки восстановления). Эти значения нам пригодятся для создания новых точек восстановления и отката системы до определённой точки. Если посмотреть методы класса, то получим:

[System32] gwmi -Namespace "root\default" -class systemrestore | gm


   TypeName: System.Management.ManagementObject#root\default\SystemRestore

Name                MemberType   Definition
----                ----------   ----------
CreationTime        Property     System.String CreationTime {get;set;}
Description         Property     System.String Description {get;set;}
EventType           Property     System.UInt32 EventType {get;set;}
RestorePointType    Property     System.UInt32 RestorePointType {get;set
SequenceNumber      Property     System.UInt32 SequenceNumber {get;set;}
__CLASS             Property     System.String __CLASS {get;set;}
__DERIVATION        Property     System.String[] __DERIVATION {get;set;}
__DYNASTY           Property     System.String __DYNASTY {get;set;}
__GENUS             Property     System.Int32 __GENUS {get;set;}
__NAMESPACE         Property     System.String __NAMESPACE {get;set;}
__PATH              Property     System.String __PATH {get;set;}
__PROPERTY_COUNT    Property     System.Int32 __PROPERTY_COUNT {get;set;
__RELPATH           Property     System.String __RELPATH {get;set;}
__SERVER            Property     System.String __SERVER {get;set;}
__SUPERCLASS        Property     System.String __SUPERCLASS {get;set;}
ConvertFromDateTime ScriptMethod System.Object ConvertFromDateTime();
ConvertToDateTime   ScriptMethod System.Object ConvertToDateTime();
Delete              ScriptMethod System.Object Delete();
GetType             ScriptMethod System.Object GetType();
Put                 ScriptMethod System.Object Put();


[System32]

Мы здесь не видим никаких методов, которые бы позволяли создавать и откатываться. Получив объект мы можем его только удалить. Сами методы хранятся в самом классе:

[System32] [wmiclass]'\\.\root\default:systemrestore' | gm -MemberType method


   TypeName: System.Management.ManagementClass#ROOT\default\SystemRestore

Name                 MemberType Definition
----                 ---------- ----------
CreateRestorePoint   Method     System.Management.ManagementBaseObject CreateRestorePoint(System.String Description,...
Disable              Method     System.Management.ManagementBaseObject Disable(System.String Drive)
Enable               Method     System.Management.ManagementBaseObject Enable(System.String Drive, System.Boolean Wa...
GetLastRestoreStatus Method     System.Management.ManagementBaseObject GetLastRestoreStatus()
Restore              Method     System.Management.ManagementBaseObject Restore(System.UInt32 SequenceNumber)


[System32]

вот тут и хранятся наши методы. Методы Enable и Disable позволяют глобально включать режим восстановления системы или отключать для определённого диска. Чтобы включить мониторинг для всей системы в целом или для конкретного диска нужно выполнить:

# объявляем класс:
$sysrestore = [wmiclass]'\\.\root\default:systemrestore'
# выбираем одно из нужных действий. Обратите внимание, что для изменения состояния
#
мониторинга всей системы скобки не должны быть пустые, а содержать пустые двойные кавычки
$sysrestore.Enable("")
$sysrestore.Enable("c:\")
$sysrestore.Disable("")
$sysrestore.Disable("c:\")

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

$sysrestore = [wmiclass]"\\.\root\default:systemrestore"
$sysrestore.CreateRestorePoint("MyRestorePoint", 0, 100)

где MyRestorePoint - название точки восстановления, 0 - тип точки восстановления (возможные значения можно посмотреть по ссылке на MSDN) и 100 - тип события. Давайте создадим новую точку восстановления:

[System32] $sysrestore = [wmiclass]"\\.\root\default:systemrestore"
[System32] $sysrestore.CreateRestorePoint("MyRestorePoint", 0, 100)


__GENUS          : 2
__CLASS          : __PARAMETERS
__SUPERCLASS     :
__DYNASTY        : __PARAMETERS
__RELPATH        :
__PROPERTY_COUNT : 1
__DERIVATION     : {}
__SERVER         :
__NAMESPACE      :
__PATH           :
ReturnValue      : 0



[System32]

Значение RetrunValue = 0 говорит, что точка восстановления создана. Убедимся в этом:

[System32] gwmi -Namespace "root\default" -class systemrestore | select -Last 1


__GENUS          : 2
__CLASS          : SystemRestore
__SUPERCLASS     :
__DYNASTY        : SystemRestore
__RELPATH        : SystemRestore.SequenceNumber=475
__PROPERTY_COUNT : 5
__DERIVATION     : {}
__SERVER         : THOR
__NAMESPACE      : root\default
__PATH           : \\THOR\root\default:SystemRestore.SequenceNumber=475
CreationTime     : 20081221183404.006675-000
Description      : MyRestorePoint
EventType        : 100
RestorePointType : 0
SequenceNumber   : 475



[System32]

Вот теперь мы видим нашу точку восстановления с названием MyRestorePoint и её SequenceNumber стал на 1 больше. Чтобы откатиться на эту точку восстановления нужно выполнить метод Restore с указанием SequenceNumber:

$sysrestore = [wmiclass]"\\.\root\default:systemrestore"
$sysrestore.Restore("475")
(
gwmi win32_operatingsystem).reboot()

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

[System32] gwmi -Namespace "root\default" -class systemrestoreconfig


__GENUS           : 2
__CLASS           : SystemRestoreConfig
__SUPERCLASS      :
__DYNASTY         : SystemRestoreConfig
__RELPATH         : SystemRestoreConfig.MyKey="SR"
__PROPERTY_COUNT  : 5
__DERIVATION      : {}
__SERVER          : THOR
__NAMESPACE       : ROOT\default
__PATH            : \\THOR\ROOT\default:SystemRestoreConfig.MyKey="SR"
DiskPercent       : 15
MyKey             : SR
RPGlobalInterval  : 86400
RPLifeInterval    : 4294967295
RPSessionInterval : 1



[System32]

тут всё просто. DiskPercent показывает процент дискового пространства, которое выделяется под восстановление системы. RPGlobalInterval показывает периодичность создания точек восстановления (а для Windows Vista создание теневых копий) в секундах. RPLifeInterval - время жизни точек восстановления (и теневых копий для Windows Vista) в секундах (по умолчанию 90 дней). Эти параметры можно изменять простым переприсвоением и применением новых значений скриптметодом Put(). К сожалению, провайдер SystemRestore не позволяет удалять точки восстановления (вообще это можно с использованием функции SRRemoveRestorePoint, но это уже совсем другая история программирования).

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

########################################################
# SystemRestore.ps1
# Version 1.0
#
# Functions for System Restore management
# Note: available for Windows XP and Windows Vista only!
#
# Vadims Podans (c) 2008
# http://www.sysadmins.lv/
########################################################

# функция для преобразования кода возврата в текстовое значение
function _SysRestore_Get-Code ($Action) {
switch ($Action.ReturnValue) {
   "0" {"Success"}
   default {"Unknown error $Action.ReturnValue"}
   }
}

# простая функция получения списка всех доступных точек восстановления в табличном виде
function Get-SystemRestore {
gwmi -Namespace "root\default" -class systemrestore | Select Description, @{n="Date/Time";
e={([System.Management.ManagementDateTimeconverter]::ToDateTime($_.CreationTime)).tostring()}},
@{n="Point number"; e={$_.SequenceNumber}} | ft -AutoSize
}

# функция включения или отключения System Restore для указанного диска или, если диск не указан
# то действие будет принято для всей системы в целом. А так же позволяет откатывать систему
# в более раннее состояние.
function Set-SystemRestoreStatus ($Status, $arg) {
# собственно, само действие включения или отключения System Restore
switch ($status) {
    "Enable" {$action = ([wmiclass]'\\.\root\default:systemrestore').Enable("$arg")}
    "Disable" {$action = ([wmiclass]'\\.\root\default:systemrestore').Disable("$arg")}
# процесс отката системы в более раннюю точку восстановления
    "Restore" {$RP = gwmi -Namespace "root\default" -class systemrestore | ?{$_.SequenceNumber -eq [int]$arg}
# проверяем, что указанная точка восстановления существует.
    if ($RP) {
# если существует, то проводим процесс восстановления в указанную точку
        $action = ([wmiclass]'\\.\root\default:systemrestore').Restore("$arg")
        Write-Host "Performing system restore to earlier status ..."
# получаем код возврата операции
        _SysRestore_Get-Code $action
# если восстановление системы прошло успешно, то принудительно перезагружаем систему 
        if ($(_SysRestore_Get-Code $action) -eq "Success") {
            Write-Warning "To complete System Restore system will reboot in 15 sec"
            shutdown.exe -r -t 15 -f
            }
        }else {
        Write-Host "Specified restore point does not found!"
        }
    }
    "Create" {$action = ([wmiclass]'\\.\root\default:systemrestore').CreateRestorePoint("$arg", 0, 100)}
}
# здесь происходит только генерация сообщения о ходе выполнения операций
if ($arg -and ("Enable", "Disable" -contains $status)) {
    Write-Host "Setting SystemRestore status to $status on" $arg
    _SysRestore_Get-Code $action
    } else {
    Write-Host "Setting SystemRestore status to $status on all drives: "
    _SysRestore_Get-Code $action
    }
}

# функция для изменения настроек System Restore
function Set-SystemRestoreSetting ($setting, $value) {
# получаем текущие настройки
$SRSetting = gwmi -Namespace "root\default" -class systemrestoreconfig
# проверяем выбранный параметр, который будет изменяться
switch ($setting) {
    "DiskSpace" {
# т.к. дисковое место указывается в процентах, то проверяем, что указано число от 1% до 99%
        if ($value -lt 99 -and $value -gt 1) {
            $SRSetting.DiskPercent = $value
        } else {
        Write-Warning "Disk space percentage must be integer and vlaue must be between 1-99"}
    }
# установка глубины точек восстановлений. Указывается в днях. По истечении этого времени
# точки восстановления удаляются, высвобождая место для более новых точек восстановления
    "HistoryDepth" {
        $SRSetting.RPLifeInterval = [int]$value * 86400
    }
# частота автоматического создания точек восстановлений
    "Frequency" {
# извлекаем из заданного параметра периодичности последние 4 символа, которые образуют префикс
# в днях или часах и выставляем множитель для получения секунд. Если это часы, то множитель будет
# 3600, а если в днях, то множитель будет 86400 секунд
        $time = $value.substring($value.length - 4, 4)
        switch ($time) {
            "hour" {$sec = 3600}
            "days" {$sec = 86400}
        }
# извлекаем из заданного параметра периодичности все символы, чтобы получить целочисленное значение
# кроме последних 4, которые являются текстовыми и обозначают лишь префикс
        $SRSetting.RPGlobalInterval = $sec * $value.substring(0, $value.length - 4)
        }
    default {Write-Warning "Parameter must be DiskSpace or HistoryDepth or Frequency"}
    }
# применение новых параметров
$SRSetting.Put()
}

Синтаксис функций может быть следующий:

  • Get-SystemRestore - показывает в табличном виде все существующие точки восстановления. Аргументов не принимает.
  • Set-SystemRestoreStatus Enable c:\ - включает режим SystemRestore.
    где c:\ - диск, для которого включается мониторинг SystemRestore. Параметр опциональный. Если не указан, то включается для всех дисков.
  • Set-SystemRestoreStatus Disable c:\ - отключает режим systemRestore.
    где c:\ - диск, для которого выключается мониторинг SystemRestore. Параметр опциональный. Если не указан, то выключается для всех дисков.
  • Set-SystemRestoreStatus Restore 5 - производит откат системы до указанной точки восстановления. После отката произойдёт принудительная перезагрузка системы.
    где 5 - номер точки восстановления. Номер можно получить выполнив функцию Get-SystemRestore.
  • Set-SystemRestoreStatus Create "MyNewRestorePoint" - создаёт новую точку восстановления
    где MyNewRestorePoint - название точки восстановления.
  • Set-SystemRestoreSetting DiskSpace 10 - задаёт резервируемое место для точек восстановления в процентах от ёмкости диска.
    где 10 - процент диска, которое отводится под SystemRestore. Может иметь значение от 1 до 99
  • Set-SystemRestoreSetting HistoryDepth 10 - задаёт время хранения точек восстановления
    где 10 - количество дней, в течении которого будут храниться точки восстановления. Более старые будут автоматически удаляться.
  • Set-SystemRestoreSetting Frequence 5days - задаёт частоту автоматического создания точек восстановления.
    где 5days - периодичность создания точек восстановления в днях. Можно указывать и в часах, например, 10hour (будет делать автоматические точки восстановления каждые 10 часов). В общем смысле сначала идёт число и потом без пробелов суффикс days или hour.
Monday, December 22, 2008 3:40:46 PM (FLE Standard Time, UTC+02:00)   Comments [0]    

 

В предыдущей части мы рассмотрели возможности управления журналами событий средствами .NET Framework, а так же рассмотрели проблематику использования .NET (из моего предыдущего блога) - Странности Get-Eventlog и не совсем богатый функционал. В этом посте мы разберём данный вопрос, но с использованием WMI и все примеры будут выполняться системе под управлением Windows Vista.

В WMI за журнал событий отвечают следующие классы:

  1. Win32_NTEventlogFile
  2. Win32_NTLogEvent
  3. Win32_NTLogEventComputer
  4. Win32_NTLogEventLog
  5. Win32_NTLogEventUser

WMI, как и .NET поддерживает удалённую работу с использованием ключа -ComputerName, поэтому на этом заострять внимание не будем. За публикацию списка журналов в системе отвечает класс Win32_NTEventlogFile:

[vPodans] gwmi Win32_NTEventlogFile FileSize LogfileName Name NumberOfRecords -------- ----------- ---- --------------- 8458240 Application C:\Windows\System32\Winevt... 16154 69632 DFS Replication C:\Windows\System32\Winevt... 6 69632 HardwareEvents C:\Windows\System32\Winevt... 0 69632 Internet Explorer C:\Windows\System32\Winevt... 0 69632 Key Management Service C:\Windows\System32\Winevt... 0 69632 ODiag C:\Windows\System32\Winevt... 267 1118208 OSession C:\Windows\System32\Winevt... 524 20975616 System C:\Windows\System32\Winevt... 42472 12652544 Windows PowerShell C:\Windows\System32\Winevt... 14208 [vPodans]

Стандартная процедура вывода списка журналов в системе. А теперь и сами эвенты:

[vPodans] gwmi win32_ntlogevent -filter "logfile='application'" | select -first 1 Category : 0 Category String : Event Code : Event Identifier : Type Event : Insertion Strings : Log File : Message : The User Profile Service has started successfully. Record Number : Source Name : Time Generated : Time Written : Type : Information User Name : [vPodans]

как бы информации не сильно много. Давайте немного приведём вид в более оперативный:

[vPodans] gwmi win32_ntlogevent -filter "logfile='application'" | select recordnumber, timegenerated, sourcename, eventc ode, message -first 1 | ft -a recordnumber timegenerated sourcename eventcode message ------------ ------------- ---------- --------- ------- 1 20070710111520.000000-000 Microsoft-Windows-User Profiles Service 1531 The User Profile Service ha... [vPodans]

уже более читабельно, кроме вывода даты. Чтобы подвести вывод даты и времени в формат [datetime] нужно воспользоваться конвертером времени:

[System.Management.ManagementDateTimeconverter]::ToDateTime("20070710111520.000000-000")

Чтобы привести итоговый вывод времени в укороченный формат даты и времени к данному выражению достаточно применить метод ToString().

([System.Management.ManagementDateTimeconverter]::ToDateTime("20070710111520.000000-000")).ToString()

[vPodans] [System.Management.ManagementDateTimeconverter]::ToDateTime("20070710111520.000000-000")

otrdiena, 2007. gada 10. julija 14:15:20


[vPodans] ([System.Management.ManagementDateTimeconverter]::ToDateTime("20070710111520.000000-000")).ToString()
10.07.07. 14:15:20
[vPodans]

А теперь всё сделаем так (с использованием хэш-таблиц), чтобы это всё на лету преобразовывалось и мы получили сразу готовый и более опрятный вывод:

$a =  gwmi win32_ntlogevent -filter "logfile='application'" | select -first 1
$a | Select recordnumber, @{n="timegenerated";
e={([System.Management.ManagementDateTimeconverter]::ToDateTime($_.timegenerated)).tostring()}},
sourcename, eventcode, message | ft -AutoSize
[vPodans] $a =  gwmi win32_ntlogevent -filter "logfile='application'" | select -first 1
[vPodans] $a | Select recordnumber, @{n="timegenerated";
>> e={([System.Management.ManagementDateTimeconverter]::ToDateTime($_.timegenerated)).tostring()}},
>> sourcename, eventcode, message | ft -AutoSize
>>

recordnumber timegenerated      sourcename                              eventcode message
------------ -------------      ----------                              --------- -------
           1 10.07.07. 14:15:20 Microsoft-Windows-User Profiles Service      1531 The User Profile Service has start...


[vPodans]

вот так при помощи костылей мы привели вывод в нечто более божеское. В остальном WMI не представляет ничего интересного, что могло бы представиться полезным (за исключением возможности бэкапа журналов, которую я разобрал в предыдущем посте). Разобрать работу Win32_NTLogEventUser и Win32_NTLogEventComputer в принципе можно уже и самостоятельно.

Исходя из вышеизложенного можно понять, что WMI вариант управления эвентлогом не самый удобный и практичный. Но позволяет извлекать события из журналов Windows Vista/Windows Server 2008 в читабельном виде. При этом можно изменять различные параметры журналов событий которые описаны здесь: http://msdn.microsoft.com/en-us/library/aa394225(VS.85).aspx

Изменения делаются по одной общей схеме:

$EventLog = gwmi Win32_NTEventlogFile -Filter "logfilename = '$logfilename'"
$EventLog.Property = "New Property Value"
$EventLog.Put()

И в качестве последнего штриха приведу замеры скорости работы с эвентлогом с использованием .NET и WMI:

  • Windows Server 2003
[user name] (measure-command {gwmi win32_ntlogevent -filter "logfile='application' and eventcode=8194"}).totalseconds
13.0865344
[user name] (measure-command {(new-object diagnostics.eventlog("application")).Entries | ? {$_.EventID -eq 8194}}).totalseconds
20.7334336
[user name]

  • Windows Vista:
[vPodans] (measure-command {gwmi win32_ntlogevent -filter "logfile='application' and eventcode=8194"}).totalseconds
18,0120719
[vPodans] (measure-command {(new-object diagnostics.eventlog("application")).Entries | ? {$_.EventID -eq 8194}}).totalseconds
13,3134996
[vPodans]

Как видим, для Windows Server 2003 более предпочтительным по скорости является WMI (от которого там толку не сильно много), а в Windows Vista - наоборот, .NET работает шустрее. Но из-за проблем с отображением событий журнала Security его польза в Windows Vista весьма сомнительна.

Tuesday, December 09, 2008 10:00:42 PM (FLE Standard Time, UTC+02:00)   Comments [0]    

 

Снова навеяно темой на форуме TechNet-Ru. Уже не первый раз встречаю топики про архивирование журналов событий для последующего хранения в оффлайне. Безусловно не стоит пытаться скопировать .evt файл из папки Windows, поскольку файлы открыты и заблокированы (как и .pst файлы при запущенном MS Outlook). Для архивирования журнала скриптом нужно либо использовать Volume Shadow Copy либо использовать особые методы (например, вот так: http://support.microsoft.com/kb/312571). В качестве особых методов можно так же выделить использование WMI, которое позволяет решить поставленную задачу и добавит нам удалённой управляемости.

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

Задача: проводить с некоторой периодичностью архивацию журнала событий в файл. Убедиться, что бэкап сделан и после этого очистить журналы. В противном случае сделать что-нибудь другое.

За отправную точку возьмём класс Win32_NTEventlogFile. Давайте, вызовем его:

[vPodans] gwmi Win32_NTEventlogFile FileSize LogfileName Name -------- ----------- ---- 8458240 Application C:\Windows\System32\Winevt... 69632 DFS Replication C:\Windows\System32\Winevt... 69632 HardwareEvents C:\Windows\System32\Winevt... 69632 Internet Explorer C:\Windows\System32\Winevt... 69632 Key Management Service C:\Windows\System32\Winevt... 69632 ODiag C:\Windows\System32\Winevt... 1118208 OSession C:\Windows\System32\Winevt... 20975616 System C:\Windows\System32\Winevt... 11603968 Windows PowerShell C:\Windows\System32\Winevt... [vPodans]

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

[vPodans] gwmi Win32_NTEventlogFile | gm -MemberType method TypeName: System.Management.ManagementObject#root\cimv2\Win32_NTEventlogFile Name MemberType Definition ---- ---------- ---------- BackupEventlog Method System.Management.ManagementBaseObject BackupEventlog(System.String ArchiveFi... ChangeSecurityPermissions Method System.Management.ManagementBaseObject ChangeSecurityPermissions(System.Manag... ChangeSecurityPermissionsEx Method System.Management.ManagementBaseObject ChangeSecurityPermissionsEx(System.Man... ClearEventlog Method System.Management.ManagementBaseObject ClearEventlog(System.String ArchiveFil... Compress Method System.Management.ManagementBaseObject Compress() CompressEx Method System.Management.ManagementBaseObject CompressEx(System.String StartFileName... Copy Method System.Management.ManagementBaseObject Copy(System.String FileName) CopyEx Method System.Management.ManagementBaseObject CopyEx(System.String FileName, System.... DeleteEx Method System.Management.ManagementBaseObject DeleteEx(System.String StartFileName) GetEffectivePermission Method System.Management.ManagementBaseObject GetEffectivePermission(System.UInt32 P... Rename Method System.Management.ManagementBaseObject Rename(System.String FileName) TakeOwnerShip Method System.Management.ManagementBaseObject TakeOwnerShip() TakeOwnerShipEx Method System.Management.ManagementBaseObject TakeOwnerShipEx(System.String StartFil... Uncompress Method System.Management.ManagementBaseObject Uncompress() UncompressEx Method System.Management.ManagementBaseObject UncompressEx(System.String StartFileNa... [vPodans]

Здесь нас заинтересует 2 метода - BackupEventlog и ClearEventlog (там ещё есть ChangeSecurityPermissions - я бы не советовал с ним связываться :-D). В упрощённом варианте копирование и удаление будет сводиться к:

$LogName = "Application"
$Eventlog = gwmi Win32_NTEventlogFile -Filter "LogFileName = '$LogName'"
$Eventlog.BackupEventLog("F:\Eventlogs\$env:computername\$LogName" + "_" + "$(Get-Date -Format dd.MM.yyyy).evt")
$EventLog.Clear()

В данном случае с локальной машины заархивируем журнал Application в путь (после преобразований переменных) F:\Eventlogs\THOR\Application_04.12.2008.evt, после чего данный журнал будет очищен. Ничего сложного нету совсем. Разве что кроме одной детали. Такой фокус не получится с журналом Security, поскольку это жрунал аудита. И для очистки журнала потребуется право Manage auditing and security log привилегия SeAudit (в общем смысле это будет административная учётная запись). Поэтому перед использованием метода ClearEventlog нужно подключить эти самые привилегии:

$EventLog.PSBase.Scope.Options.EnablePrivileges = $true

вот и всё. Осталось только решить задачу ротации журналов (удалять все файлы, которые старше 365 дней или 1 года):

dir F:\Eventlogs\$env:computername -Recurse | ? {$_.lastwritetime -gt (Get-Date).AddDays(-365)} | del -Force

В качестве последнего штриха стоит оформить всё в красивый скрипт:

######################################################## # EventLog Backup Manager.ps1 # Version 1.0 # # Eventlog archiving script # # Vadims Podans (c) 2008 # http://www.sysadmins.lv/ ######################################################## function Backup-Eventlog { param ([string]$Computer) Begin { # разово получаем текущую дату в простом формате для пристыковки даты к имени файла $date = Get-Date -Format dd.MM.yyyy } Process { # получение списка всех журналов событий на текущем компьютере $Eventlog = gwmi Win32_NTEventlogFile -ComputerName $Computer $Eventlog | % { # косметическая переменная для вставки имени лога в текстовые сообщения $CurrentName = $_.LogFileName $CurrentLog = $_ # включение SeAudit привилегий $_.PSBase.Scope.Options.EnablePrivileges = $true # задаём путь, куда будут складываться архивы эвентлога $path = "\\BackupServer\Eventlogs\$computer\$CurrentName" + "_" + $date + ".evt" Write-Host "Processing $CurrentName log on $computer .." # непосредственно сам бэкап $Backup = $_.BackupEventLog($path) # проверка статуса бэкапа текущего журнала if ($Backup.ReturnValue -eq 0) { Write-Host "Success" # если бэкап прошёл успешно, то можно удалять записи в журнале и командлетом Out-Null
# подавляем ненужный вывод на экран
$CurrentLog.ClearEventlog() | out-null } else { Write-Warning "Unexpected error occured. Operation aborted" } } } End { # эта часть обеспечивает удаление старых архивов и их ротацию после каждого запуска скрипта # стоит отметить, что данная секция выполняется только один раз и после проведения всей архивации Write-Host "Purging old archives .." dir \\BackupServer\Eventlogs\$computer -Recurse | ? {$_.lastwritetime -gt (Get-Date).AddDays(-365)} | del -Force } }

как бы и всё.

Thursday, December 04, 2008 11:55:32 PM (FLE Standard Time, UTC+02:00)   Comments [8]    

 

Update 28.10.2010: исправлена ошибка с назначением права ManageDocuments.


Вот и пришло время закрыть тему управления принтерами и их списками ACL в PowerShell с использованием WMI. Я в блоге уже расписывал решение частных задач по основным задачам управления принтеров и по управлению их ACL списками. В этом посте я сложу все наработки по этому вопросу в единый концептуальный скрипт, который будет называться PrinterUtils.ps1 с достаточно объёмным набором функций, которые нацелены на упрощение для администраторов автоматизации принтеров с использованием PowerShell. Если кто-то захочет разобраться в работе скрипта и понять используемые приёмы, то предлагаю ознакомиться с следующими ссылками:

В качестве основы я использовал свои предыдущие наработки с SecurityDescriptor в предыдущем блоге, когда разбирал вопрос управления SharePermissions из PowerShell:

Материала у меня на эту тему набралось достаточно много, чтобы проникнуться в идею работы классов WMI и SecurityDescriptor, который не раз пытался посадить меня в лужу :) Однако версия скрипта ShareUtils чётко говорит о том, что скрипт далеко не идеален и не оптимален, имеет свои недостатки, т.к. это был мой первый опыт работы с функциями. Сейчас я значительно переработал структуру работы скрипта (оставив только Core работы с SecurityDescriptor), добавив удалённое управление (в разумных пределах) и, главное (как мне кажется), реализовал работу функций в конвейере. Примеры использования скрипта распишу чуть ниже. Итак, представляю набор функций, которые реализованы в скрипте:

  1. Подключение (маппинг) сетевого принтера к пользователю;
  2. Отключение маппинга сетевого принтера от пользователя;
  3. Получение сведений о принтерах;
  4. Установка принтера по умолчанию;
  5. Установка принтера для общего пользования (расшаривание принтера);
  6. Отмена принтера для общего пользования;

Данная секция представляет собой базовые возможности по управлению принтерами и полностью работоспособна в среде Windows XP/Windows Server 2003. А вот секция управления ACL списками принтеров доступна только в среде Windows Vista/Windows Server 2008. Сюда входят следующие функции:

  1. Получение сведений о правах доступа на конкретный принтер, конкретный принтсервер или по списку компьютеров;
  2. Импорт сведений о правах доступа из внешнего источника. Это может быть и CSV и XML или другой формат;
  3. Добавление пользователя или группы в ACL список принтера или всех принтеров, которые подключены к принтсерверу;
  4. Удаление пользователя или группы из ACL списка принтера или всех принтеров, которые подключены к принтсерверу;
  5. Установка пользователя или группы в ACL список принтера или всех принтеров, которые подключены к принтеру. При этом все имеющиеся права доступа будут удалены и заменены только одним ACE с правом ManagePrinters.

Для этих функций полностью реализована поддержка удалённой работы и работа в конвейере. Синтаксис команд используется примерно следующий:

  • New-NetworkPrinter -Computer <name> -name <name>
    где Computer - имя компьютера, к которому подключён сетевой принтер
    Name - сетевое имя принтера, который следует подключить к пользователю
  • Remove-NetworkPrinter -Name <name>
    где Name - имя примапленного сетевого принтера (не обязательный параметр). Если параметр Name не указан, то будут отключены все примапленные сетевые принтеры.
  • Get-PrinterInfo -Computer <name> -Name <name>
    где Computer - имя компьютера, к которому подключён принтер (не обязательный параметр. Если не указан, то будет использоваться локальная машина),
    Name - имя принтера на удалённой или локальной машине, зависит от предыдущего параметра (не обязательный параметр. Если не указан, то будет выведена краткая справка о всех принтерах указанного компьютера. Если указан, то будет выведена подробная информация о принтере). Вывод данной команды не будет содержать сведений о правах доступа на принтер. 
  • Set-DefaultPrinter -Name <name>
    где Name - имя (или путь) принтера, который должен стать для пользователя принтером по умолчанию.
  • New-PrinterShare -Computer <name> -Name <name> -ShareName <name>
    где Computer - имя компьютера, к которому подключён принтер (не обязательный параметр. Если не указан, будет использоваться локальная машина)
    Name - имя принтера, который требуется предоставить для общего доступа
    ShareName - сетевое имя принтера, т.е. имя, под которым принтер будет виден из сети
  • Remove-PrinterShare -Computer <name> -Name <name>
    где Computer - имя компьютера, к которому подключён принтер (не обязательный параметр. Если не указан, будет использоваться локальная машина)
    Name - имя принтера, для которого необходимо отключить общий доступ (не обязательный параметр. Если не указан, то общий доступ будет отменён для всех расшаренных принтеров на выбранном предыдущим параметром компьютере)
  • Get-Printer -Computer <name> -Name <name>
    где Computer - имя компьютера, к которому подключён принтер (не обязательный параметр. Если не указан, будет использоваться локальная машина)
    Name - имя принтера, для которого следует получить сведения о правах доступа (не обязательный параметр. Если не указан, то будут получены сведения об ACL всех принтеров на выбранном предыдущим параметром компьютере)
    Генерирует на выходе массив объектов с необходимыми сведениями о каждом ACE. Данный массив можно использовать как для изменения прав доступа, так и просто для экспорта во внешний файл.
  • Set-Printer
    не принимает никаких аргументов, а только получает данные по конвейеру. В качестве входных данных должны использоваться объекты, которые по свойствам соответствуют объектам, которые генерирует команда Get-Printer. Команда не может быть в начале строки, а только на выходе конвейера, с которого поступают объекты. Так же по конвейеру можно передавать объекты из внешних файлов (например, CSV, XML)
  • Add-PrinterPermission -User <name> -AceType <name> -AccessMask <name>
    где User - имя пользователя/группы, которого следует добавить в ACL список принтера
    AceType - тип доступа. Может быть Allow или Deny
    AccessMask - маска доступа. Может иметь следующие значения: ManagePrinters, ManageDocuments, Print, TakeOwnership, ReadPermissions, ChangePermissions
    Данная команда так же не может быть в начале строки, а должна находиться на выходе конвейера, с которого поступают объекты. В качестве входных данных должны использоваться объекты, которые по свойствам соответствуют объектам, которые генерирует команда Get-Printer. Команда не может быть в начале строки, а только на выходе конвейера, с которого поступают объекты. Так же по конвейеру можно передавать объекты из внешних файлов (например, CSV, XML)
  • Remove-PrinterPermission -User <name>
    где User - имя пользователя/группы, которого следует удалить из списка ACL принтера
    Данная команда так же не может быть в начале строки, а должна находиться на выходе конвейера, с которого поступают объекты. В качестве входных данных должны использоваться объекты, которые по свойствам соответствуют объектам, которые генерирует команда Get-Printer. Команда не может быть в начале строки, а только на выходе конвейера, с которого поступают объекты. Так же по конвейеру можно передавать объекты из внешних файлов (например, CSV, XML)
  • Set-PrinterPermission -User <name>
    где User - имя пользователя/группы, для которого следует предоставить привилегированный доступ.
    Важно: при использовании команды Set-PrinterPermission следует помнить, что указанный пользователь/группа будут иметь единственный доступ к принтеру с правом ManagePrinters. При этом вероятно, что вы после исполнения команды потеряете доступ к принтеру
    Данная команда так же не может быть в начале строки, а должна находиться на выходе конвейера, с которого поступают объекты. В качестве входных данных должны использоваться объекты, которые по свойствам соответствуют объектам, которые генерирует команда Get-Printer.

    Команда не может быть в начале строки, а только на выходе конвейера, с которого поступают объекты. Так же по конвейеру можно передавать объекты из внешних файлов (например, CSV, XML)

и несколько примеров использования:

Get-Printer PrintSrv "MyPrinter" | export-csv C:\LaserJet.csv - экспортирует ACL списки принтера MyPrinter в CSV файл

Get-Printer PrintSrv | Remove-PrinterPermission Everyone - удаляет группу Everyone из списков ACL всех принтеров сервера PrintSrv

Import-Clixml C:\Printers.xml | Set-Printer - восстанавливает права для принтеров на те, которые содержатся в XML файле. При этом текущие списки ACL указанных в файле принтеров будут полностью перезаписаны списком ACL из файла.

Get-Content C:\Computers.txt | %{Get-printer $_ | Add-Printerpermission NewPrinterWorkers Allow Print} - даёт право печати на всех принтерах, которые подключены к компьютерам из списка computers.txt

New-NetworkPrinter PrintSrv "HP LaserJet 2100" - подключает пользователю в контексте котрого исполняется скрипт сетевой принтер HP LaserJet 2100, который физически подключен к компьютеру PrintSrv

Это далеко не все варианты использования :) Вобщем, я старался создать достаточно широким функционалом, который обычно требуется в скриптах для принтменеджмента. Безусловно, он не охватывает все задачи и в нём реализованы только те функции, которые я посчитал актуальными. А вот, собственно и скрипт с небольшими комментариями по ходу дела:

########################################################
# PrinterUtils.ps1
# Version 0.1.0.0
#
# Functions for advanced printer management
#
# Vadims Podans (c) 2008
# http://www.sysadmins.lv/
########################################################

# внутренняя функция, которая преобразовывает числовой код возврата операции записи ACL
# в текстовое значение.
function _PrinterUtils_Get-Code ($Write) {
    switch ($Write.ReturnValue) {
        "0" {"Success"}
        "2" {"Access Denied"}
        "8" {"Unknown Error"}
        "9" {"The user does not have adequate privileges to execute the method"}
        "21" {"A parameter specified in the method call is invalid"}
        default {"Unknown error $Write.ReturnValue"}
    }
}

# функция получения списка (списков) ACL принтера или всех принтеров
function Get-Printer ($Computer = ".", $name) {
    # Если переменная $name пустая, то возвращается список всех локальных принтеров
    if ($name) {
        $Printers = gwmi Win32_Printer -ComputerName $Computer -Filter "name = '$name'"
    } else {
        $Printers = gwmi Win32_Printer -ComputerName $Computer -Filter "local = '$True'"
    }
    # объявление массива списков ACL
    $PrinterInfo = @()
    # извлечение списка ACL из каждого элемента массива списков ACL
    foreach ($Printer in $Printers) {
        if ($printer) {
            # в переменную $SD получаем дескриптор безопасности для каждого принтера и каждый элемент ACE (DACL)
            # и добавляем в $PrinterInfo
            $SD = $Printer.GetSecurityDescriptor()
            $PrinterInfo += $SD.Descriptor.DACL | %{
                $_ | Select @{e = {$Printer.SystemName}; n = 'Computer'},
                @{e = {$Printer.name}; n = 'Name'},
                AccessMask,
                AceFlags,
                AceType,
                @{e = {$_.trustee.Name}; n = 'User'},
                @{e = {$_.trustee.Domain}; n = 'Domain'},
                @{e = {$_.trustee.SIDString}; n = 'SID'}
            }
        } else {
            Write-Warning "Specified printer not found!"
        }
    }
    # выдача сведений об ACL на выход функции для последующей подачи на конвейер
    $PrinterInfo
}

# функция записи в ACL принтера. Она не принимает никаких аргументов,
# а только принимает данные с конвейера
function Set-Printer {
    # по конвейеру получаем массив ACE из внешнего источника
    $PrinterInfo = @($Input)
    # расшиваем полученный массив по имени принтера и дальше по циклу подаём на
    # обработку только ACL одного принтера
    $PrinterInfo | Select -Unique Computer, Name | % {
        $Computer = $_.Computer
        $name = $_.name
        # создаём новые объекты необходимых классов
        $SD = ([WMIClass] "Win32_SecurityDescriptor").CreateInstance()
        $ace = ([WMIClass] "Win32_Ace").CreateInstance()
        $Trustee = ([WMIClass] "Win32_Trustee").CreateInstance()
        # теперь расшиваем каждый ACE уже отфильтрованного списка ACL из PrinterInfo и
        # заполняем форму SecurityDescriptor
        $PrinterInfo | ? {$_.Computer -eq $Computer -and $_.name -eq $name} | % {
            $SID = new-object security.principal.securityidentifier($_.SID)
            [byte[]] $SIDArray = ,0 * $SID.BinaryLength
            $SID.GetBinaryForm($SIDArray,0)
            $Trustee.Name = $_.user
            $Trustee.SID = $SIDArray
            $ace.AccessMask = $_.AccessMask
            $ace.AceType = $_.AceType
            $ace.AceFlags = $_.AceFlags
            $ace.trustee = $Trustee
            # набор ACE поэтапно добавляем в DACL дескриптора безопасности
            $SD.DACL += @($ace.psobject.baseobject)
            # устанавливаем флаг SE_DACL_PRESENT, что будет говорить о том, что мы изменяем
            # только DACL и ничего более
            $SD.ControlFlags = 0x0004
        }
        # когда полный список ACL для текущего принтера собран, выбираем имя текущего принтера
        $Printer = gwmi Win32_Printer -ComputerName $Computer -Filter "name = '$name'"
        # проверяется, что принтер для записи ACL найден и производится запись.
        # В противном случае запись ACL пропускается
        if ($Printer) {
            $Write = $Printer.SetSecurityDescriptor($SD)
            Write-Host "Processing current printer: $name"
            _PrinterUtils_Get-Code $Write
        } else {
            Write-Warning "Skipping non-present printer: $name"
        }
    }
}

# внутренняя функция, которая только формирует объект пользователя с набором прав
# и возвращает объект в вызывающую функцию для последующих преобразований
function _Create-SDObject ( $user, $AceType, $AccessMask) {
    # преобразование текстового вида прав в числовые значения
    $masks = @{ManagePrinters = 983052; ManageDocuments = 983088; Print = 131080;
        TakeOwnership = 524288; ReadPermissions = 131072; ChangePermissions = 262144}
    $types = @{Allow = 0; Deny = 1}
    # создание необходимых свойств для объекта. Для поддержки удалённого управления
    # было добавлено свойство Computer, которое будет принимать от Get-Printer аналогичное
    # значение. Тем самым обеспечивается сквозная трансляция имени компьютера, где
    # подключен принтер, по конвейеру для последующей записи
    $AddInfo = New-Object System.Management.Automation.PSObject
    $AddInfo | Add-Member NoteProperty Computer ([PSObject]$null)
    $AddInfo | Add-Member NoteProperty Name ([PSObject]$null)
    $AddInfo | Add-Member NoteProperty AccessMask ([uint32]$null)
    $AddInfo | Add-Member NoteProperty AceFlags ([uint32]$null)
    $AddInfo | Add-Member NoteProperty AceType ([uint32]$null)
    $AddInfo | Add-Member NoteProperty User ([PSObject]$null)
    $AddInfo | Add-Member NoteProperty Domain ([PSObject]$null)
    $AddInfo | Add-Member NoteProperty SID ([PSObject]$null)
    # заполнение объекта данными, которые были указаны в качестве аргументов вызова функции и возврат
    # объекта в вызывающую функцию
    $AddInfo.Name = $name
    $AddInfo.User = $user
    $AddInfo.SID = (new-object security.principal.ntaccount $user).translate([security.principal.securityidentifier])
    $AddInfo.AccessMask = $masks.$AccessMask
    $AddInfo.AceType = $types.$AceType
    if ($masks.$AccessMask -eq 983088) {$AddInfo.AceFlags = 9}
    $AddInfo
}

# функция для установки разрешений на принтер. При её использовании, текущий ACL очищается
# от всех записей и устанавливается только один ползователь/группа с правом ManagePrinters
function Set-PrinterPermission ($user) {
    # принимаются данные с конвейера
    $PrinterInfo = @($Input)
    $AddInfo = _Create-SDObject $user Allow ManagePrinters
    # в этом цикле перебираются по именам все имена принтеров и для каждого из них
    # записывается указанный в аргументах пользователь с удалением текущих ACE из ACL принтера
    # это видно по тому, что никакая часть $PrinterInfo не передаётся по конвейеру на запись
    foreach ($Printer in ($PrinterInfo | select -Unique Computer, Name)) {
        $AddInfo.Computer = $Printer.Computer
        $AddInfo.Name = $Printer.name
        $AddInfo | Set-Printer
    }
}

# функция добавления пользователя/группу в имеющийся список ACL принтера. Основное отличие от
# предыдущего варианта, что для каждого принтера ACE не устанавливается, а добавляется
function Add-PrinterPermission ($user, $AceType, $AccessMask) {
    $PrinterInfo = @($Input)
    $AddInfo = _Create-SDObject $user $AceType $AccessMask
    foreach ($Printer in ($PrinterInfo | select -Unique Computer, Name)) {
        $AddInfo.Name = $Printer.name
        $AddInfo.Computer = $Printer.Computer
        # вот этой строкой мы из списка всех принтеров итеративно перебираем каждый принтер
        $PrinterInfoNew = $PrinterInfo | ?{$_.name -eq $Printer.name}
        # и в хвост списка ACL добавляем новый ACE
        $PrinterInfoNew += $AddInfo
        # и подаём на запись
        $PrinterInfoNew | Set-Printer
    }
}

# функция для удаления ACE пользователя/группы из ACL
function Remove-PrinterPermission ($user) {
    $Printers = @($Input)
    # просто берём списки ACL, которые пришли по конвейеру и выкидываем оттуда все ACE,
    # в которых фигурирует указанный в аргументах пользователь/группа и записывем ACE обратно в ACL
    $printers | ? {$_.user -ne $user} | Set-Printer
}

function New-NetworkPrinter ($Computer, $name) {
    ([wmiclass]'Win32_Printer').AddPrinterConnection("\\$Computer\$name")
}

function Remove-NetworkPrinter ($name) {
    if ($name) {
        (gwmi Win32_Printer -Filter "sharename='$name'").delete()
    } else {
        (gwmi Win32_Printer -Filter "local='$false'").delete()
    }
}

function Set-DefaultPrinter ($name) {
    if (!$name) {
        Write-Warning "You must to specify printer name. Operation aborted!"
    } else {
        if (gwmi win32_Printer -Filter "name='$name'") {
            $SetDefault = (gwmi win32_Printer -Filter "name='$name'").SetDefaultPrinter()
            switch ($SetDefault.ReturnValue) {
                "0" {Write-Host "Now your default printer is $name"}
                default {Write-Warning "Some error occur"}
            }
        } else {
            Write-Warning "Specified printer not exist!"
        }
    }
}

function Get-PrinterInfo ($Computer = ".", $name) {
    # здесь я предлагаю получить как полный набор свойств, так и упрощённый вывод сведений. 
    if ($name) {
        gwmi Win32_Printer -ComputerName $Computer -Filter "name='$name'" | select *
    } else {
        gwmi Win32_Printer -ComputerName $Computer
    }
}

function New-PrinterShare ($Computer = ".", $name, $ShareName) {
    $Printer = gwmi win32_Printer -ComputerName $Computer -Filter "name='$name'"
    if ($Printer) {
        $Printer.shared = $True
        $Printer.ShareName = $ShareName
        $Printer.put()
    } else {
        Write-Warning "Specified printer not exist!"
    }
}

function Remove-PrinterShare ($Computer = ".", $name) {
    if ($name) {
        $filter = "name = '$name'"
    } else {
        $filter = "local = '$false'"
    }
    gwmi Win32_Printer -ComputerName $Computer -Filter $filter | % {
        $_.shared = $false
        $_.put()
    }
}

многовато, конечно же, но ничего космически сложного для разбора я тут не вижу. Главное - чёткое понимание структуры SecurityDescriptor и хотя бы базовые навыки работы с ним.

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

Даже не знаю, что ещё добавить сюда. Вроде все вопросы разобрал ранее, сейчас просто это всё собрал воедино. Вобщем, как обычно, если есть вопросы, замечания - кнопка Comments вам в помощь ;)

PowerShell |  ACL |  WMI
Saturday, November 22, 2008 8:11:28 PM (FLE Standard Time, UTC+02:00)   Comments [3]    

 

Previous Page Page 2 of 4 in the PowerShellWMI category Next Page
 · 

All content © 2008 - 2012, Vadims Podāns
"Spaces" Theme provided by: Vadims Podāns
About


E-mail - Send mail to the author(s)
Live Messenger -
For english language visitors
Библиотека
Календарик
<February 2012>
SunMonTueWedThuFriSat
2930311234
567891011
12131415161718
19202122232425
26272829123
45678910

Карта расположения посетителей
Favorites





Disclaimer
Вся информация на сайте предоставляется на условиях «как есть», без предоставления каких-либо гарантий и прав.

При использовании материалов c данного сайта ссылка на оригинальный источник обязательна.
Protected by Copyscape Online Plagiarism Scanner