Vadims 的个人资料Vadims Podans's former b...照片日志列表更多 工具 帮助

日志


2008/7/16

Управление безопасностью общих папок (сетевых шар) в PowerShell (часть 4, заключительная)

Ну что ж, настало время подвести итоги по материалу управления сетевыми шарами и управлению доступа к ним. Как и обещал, я написал скрипт, который выполнен в виде функций при помощи которого можно действительно легко и просто управлять самими сетевыми папками и их безопасностью из 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' | fl}
else {
gwmi Win32_Share -Filter "name='$name'" | fl}
}

# основная функция для вывода на экран сведений о безопасности (содержимого списка 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 и что-то может быть упущено или неоптимизировано. Вобщем, если есть что сказать, то отписывайтесь в комментариях.

评论 (2)

请稍候...
很抱歉,您输入的评论太长。请缩短您的评论。
您没有输入任何内容,请重试。
很抱歉,我们当前无法添加您的评论。请稍后重试。
若要添加评论,需要您的家长授予您相应权限。请求权限
您的家长禁用了评论功能。
很抱歉,我们当前无法删除您的评论。请稍后重试。
您已超过了一天之内允许提供的评论数上限。请在 24 小时后重试。
因为我们的系统表明您可能在向其他用户提供垃圾评论,您的帐户已禁用了评论功能。如果您认为我们错误地禁用了您的帐户,请联系 Windows Live 支持部门
完成下面的安全检查,您提供评论的过程才能完成。
您在安全检查中键入的字符必须与图片或音频中的字符一致。
PodānsVadi​ms 在此页禁用了评论功能。
Я вобщем-то тоже не программист и не утверждаю, что весь код написал самостоятельно. Часть наработок я взял у /\/\o\/\/ и дальше уже сам всё прорабатывал, дописывал и связал в нечто единое целое. Кстати говоря, решение о разборе данного вопроса принял после вашего сообщения в своём блоге, т.к. я сразу понял, что тут дело двумя строчками не решается.
7 月 16 日
Просто маньяк какой-то :-)
А я не программист, мне такое написание самостоятельно не светит :-(
7 月 16 日

引用通告

此日志的引用通告 URL 是:
http://vpodans.spaces.live.com/blog/cns!BB1419A2CFC1E008!188.trak
引用此项的网络日志