По просьбе читателей, а так же с учётом востребованности (судя по сообщениям форумов и ньюсгрупп) я нашёл время переписать скрипт ShareUtils.ps1 с поддержкой работы с удалёнными машинами и попутно пофиксив недочёты, которые были найдены за время эксплуатации предыдущей версии скрипта. Предыдущая версия опубликована здесь: Управление безопасностью общих папок (сетевых шар) в PowerShell (часть 4)
Технический функционал изменился только возможностью работы с удалёнными компьютерами, но синтаксис был изменён (а так же удалены лишние функции) по аналогии с PrinterUtils и имеет примерно следующий вид:
- New-Share –Computer <Computer> –Name <Name> –Path <Path> –Description <Description>
где Computer – имя или IP адрес компьютера, на котором необходимо расшарить папку. (не обязательный параметр). Если не указан, используется текущий компьютер.
Name - сетевое имя для папки;
Path - путь к физической папке;
Description описание к сетевой папке. При наличии пробелов - заключить в кавычки (не обязательный параметр);
- Remove-Share –Computer <Computer> –Name <Name> – отменяет расшаривание на папке. Сама папка не удаляется.
где Computer – имя или IP адрес компьютера, на котором нужно отменить расшаривание папки. (не обязательный параметр). Если не указан, используется текущий компьютер.
Name - сетевое имя папки;
- Get-Share –Computer <Computer> –Name <Name> – получает основные сведения и списки DACL Share Permissions с указанных или всех сетевых папок.
где Computer – имя или IP адрес компьютера, с которого нужно получить сведения о сетевых папках. (не обязательный параметр). Если не указан, используется текущий компьютер.
Name - имя сетевой папки (не обязательный параметр). Если не указан, то выбираются все сетевые папки с типом Disk Drive (в которые системные шары не входят).
- Set-SharePermission –User <User> –AceType <AceType> –AccessMask <AccessMask> – устанавливает единственный Share Permission ACE для указанного в аргументах пользователя.
User - имя пользователя/группы, которой предоставляется доступ;
AceType - тип доступа. Этот параметр должен иметь одно из значений Allow/Deny;
AccessMask - маска доступа. Этот параметр должен иметь одно из значений FullControl/Change/Read;
Функция не может быть вначале строки, а только после конвейера Get-Share или другого источника с подходящими данными (например, если данные были сохранены в CSV/XML файле, то их можно использовать в качестве источника: Import-Csv path.csv | Set-SharePermission Everyone Allow Change). При этом все текущие права на сетевую папку будут удалены и записан только указанный в аргументах пользователь/группа.
- Add-SharePermission –User <User> –AceType <AceType> –AccessMask <AccessMask> – добавляет указанного в аргументах пользователя к Share Permissions выбранной сетевой папки (или папок)
User - имя пользователя/группы, которой предоставляется доступ;
AceType - тип доступа. Этот параметр должен иметь одно из значений Allow/Deny;
AccessMask - маска доступа. Этот параметр должен иметь одно из значений FullControl/Change/Read;
Функция не может быть вначале строки, а только после конвейера Get-Share или другого источника с подходящими данными (например, если данные были сохранены в CSV/XML файле, то их можно использовать в качестве источника: Import-Csv path.csv | Add-SharePermission Everyone Allow Change).
- Remove-SharePermission –User <User> – удаляет указанного пользователя из DACL выбранной сетевой папки (папок). Не может быть вначале строки, а только на выходе конвейера, откуда поступают объекты сетевых папок. Например, Get-Share | Remove-SharePermission Everyone – удалит группу Everyone из всех SharePermissions всех расшаренных папок на локальном компьютере. Разрешения NTFS при этом не изменяются.
где User - имя пользователя/группы, которого следует удалить из ACL сетевой папки.
Примеры использования практически идентичные, как и в 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
}
}