Примечание: данный пост перепечатан в связи с закрытием бложиков на 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 мне так не кажется, что это возможно), но в любом случае адекватные замечания/поправки/дополнения к этому скрипту всячески приветствуются.
Comments: