Contents of this directory is archived and no longer updated.

По просьбе читателей, а так же с учётом востребованности (судя по сообщениям форумов и ньюсгрупп) я нашёл время переписать скрипт ShareUtils.ps1 с поддержкой работы с удалёнными машинами и попутно пофиксив недочёты, которые были найдены за время эксплуатации предыдущей версии скрипта. Предыдущая версия опубликована здесь: Управление безопасностью общих папок (сетевых шар) в PowerShell (часть 4)

Технический функционал изменился только возможностью работы с удалёнными компьютерами, но синтаксис был изменён (а так же удалены лишние функции) по аналогии с PrinterUtils и имеет примерно следующий вид:

  1. New-Share –Computer <Computer> –Name <Name> –Path <Path> –Description <Description>
    где Computer – имя или IP адрес компьютера, на котором необходимо расшарить папку. (не обязательный параметр). Если не указан, используется текущий компьютер.
    Name - сетевое имя для папки;
    Path - путь к физической папке;
    Description описание к сетевой папке. При наличии пробелов -  заключить в кавычки (не обязательный параметр);
  2. Remove-Share –Computer <Computer> –Name <Name> – отменяет расшаривание на папке. Сама папка не удаляется.
    где Computer – имя или IP адрес компьютера, на котором нужно отменить расшаривание папки. (не обязательный параметр). Если не указан, используется текущий компьютер.
    Name - сетевое имя папки;
  3. Get-Share –Computer <Computer> –Name <Name> – получает основные сведения и списки DACL Share Permissions с указанных или всех сетевых папок.
    где Computer – имя или IP адрес компьютера, с которого нужно получить сведения о сетевых папках. (не обязательный параметр). Если не указан, используется текущий компьютер.
    Name - имя сетевой папки (не обязательный параметр). Если не указан, то выбираются все сетевые папки с типом Disk Drive (в которые системные шары не входят).
  4. 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). При этом все текущие права на сетевую папку будут удалены и записан только указанный в аргументах пользователь/группа.
  5. 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).
  6. 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
    }
}

Share this article:

Comments:

Фарид

Привет. Классный модуль, но у меня возникла небольшая проблема. У вновь созданного пользователя параметр User в cmdlet *-SharePermission не принимается. Можно как-нибудь изменить, чтобы мог приниматься SID? Я новичок в PS и пока самостоятельно подредактировать модуль не могу - не хватает знаний). Может быть есть альтернативные решения проблемы?

Dmitriy

Доброго времени суток. При расшаривании папки на удаленном компьютере командой new-share hostname temp$ c:\temp | add-sharepermission username allow FullControl | set-share шара образуется, но вот "Передельное число пользователей" устанавливается в 0. Поэтому хоть шара и есть, но зайти в неё нельзя. Какой командой можно устанавливать число возможных подключений к шаре?

Vadims Podāns

в функции New-Share после строчки: $AddInfo = _Create-SDObject $user.Value Allow Read добавить другую строчку: $AddInfo.AllowMaximum = $true тогда должно заработать.

Comments are closed.