Contents of this directory is archived and no longer updated.

Примечание: данный пост перепечатан в связи с закрытием бложиков на spaces.live.com, как имеющий какую-то ценность для автора и/или читателей.


В предыдущей части мы рассмотрели чтение Share Permissions, их редактирование и удаление ACE из полного списка ACL. Здесь осталось рассмотреть вопрос добавления участников безопасности в DACL сетевой шары. Этот процесс, к сожалению, не такой и простой, как может показаться, но тем не менее его тоже нужно решать. Для решения этой задачи нам нужно создать такой же объект с такими же свойствами как и содержимое $ShareInfo. Давайте посмотрим, какими свойствами обладают элементы массива $ShareInfo:

[C:\] $Shareinfo[0] | gm


   TypeName: Selected.System.Management.ManagementBaseObject

Name        MemberType   Definition
----        ----------   ----------
Equals      Method       bool Equals(System.Object obj)
GetHashCode Method       int GetHashCode()
GetType     Method       type GetType()
ToString    Method       string ToString()
AccessMask  NoteProperty System.Int32 AccessMask=1179817
AceFlags    NoteProperty System.UInt32 AceFlags=0
AceType     NoteProperty System.UInt32 AceType=0
Description NoteProperty System.String Description=
Domain      NoteProperty System.String Domain=CONTOSO
Name        NoteProperty System.String Name=UserShare
Path        NoteProperty System.String Path=C:\Test
SID         NoteProperty System.String SID=S-1-5-21-3709200118-438321133-4282490648-513
User        NoteProperty System.String User=Domain Users

Нас тут будут интересовать только NoteProperty. Давайте теперь исходя из этих данных создадим свой объект, который будет обладать вот этими свойствами. Тип объекта будет такой же - System.Management.Automation.PSObject  (без Custom). Новый объект создаётся командной New-Object, а члены объекта создаются командой Add-Member:

