По просьбе читателей, а так же с учётом востребованности (судя по сообщениям форумов и ньюсгрупп) я нашёл время переписать скрипт ShareUtils.ps1 с поддержкой работы с удалёнными машинами и попутно пофиксив недочёты, которые были найдены за время эксплуатации предыдущей версии скрипта. Предыдущая версия опубликована здесь: Управление безопасностью общих папок (сетевых шар) в PowerShell (часть 4)
Технический функционал изменился только возможностью работы с удалёнными компьютерами, но синтаксис был изменён (а так же удалены лишние функции) по аналогии с PrinterUtils и имеет примерно следующий вид:
Примеры использования практически идентичные, как и в PrinterUtils: http://www.sysadmins.lv/PermaLink,guid,22c0550d-0c46-44ca-97ce-2b0bccbb51de.aspx
И, собственно, сам код:
######################################################## # ShareUtils.ps1 # Version 0.9 # # Functions for advanced share management # # Note: # Previous version is published at my former blog: # http://vpodans.spaces.live.com/blog/cns!BB1419A2CFC1E008!188.entry # # Vadims Podans (c) 2009 # http://www.sysadmins.lv/ ######################################################## # внутренняя функция, которая преобразовывает числовой код возврата операции записи DACL # в текстовое значение. function _ShareUtils_Get-Code ($write) { switch ($write.ReturnValue) { "0" {"Success"} "2" {"Access Denied"} "8" {"Unknown Failure"} "9" {"Invalid Name"} "21" {"Invalid Parameter"} "22" {"Duplicate Share"} "23" {"Redirected Path"} "24" {"Unknown Device or Directory"} "25" {"Net Name Not Found"} default {"Unknown error $write.ReturnValue"} } } # функция для извлечения сведений и DACL с существующих сетевых папок. # обязательна для использования функций Add-SharePermission и Set-SharePermission # если компьютер не указан, то используется текущий. Если имя сетевой паки не указано, # то возвращается список сведений и DACL всех сетевых папок на локальном или удалённом компьютере function Get-Share ($computer = ".", $name) { if ($name) { $shares = gwmi Win32_Share -ComputerName $computer -Filter "name = '$name'" } else { $shares = gwmi Win32_Share -ComputerName $computer -Filter "type = 0" } $ShareInfo = @() foreach ($share in $shares) { $ShareSec = gwmi Win32_LogicalShareSecuritySetting -ComputerName $computer -filter "name='$($share.name)'" if ($shareSec) { $SD = $sharesec.GetSecurityDescriptor() $ShareInfo += $SD.Descriptor.DACL | % { $_ | select @{e={$share.ClassPath.Server};n='Computer'}, @{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'} } } else { Write-Warning "Specified share not exist or you may not have sufficient rights to access them!" } } $ShareInfo } # функция записи обновлённых сведений в сетевые папки. Не может быть первой в строке, а только после # конвейера, откуда поступают данные для записи. Если папка не расшарена, то скрипт её расшарит # автоматически и запишет необходимые сведения о сетевой папке. function Set-Share { $ShareInfo = @($input) $ShareInfo | select -unique Computer, Name, Path, Description | % { $Computer = $_.Computer $name = $_.name $SD = ([WMIClass] "Win32_SecurityDescriptor").CreateInstance() $ace = ([WMIClass] "Win32_Ace").CreateInstance() $Trustee = ([WMIClass] "Win32_Trustee").CreateInstance() $sd.DACL = @() $ShareInfo | ? {$_.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 $SD.DACL += $ace.psObject.baseobject } # проверяется наличие расшаренной папки. Если папка есть, то в неё записывается только SecurityDescriptor # в противном случае она расшаривается и в неё производится полная запись всех данных $share = gwmi Win32_Share -ComputerName $computer -Filter "name = '$name'" if ($share) { $inParams = $share.psbase.GetMethodParameters("SetShareInfo") $inParams.Access = $SD $write = $share.psbase.invokemethod("SetShareInfo", $inParams, $null) Write-Host "Setting DACL on current share: $name on server $computer" -ForegroundColor green _ShareUtils_Get-Code $Write } else { $shareobject = [wmiClass]"\\$computer\root\cimv2:win32_Share" $inParams = $shareobject.psbase.GetMethodParameters("Create") $inParams.name = $_.name $inParams.path = $_.path $inParams.Description = $_.Description $inParams.Type = 0 $inParams.Access = $SD $write = $shareobject.psbase.invokemethod("Create", $inParams, $null) Write-Host "Processing current share: $name on server $computer" -ForegroundColor green _ShareUtils_Get-Code $Write } } } function _Create-SDObject ($user, $AceType, $AccessMask) { # преобразование текстового вида прав в числовые значения $masks = @{FullControl = 2032127; Change = 1245631; Read = 1179817} $types = @{Allow = 0; Deny = 1} # создание необходимых свойств для объекта. Для поддержки удалённого управления # было добавлено свойство Computer, которое будет принимать от Get-Share аналогичное # значение. Тем самым обеспечивается сквозная трансляция имени компьютера, где # находится сетевая папка, по конвейеру для последующей записи $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 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) # заполнение объекта данными, которые были указаны в качестве аргументов вызова функции и возврат # объекта в вызывающую функцию $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 $AddInfo } function Set-SharePermission ($user, $AceType, $AccessMask) { # принимаются данные с конвейера $ShareInfo = @($input) $AddInfo = _Create-SDObject $user $AceType $AccessMask # в этом цикле перебираются по именам все имена расшаренных папок и для каждой из них # записывается указанный в аргументах пользователь с удалением текущих ACE из ACL шары # это видно по тому, что никакая часть $ShareInfo не передаётся по конвейеру на запись foreach ($share in ($ShareInfo | select -Unique Computer, Name)) { $AddInfo.Computer = $share.Computer $AddInfo.Name = $share.name $AddInfo.Description = $Share.Description $AddInfo | Set-Share } } # просто добавляет нового участника безопасности к текущему DACL расшаренной папки. # NTFS Acl не изменяется. function Add-SharePermission ($user, $AceType, $AccessMask) { $ShareInfo = @($input); $ShareInfoNew = @() $AddInfo = _Create-SDObject $user $AceType $AccessMask foreach ($Share in ($ShareInfo | select -Unique Computer, Name)) { $AddInfo.Name = $Share.name $AddInfo.Computer = $Share.Computer $AddInfo.Description = $Share.Description # вот этой строкой мы из списка всех сетевых папок итеративно перебираем каждую шару $ShareInfoNew = @($ShareInfo | ?{$_.name -eq $Share.name}) # в хвост списка ACL каждой сетевой шары добавляем новый ACE $ShareInfoNew += $AddInfo # и подаём на запись $ShareInfoNew | Set-Share } } # основная функция для удаления единичного ACE из ACL сетевой папки. Процесс сводится к извлечению # текущего списка (или списков) ACL и фильтрации ACE в этом списке по методу Not Equal. Всё, что не подпадает под # это действие записываются обратно в переменную, а всё, что подпало (указанный пользователь) обратно # в переменную $ShareInfo не записывается. function Remove-SharePermission ($user) { $shares = @($input) # просто берём списки ACL, которые пришли по конвейеру и выкидываем оттуда все ACE, # в которых фигурирует указанный в аргументах пользователь/группа и записывем ACE обратно в ACL $shares | ? {$_.user -ne $user} | Set-Share } # основная функция для создания новых сетевых папок на локальном компьютере. Здесь я использую упрощённый # вариант создания сетевой папки, но учитывая один большой нюанс я добавил одно действие. Суть проблемы # изложена тут: http://vpodans.spaces.live.com/blog/cns!BB1419A2CFC1E008!170.entry# поэтому при создании новой сетевой папки я вручную создаю с нуля список ACL, который содержит # только группу Everyone и с правом Allow Read. function New-Share ($computer = $env:COMPUTERNAME, $name, $path, $Description) { $user = (new-object security.principal.securityidentifier "S-1-1-0").translate([security.principal.ntaccount]) $AddInfo = _Create-SDObject $user.Value Allow Read $AddInfo.Computer = $computer $AddInfo.Path = $path $AddInfo.Description = $Description $AddInfo | Set-Share } # отменяет расшаривание сетевой папки. Сама же физическая папка не изменяется. function Remove-Share ($computer = ".", $name) { $share = gwmi Win32_Share -ComputerName $computer -Filter "name = '$name'" if (!$share) { Write-Warning "Specified network share doesn't exist!" } else { $write = $share.delete() Write-Host "Deleting network share $name on computer $computer:" _ShareUtils_Get-Code $write } }
Привет. Классный модуль, но у меня возникла небольшая проблема. У вновь созданного пользователя параметр User в cmdlet *-SharePermission не принимается. Можно как-нибудь изменить, чтобы мог приниматься SID? Я новичок в PS и пока самостоятельно подредактировать модуль не могу - не хватает знаний). Может быть есть альтернативные решения проблемы?
Доброго времени суток. При расшаривании папки на удаленном компьютере командой new-share hostname temp$ c:\temp | add-sharepermission username allow FullControl | set-share шара образуется, но вот "Передельное число пользователей" устанавливается в 0. Поэтому хоть шара и есть, но зайти в неё нельзя. Какой командой можно устанавливать число возможных подключений к шаре?
в функции New-Share после строчки: $AddInfo = _Create-SDObject $user.Value Allow Read добавить другую строчку: $AddInfo.AllowMaximum = $true тогда должно заработать.
Comments: