Contents of this directory is archived and no longer updated.

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

Примечание: существует обновлённый вариант этого поста с модернизированным скриптом: Управление безопасностью общих папок (сетевых шар) в PowerShell (часть 5)


Ну что ж, настало время подвести итоги по материалу управления сетевыми шарами и управлению доступа к ним. Как и обещал, я написал скрипт, который выполнен в виде функций при помощи которого можно действительно легко и просто управлять самими сетевыми папками и их безопасностью из PowerShell. Интеграция данного скрипта в профиль PowerShell позволяет использовать уже готовые функции как простые командлеты. Я считаю, это действительно большим плюсом, т.к. до этого администратору для решения этих задач приходилось писать длиннющий код в каждом скрипте, который касался безопасности сетевых шар. Итак, какая же функциональность заложена в данный скрипт:

  1. создание сетевой папки;
  2. удаление сетевой папки;
  3. получения перечня всех сетевых папок на сервере с выводом необходимой информации о них;
  4. установка ACL сетевой папки;
  5. добавление ACE к существующему ACL сетевой папки;
  6. удаление единичных ACE изи ACL сетевой папки;
  7. просмотр текущих списков ACL сетевой папки;
  8. экспорт всех сведений (включая списки ACL) сетевых папок в CSV файл;
  9. импорт всех сведений (включая списки ACL) сетевых папок из CSV файла.

Касательно последнего пункта, то хочу отметить, что импорт при отсутствии наличия папки для расшаривания создаст папку и расшрарит с данными из CSV файла. Сначала я приведу список команд, которые доступны при использовании скрипта и их синтаксис:

  1. New-Share Name Path Description
    где Name - сетевое имя для папки;
    Path - путь к физической папке;
    Description описание к сетевой папке. При наличии пробелов -  заключить в кавычки (не обязательный параметр);
  2. Remove-Share Name
    где Name - сетевое имя папки;
  3. Get-Share Name
    где Name - имя сетевой папки (не обязательный параметр);
  4. Set-SharePermission Name User AccessMask AceType
    где Name - имя сетевой папки;
    User - имя пользователя/группы, которой предоставляется доступ;
    AccessMask - маска доступа. Этот параметр должен иметь одно из значений FullControl/Change/Read;
    AceType - тип доступа. Этот параметр должен иметь одно из значений Allow/Deny;
  5. Add-SharePermission Name User AccessMask AceType
    где Name - имя сетевой папки;
    User - имя пользователя/группы, которой предоставляется доступ;
    AccessMask - маска доступа. Этот параметр должен иметь одно из значений FullControl/Change/Read;
    AceType - тип доступа. Этот параметр должен иметь одно из значений Allow/Deny;
  6. Remove-SharePermission User
    где User - имя пользователя/группы, которого следует удалить из ACL сетевой папки;
  7. Get-SharePermission Name
    где Name - имя сетевой папки (не обязательный параметр);
  8. Export-ShareInfo Path
    где Path - путь к CSV файлу (включая имя файла). Если в пути присутствуют пробелы, то путь заключить в кавычки;
  9. Import-ShareInfo Path
    где Path - путь к CSV файлу (включая имя файла). Если в пути присутствуют пробелы, то путь заключить в кавычки.

Ну и собственно сам скрипт с некоторыми комментариями по работе самих функций. Для желающих подробно изучить скрипт я не делал особо комментариев, т.к. построение кода описано в предыдущих частях по управлению безопасностью сетевых папок. Ссылки на предыдущие части:

  1. Управление общими сетевыми ресурсами (шарами) в PowerShell
  2. Управление безопасностью общих папок (сетевых шар) в PowerShell (часть 1)
  3. Управление безопасностью общих папок (сетевых шар) в PowerShell (часть 2)
  4. Управление безопасностью общих папок (сетевых шар) в PowerShell (часть 3)
######################################################## 
# ShareUtils.ps1 
# Version 0.0.0.5 
# 
# Functions for advanced share management 
# 
# Vadims Podans (c) 2008 
# http://vpodans.spaces.live.com/ 
######################################################## 

Write-Host "Vadims Podans's ShareUtils are installed"


# внутренняя функция, которая преобразовывает числовой код возврата операции записи ACL 
# в текстовое значение. 
function _ShareUtils_Get-Code ($share) { 
    switch ($Share.ReturnValue) { 
        "0" {"Успешно"} 
        "2" {"Отказано в доступе"} 
        "8" {"Неизвестная ошибка"} 
        "9" {"Указано недопустимое имя шары"} 
        "21" {"Указан неправильный параметр"} 
        "22" {"Сетевая шара уже существует"} 
        "23" {"Путь перенаправлен"} 
        "24" {"Указан неверный путь"} 
        "25" {"Сетевое имя не найдено"} 
    } 
} 

# основная функция экспорта сведений о сетевых папках в CSV файл. 
function Export-ShareInfo ($path, $name) {
    # если переменная $name пустая, то функция вовзращает все сетевые папки с типом DiskDrive 
    if ($name -ne $null) {
        $shares = Get-WmiObject Win32_Share -filter "name = '$name'" 
    } Else {$shares = Get-WmiObject Win32_Share -filter 'type = 0'} 
    $Shareinfo = @() 
    # цикл извлечения сведений о каждой сетевой папке в переменную $ShareInfo 
    foreach ($share in $shares) {
        $ShareSec = Get-WmiObject Win32_LogicalShareSecuritySetting  -filter "name='$($share.name)'"
        if($shareSec) {
            $sd = $sharesec.GetSecurityDescriptor()
            $ShareInfo += $SD.Descriptor.DACL | % {
                $_ | 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'}
            }
        }
    } 
    # если переменная $path не передана, то сведения о сетевых папках передаётся в вызывющую 
    # функцию для последующей обработки, в частности добавления и удаления ACE из ACL 
    # списка сетевой папки 
    if ($path -eq $null) {$shareinfo}
    else { 
        # собственно сам экспорт содержимого $ShareInfo в CSV файл 
        $ShareInfo | select Name, Path, Description, User, Domain, SID, AccessMask, AceFlags, AceType | export-csv -noType $path 
        # если указан путь к CSV файлу, то после экспорта данных в файл проверяется, что файл действительно был создан 
        if (Test-Path $path) {Write-Host "Выполнено!"}
        else {Write-Warning "Не удалось создать файл $path. Возможно у вас не хватает прав или путь недоступен."}
    }
} 

# внутренняя функция для записи уже сформированной переменной $ShareInfo в ACL сетевой папки 
function _ShareUtils_WriteShare ($ShareInfo, $shares, $param) {
    $ShareInfo | select -unique name, Path, Description | ForEach-Object {
        $name = $_.name
        $path = $_.Path
        $description = $_.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
        }
        # здесь проверяется наличие промежуточного параметра $param, который после сборки определяет 
        # тип записи. Если $param пустой, то производится запись только в конкретную сетевую папку. Если 
        # же $param не пустой, то при записи и отсутствии сетевой папки она будет создана и в неё будут записаны 
        # данные из CSV файла. Конструкция после Else используется только при импорте данных о сетевых папках 
        # включая Access из заранее подготовленного CSV файла. 
        if ($param -eq $null) {
            $inParams = $shares.psbase.GetMethodParameters("SetShareInfo") 
            $inParams.Access = $SD 
            $write = $shares.psbase.invokemethod("setshareinfo", $inParams, $null) 
            Write-Host "Запись DACL сетевой папки:"
        _ShareUtils_Get-Code $write
        } else {
            $shares = ([WMIClass] "Win32_Share") 
            $inParams = $shares.psbase.GetMethodParameters("Create") 
            $inParams["Name"] = $_.name 
            $inParams["Type"] = 0 
            $inParams["Path"] = $_.Path 
            $inParams["Description"] = $_.Description 
            $inParams["Access"] = $SD.PsObject.BaseObject 
            $write = $shares.psbase.invokemethod("Create", $inParams, $null) 
            Write-Host "Обработка сетевой папки $name по пути $path:" 
            _ShareUtils_Get-Code $write 
        }
    }
} 

# основная функция для импорта данных о сетевых папках из CSV файла. Переменная $path 
# должна содержать путь к CSV файлу. Внутри функции проверяется, чтобы был указан 
# верный путь к CSV файлу. 
function Import-ShareInfo ($path) {
    if (Test-Path $path) {
        $param = "param" 
        $ShareInfo = Import-Csv $path 
        _ShareUtils_WriteShare $ShareInfo -param $param
    } else {Write-Warning "путь к CSV файлу указан неверный!"}
} 

# основная функция для компоновки объекта $AddInfo параметрами безопасности, которые 
# включают в себя как имя сетевой папки, имени пользователя, который должен иметь к ней 
# доступ, и типах доступа, как чтение/запись и действие разрешено/запрещено. Переменная 
# $param определяет действие с готовым объектом - отправить на запись сразу (при этом все 
# существующие разрешения будут удалены и заменены только данными из текущего объекта) 
# или вернуть обратно в вызывющую функцию, для присоединения этого объекта к уже имеющимся, 
# для окончательной компоновки объекта с полным списком ACL. 
function Set-SharePermission ($name, $user, $AceType, $AccessMask, $param) {
    $shares = gwmi Win32_share -Filter "name = '$name'"
    if ($shares -eq $null) {Write-Warning "Указанная сетевая шара не найдена"}
    else {
        # здесь я использовал хэш-таблицы для преобразования текстовых значений маски и типа
        # доступа, которые вводит пользователь в числовые значения, которые затем транслируются и
        # и помещаются в текущий объект с параметрами безопасности.
        $masks = @{FullControl = 2032127; Change = 1245631; Read = 1179817}
        $types = @{Allow = 0; Deny = 1}
        $AddInfo = New-Object System.Management.Automation.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.Path = $shares.Path
        $AddInfo.Description = $Shares.Description
        $AddInfo.User = $user
        $AddInfo.SID = (new-object security.principal.ntaccount $user).translate([security.principal.securityidentifier])
        $AddInfo.AccessMask = $masks.$AccessMask
        $AddInfo.AceType = $types.$AceType
        # тут так же использовалась временная переменная $param, которая определяет дальнейшее действие
        # с данным объектом - отправка объекта на запись (только при использовании функции Set-SharePermission),
        # либо возврат в вызываемую функцию (только при использовании функции Add-SharePermission).
        if ($param -ne $null) {
        $AddInfo} else {
            _ShareUtils_WriteShare $AddInfo $shares
        }
    }
}

# основная функция для добавления участников безопасности к имеющимуся списку ACL. Данная функция 
# сперва использует функцию экспорта для извлечения сведений об указанной сетевой папке, после чего 
# вызывается функция Set-SharePermission в качестве промежуточной функции, т.к. в неё передаётся перменная 
# $param, то вызываемая функция не будет записывать новый ACL, а вернёт скомпонованный объект $AddInfo. 
function Add-SharePermission ($name, $user, $AceType, $AccessMask) {
    $shares = gwmi Win32_share -Filter "name = '$name'"
    if ($shares -eq $null) {Write-Warning "Указанная сетевая шара не найдена"}
    else {
        # здесь нужно быть внимательным, т.к. нужно обязательно использовать обозначение массива @() для того,
        # чтобы переменная $ShareInfo смогла бы содержать массив объектов с параметрами безопасности. Один объект
        # содержит один ACE для каждого пользователя/группы. Если не использовать обозначение массива, то данная
        # переменная сможет содержать только один объект (т.е. только одного участника безопасности).
        $ShareInfo = @(Export-ShareInfo -name $name)
        $param = "param"
        $ShareInfoNew = Set-SharePermission $name $user $AceType $AccessMask $param
        # вот здесь происходит присоединение с нуля созданного объекта (ACE) к имеющемуся массиву текущих
        # ACE. Таким образом мы можем добавлять участников безопасности к ACL сетевой папки без удаления
        # текущих ACE.
        $ShareInfo += $ShareInfoNew
        _ShareUtils_WriteShare $ShareInfo $shares
    }
}

# основная функция для удаления единичного ACE из ACL сетевой папки. Процесс сводится к извлечению 
# текущего списка ACL и фильтрации ACE в этом списке по методу Not Equal. Всё, что не подпадает под 
# это действие записываются обратно в переменную, а всё, что подпало (указанный пользователь) обратно 
# в переменную $ShareInfo не записывается. 
function Remove-SharePermission ($name, $user) { 
    $shares = gwmi Win32_share -Filter "name = '$name'" 
    if ($shares -eq $null) {Write-Warning "Указанная сетевая шара не найдена"} 
    else {
        $ShareInfo = Export-ShareInfo -name $name 
        $ShareInfo = $shareInfo | where {$_.name -eq "$name" -and $_.user -ne "$user"} 
        _ShareUtils_WriteShare $ShareInfo $shares
    }
} 

# основная функция для создания новых сетевых папок на локальном компьютере. Здесь я использую упрощённый 
# вариант создания сетевой папки, но учитывая один большой нюанс я добавил одно действие. Суть проблемы 
# изложена тут: http://vpodans.spaces.live.com/blog/cns!BB1419A2CFC1E008!170.entry 
# поэтому при создании новой сетевой папки я вручную создаю с нуля список ACL, который содержит 
# только группу Everyone и с правом Allow Read. 
function New-Share ($name, $path, $Description) { 
    $Share = ([wmiClass] 'Win32_share').Create($path, $name, 0, $null, $Description) 
    Write-Host "Создание сетевой шары $name :" 
    $Return = _ShareUtils_Get-Code $share 
    $Return 
    if ($Return -eq "Успешно") { 
        # для использования скрипта в мультиязычных системах без лишних правок в скрипте я вместо именования 
        # группы Everyone я использовал трансляцию её уникального для всех систем SID в строковое значение, 
        # которое может отличаться в зависимости от языка системы. 
        $user = (new-object security.principal.securityidentifier "S-1-1-0").translate([security.principal.ntaccount]) 
        Set-SharePermission $name $user.value "Allow" "Read"
    } 
} 

# основная функция для удаления сетевой шары (равносильно Stop Sharing в консоли Shares). Сама папка 
# и её содержимое не удаляется. 
function Remove-Share ($name) { 
    $share = gwmi Win32_share -Filter "name = '$name'" 
    if ($share -eq $null) {Write-Warning "Указанная сетевая шара не найдена"} else { 
        $share.delete($null) 
        Write-Host "Удаление сетевой шары $name :" 
        _ShareUtils_Get-Code $share
    }
} 

# функция, которая возвращает на экран пользователю список всех сетевых папок на локальном компьютере. 
# можно так же получить сведения только об одной сетевой папке, которую нужно указать при вызове. 
function Get-Share ($name) { 
    if ($name -eq $null) {gwmi Win32_Share -Filter 'type = 0'}    else {gwmi Win32_Share -Filter "name='$name'"} 
} 

# основная функция для вывода на экран сведений о безопасности (содержимого списка ACL) как для всех 
# сетевых папок (если вызывается функция без параметров), так и для конкретной сетевой папки. Т.к. маски и типы 
# доступа приводятся в числовых значениях после вывода сведений выводится краткая справка по трансляции 
# данных значений. Считаю, что нету смысла писать транслятор, который перед выводом информации на экран 
# данных сам автоматически переводил бы в понятные текстовые значения. 
function Get-SharePermission ($name) { 
    Export-ShareInfo -name $name | select name, user, AccessMask, AceType | ft -a -group name 
    Write-Host "Данные колонки AccessMask имеют следующие значения: 
    2032127 - FullControl 
    1245631 - Change 
    1179817 - Read `n 
    Данные колонки AceType имеют следующие значения: 
    0 - Allow 
    1 - Deny 
    2- SystemAudit (группы Administrators и System имеют право Allow FullControl" -foregroundcolor "Yellow"
}

 

 

 

 

 

 

 

 

 

Вот так это всё выглядит. На первый взгляд много и страшно, но если прочитать все предыдушие статьи по данной теме, то данный код уже будет обретать некий смысл. На этом я предлагаю поставить жирную точку в вопросе управления сетевыми папками и безопасностью (Share Permissions) сетевых папок в PowerShell.

И на последок добавлю, что принимаются любые замечания, поправки предложения по данному скрипту, т.к. это лишь мой первый пробный многофункциональный скрипт по ACL и что-то может быть упущено или неоптимизировано. Вобщем, если есть что сказать, то отписывайтесь в комментариях.


Share this article:

Comments:

Comments are closed.