# создаём новый объект с типом System.Management.Automation.PSObject 
$AddInfo = new-object System.Management.Automation.PSObject 
# добавляем по очереди членов NoteProperty объекта как и в исходном варианте 
$AddInfo | add-member NoteProperty Name  ([PSObject]$null) 
$AddInfo | add-member NoteProperty Path  ([PSObject]$null) 
$AddInfo | add-member NoteProperty Description  ([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)

Теперь можно посмотреть на результаты нашей работы:

[C:\] $AddInfo


Name        :
Path        :
Description :
AccessMask  : 0
AceFlags    : 0
AceType     : 0
User        :
Domain      :
SID         :

Ну что ж, уже лучше, теперь можно заполнять эти поля в соответствии с нашими требованиями. Но чтобы не заполнять все поля вручную, предполагается, что для добавления нового участника безопасности в Share Permissions пользователь укажет только имя сетевой шары, имя пользователя, маску доступа и тип доступа (Allow/Deny). Поэтому нам потребуется вытащить информацию о текущей шаре (которая общая для всей шары, как имя, путь, описание и т.д.) и передать эти значения в новую переменную $AddInfo. Остальную часть информации мы обработаем на основании уже переданных параметров и запишем оставшиеся поля переменной $AddInfo, которая в конечном итоге будет содержать всю необходимую информацию. Т.к. уже неоднократно говорилось в предыдущих частях, что метод SetShareInfo перезаписывает полностью информацию о сетевой шаре, поэтому для сохранения существующих ACE мы произведём уже известным способом чтение существующих DACL и сделаем инкремент (добавим к существующим DACL нами созданный DACL). Когда все данные будут скомпонованы мы произведём запись обновлённого списка DACL в ACL шары. Итак, поехали:

# принимаем вводные параметры от пользователя из командной строки 
param ($share, $user, $AccessMask, $AceType) 
# предполагается, что данный скрипт выполняет только добавление участников безопасности 
# поэтому сразу создаём новый объект с необходимыми членами и указанием типа принимаемых 
# данных 
$AddInfo = New-Object System.Management.Automation.PSObject 
$AddInfo | Add-Member NoteProperty Name  ([PSObject]) 
$AddInfo | Add-Member NoteProperty Path  ([PSObject]) 
$AddInfo | Add-Member NoteProperty Description  ([PSObject]) 
$AddInfo | Add-Member NoteProperty AccessMask  ([uint32]) 
$AddInfo | Add-Member NoteProperty AceFlags  ([uint32]) 
$AddInfo | Add-Member NoteProperty AceType  ([uint32]) 
$AddInfo | Add-Member NoteProperty User  ([PSObject]) 
$AddInfo | Add-Member NoteProperty Domain  ([PSObject]) 
$AddInfo | Add-Member NoteProperty SID  ([PSObject]) 
# зная имя сетевой шары делаем её поиск и копируем информацию о имени, пути 
# и описании (поле Description) и выставим прочие параметры 
$CustomShare = Get-WmiObject Win32_Share -filter "name = '$share'" 
$AddInfo.Name = $share 
$AddInfo.Path = $CustomShare.Path 
$AddInfo.Description = $CustomShare.Description 
$AddInfo.Domain = $null 
$AddInfo.AceFlags = 3 
# далее заполняется информация о пользователе и его доступе, поэтому 
# дальше мы ничего не копируем, а обрабатываем уже переданные параметры: 
$AddInfo.User = $user 
# преобразовываем маску доступа из текстовой в численный формат с использованием 
# конструкции Switch.
switch ($AccessMask) {
    "Full"   {$AddInfo.AccessMask = 2032127}
    "Change" {$AddInfo.AccessMask = 1245631}
    "Read"   {$AddInfo.AccessMask = 1179817}
} 
# таким же образом обрабатываем и переменную $AceType: 
switch ($AceType) {
    "Allow"   {$AddInfo.AceType = 0}
    "Deny" {$AddInfo.AceType = 1}
}
# заполняем последнее поле SID путём трансляции имени пользователя/группы в его SID
$AddInfo.SID = (new-object security.principal.ntaccount $user).translate([security.principal.securityidentifier])
# теперь считываем текущий список DACL с указанной сетевой шары в переменную $ShareInfo
$shares = Get-WmiObject Win32_Share -filter "name='$share'"
$Shareinfo = @()
foreach ($share in $shares) {
    $shareSec = Get-WmiObject Win32_LogicalShareSecuritySetting  -filter "name='$($share.name)'"
    if($shareSec) {
        $sd = $sharesec.GetSecurityDescriptor()
        $ShareInfo += $sd.Descriptor.DACL | ForEach-Object {
            $_ | select @{e={$share.name};n='Name'},
            @{e={$share.Path};n='Path'},
            @{e={$share.Description};n='Description'},
            AccessMask,
            AceFlags,
            AceType,
            @{e={$_.trustee.Name};n='User'},
            @{e={$_.trustee.Domain};n='Domain'},
            @{e={$_.trustee.SIDString};n='SID'}
        }
    }
}
# теперь делаем добавление (инкремент) созданного нами массива объектов с зполненными полями к 
# существующему массиву объектов $ShareInfo 
$ShareInfo += $AddInfo 
# Можно для верности убедиться, что $ShareInfo обладает всей необходимой информацией, которую 
# теперь можно записать в шару. В принципе, эта строчка несёт в себе лишь отладочную информацию 
# и когда отладка будет завершена эту строчку можно будет удалить или закомментировать для 
# отладки скрипта в будущем. 
$ShareInfo 
# Если всё в порядке, то можно перезаписывать эти данные в DACL указанной шары: 
$ShareInfo | select -unique name, Path, Description | ForEach-Object { 
    $name = $_.name 
    $path = $_.Path 
    $description = $_.Description 
    "Processing : $name $path $description" 
    $SD = ([WMIClass] "Win32_SecurityDescriptor").CreateInstance() 
    $ace = ([WMIClass] "Win32_Ace").CreateInstance() 
    $Trustee = ([WMIClass] "Win32_Trustee").CreateInstance() 
    $sd.DACL = @() 
    $ShareInfo | where {$_.name -eq $name} | ForEach-Object { 
        $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
        $sd.DACL += $ACE.psObject.baseobject
    } 
    $inParams = $CustomShare.psbase.GetMethodParameters("SetShareInfo") 
    $inParams.Access = $SD 
    $CustomShare.psbase.invokemethod("setshareinfo", $inParams, $null) 
}

Формат запуска данного скрипта из командной строки CMD или меню Run будет следующим:

powershell %path%\AddUser.ps1 -share "Имя сетевой шары" -user "имя добавляемой группы" -mask "маска доступа, Full/Change/Read" -type "тип доступа, Allow/Deny"

эту команду следует выполнять в одну строчку. В качестве переменной %path% нужно указать путь к папке со скриптом, если он заранее не добавлен в системную переменную %path%. В качестве маски доступа нужно указать одно из 3-х значений, которое может быть Full, Change или Read. Более одного параметра указывать нельзя. Ну и в качестве типа доступа указать либо Allow, что даст доступ, либо Deny, что явно запретит доступ.

Ну вот как бы и всё на данном этапе. Здесь я много чего не пояснял, т.к. достаточно (в моём понимании) разобрал в предыдущих 3-х частях о работе с сетевыми шарами в PowerShell. Но это ещё не всё. Главный девиз PowerShell - быть удобным для использования и кратким для написания (это я сам придумал :-) ), однако при работе с сетевыми шарами это совершенно не прослеживается и даже может создаться впечатление громоздкости (хотя тот, кто считает этот код громоздким может написать скрипт короче на VBS с использованием только WMI/.NET :) ), поэтому в следующей части я постараюсь исправить сей момент путём написания единого (относительно компактного по возможности) и представить его как готовое решение, которое в работе будет действительно удобным. Не отключайтесь, продолжение обязательно будет :)

p.s. Данный скрипт не обязательно является самым простым решением, т.к. возможно, что данную операцию можно провести более удобным и изящным способом (хотя, после чтения документации на MSDN мне так не кажется, что это возможно), но в любом случае адекватные замечания/поправки/дополнения к этому скрипту всячески приветствуются.


Share this article:

Comments:

Comments are closed.