В 2021 году достаточно много компаний используют файловые серверы для совместной работы, поэтому остается актуальным вопрос разграничения доступа на общих ресурсах.
Как правильно организовать доступ к файловым ресурсам описано в Best Practices от Microsoft, в том числе и в документе Windows Productivity for IT Professionals из Microsoft Resource Kit. В Сети можно найти множество статей на русском языке по организации файлового сервера, в том числе и на Хабре.
Например, вот эти:
Аспирин от настройки прав на файловом сервере
Правила хорошего тона для дизайна разрешений на файловых серверах
В статьях хорошо описано, что необходимо создавать группы доступа в Active Directory и уже потом раздавать права на папки шары этим группам и в свою очередь в эти группы доступа помещать пользователей или ролевые группы. Подход достаточно здравый и применим в большинстве практических ситуаций.
А что делать, если необходимо, например, раздать доступ на ресурсе, в котором 200 папок? И таких ресурсов у вас несколько штук.
Сделаем подсчет сколько времени уйдет на ручную настройку такой структуры.
… тут был скучный подсчет времени, которое требуется для ручного выполнения задачи.
Избавлю Вас от этого чтения рассуждений и подсчетов и сразу перейду к выводу: такую работу необходимо автоматизировать.
Достаточно просто это сделать при помощи скрипта на PowerShell.
Что требуется от скрипта
После выполнения скрипта мы должны получить в Active Directory структуру OU в виде дерева, которое будет повторять дерево папок нашего ресурса (шары).
В каждой OU-папке будут созданы группы с названием каждой из папок.
-
Нам необходимо четыре вида групп:
RO – группы только для чтения.
RW – группы на запись, но без возможности переименовывать или создавать папки в закрепленной структуре.
FL – группы с полными правами на папки и файлы, в том числе с правами на редактирование закрепленной структуры.
DY – группы с полным запретом на чтение папки. Систему выдачи прав сделать таким образом, чтобы можно было выдавать права с любого уровня такой структуры. То есть, если мы выдали права на верхнюю папку структуры, необходимо чтобы пользователь имел такие же права и на другие вложенные папки.
Если мы выдали права на папку где-то в глубине структуры, и пользователь не имеет доступа на родительские папки, то он должен иметь права на листинг этих родительских папок выше, чтобы можно было пройти по всему дереву к необходимой папке в глубине структуры.
У каждой группы доступа в описании должен быть закреплен полный путь к ресурсу, которым она управляет.
Структура должна автоматически поддерживаться в одном и том же состоянии за счет периодического выполнения данного скрипта.
Для каждого ресурса, шары должна быть создана одна супер группа, которой должен быть выдан полный доступ ко всем папкам структуры (подарок шифровальщикам).
Весь механизм должен работать с выключенными режимом наследования до желаемого уровня, далее наследование должно быть включено и остальные папки структуры (ниже желаемого уровня) должны наследовать права от родителя.
Сам скрипт
<#
.Synopsis
Creates OU structure in Active Directory like share structure and creates access groups in created OUs.
.Description
Script creates OU structure in Active Directory like share structure and creates access groups in created OUs.
Also script can assign folders permissions for created groups if particular parameters are switched (SetFolderRights, EnableSubInheritance).
Script find folders more than 40 characters long and find folders with spec symbols in names ['',#%^`+]
More than 40 characters long folders not allowed because Active Directory group cant have name more
than 64 characters (24 characters script uses for group prefix and random string).
Spec symbols not allowed because OU names cant contains them.
All OUs and groups with long names and spec symbols will be renamed.
.Parameter ServerName
Specifies server name where shared folder.
.Parameter ShareName
Specifies share name on server.
.Parameter SubDir
Specifies sub directory for share name. Should contains full path to the folder.
For example, creating structure only for specific folder in share not for full share.
Example - ./Create-FoldersOU.ps1 -ServerName FileServer01 -ShareName 'Cloud' -SubDir 'Department3/Unit1'.
It create structure only for specific folder 'Department3/Unit1' in share Cloud on FileServer01.
.Parameter LevelOfDepth
Level of recursion depth. Starts from 0
.Parameter BaseFileShareOU
Specifies the distinguished name path ("OU=Shares,DC=domain,DC=com") to a based OU where script will store OUs structure and groups.
.Parameter SetFolderRights
Assign permissions to folders after creating groups.
.Parameter EnableSubInheritance
Enable inheritance for all folders after last folder and its subtree.
.Parameter GroupPrefix
Specifies group prefix for access groups. Should be equal or less 5 characters. Default prefix is "GRFS"
.Example
.\Create-FolderOU.ps1 -ServerName FileServer01 -ShareName 'Cloud' -SubDir 'Department3/Unit1' -LevelOfDepth 0
Create only OU structure and access groups for subfolder Department3/Unit1 on \\FileServer01\Cloud
.Example
.\Create-FolderOU.ps1 -ServerName FileServer01 -ShareName 'Cloud'-LevelOfDepth 2 -SetFolderRights -EnableSubInheritance
Create OU structure, access groups and assign permissions for groups on \\FileServer01\Cloud with recursion for 3 branch of share.
.Inputs
No inputs
#>
<#/***************************************************************************
* DISCLAIMER OF WARRANTIES:
*
* THE SOFTWARE PROVIDED HEREUNDER IS PROVIDED ON AN "AS IS" BASIS, WITHOUT
* ANY WARRANTIES OR REPRESENTATIONS EXPRESS, IMPLIED OR STATUTORY; INCLUDING,
* WITHOUT LIMITATION, WARRANTIES OF QUALITY, PERFORMANCE, NONINFRINGEMENT,
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. NOR ARE THERE ANY
* WARRANTIES CREATED BY A COURSE OR DEALING, COURSE OF PERFORMANCE OR TRADE
* USAGE. FURTHERMORE, THERE ARE NO WARRANTIES THAT THE SOFTWARE WILL MEET
* YOUR NEEDS OR BE FREE FROM ERRORS, OR THAT THE OPERATION OF THE SOFTWARE
* WILL BE UNINTERRUPTED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/*************************************************************************** #>
###########################################################
# AUTHOR : Rinat K. Nugaev - http://www.nugaev.net - rn@nugaev.net
# DATE : 15-06-2021
# EDIT : 07-07-2021
# COMMENT : This script creates OU structure in Active Directory like FileShare structure
# and creates groups with RW,FL,DY and RO access to directories and files
# RO - read only permisssions
# RW - read write permissions only for files, cant rename or change folders
# FY - full permissions
# DY - deny read permissions
#
# VERSION : 3.9
###########################################################
#Parameters
[CmdletBinding()]Param (
[Parameter(Mandatory)]
[string] $ServerName,
[Parameter(Mandatory)]
[string]$ShareName,
[Parameter(Mandatory)]
[string]$BaseFileShareOU,
[Parameter(Mandatory)]
[int]$LevelOfDepth,
[string]$SubDir,
[string]$GroupPrefix = "GRFS",
[switch]$SetFolderRights,
[switch]$EnableSubInheritance
)
Set-StrictMode -Version Latest
#Begining functions block
Function TrackTime($Time) {
If (!($Time)) { Return Get-Date } Else {
Return ((get-date) - $Time)
}
}
Function remove-SpecSymbols {
[CmdletBinding()]
Param (
[parameter(Mandatory = $true)][string]$strName
)
Process {
$pattern = '['',#%^`+]'
if ($strName -match $pattern) {
$strName = $strName -replace $pattern, ''
}
return $strName
}
}
function Rename-Longnames {
#Required Get-RandomAlphanumericString function
[CmdletBinding()]
Param (
[parameter(Mandatory = $true)][string]$strName,
[parameter(Mandatory = $true)][ValidateLength(0, 1)][string]$Char,
[parameter(Mandatory = $true)][int]$length
)
Process {
if ($strName.Length -ge $length) {
$strName = $strName.Substring(0, ($length - 1)) + $Char
}
return $strName
}
}
Function Reset-AclInheritance {
[CmdletBinding(SupportsShouldProcess = $true)]
param(
[Parameter(Mandatory = $True)] [String]$Path
)
if (-not (Test-Path -LiteralPath $Path)) {
Write-Error -Message "The object at '$Path' doesn't exist"
return
}
Write-Verbose -Message "Getting security descriptor for item at '$Path'"
$aclinh = Get-Acl -LiteralPath $Path
$Changed = $False
if ($aclinh.AreAccessRulesProtected) {
Write-Verbose -Message "Object at '$Path' has disabled inheritance, re-enabling it"
$aclinh.SetAccessRuleProtection($False, $False)
$Changed = $True
}
$Acls = $aclinh.GetAccessRules($true, $false, [System.Security.Principal.NTAccount])
ForEach ($Ace in $Acls) {
$Ace_String = "$($Ace.IdentityReference.Value): $($Ace.AccessControlType.ToString()) ($($Ace.FileSystemRights.ToString()))"
Write-Verbose -Message "Removing non-inherited ACE at '$Path' - $Ace_String"
$result = $aclinh.RemoveAccessRule($ace)
if (-not $Result) {
Write-Error -Message "Failed to remove non-inherited ACE at '$Path' - $Ace_String"
}
else {
$Changed = $True
}
}
if ($Changed) {
if ($PSCmdlet.ShouldProcess($Path, "Persisting new security descriptor that has been reset")) {
Write-Verbose -Message "Setting new security descriptor for item at '$Path'"
Set-Acl -LiteralPath $Path -AclObject $aclinh
}
else {
Write-Verbose -Message "What if: Setting new security descriptor for item at '$Path'"
}
}
else {
Write-Verbose -Message "No changes required to the security descriptor for item at '$Path'"
}
if (Test-Path -LiteralPath $Path -PathType Container) {
Get-ChildItem -LiteralPath $Path | ForEach-Object { Reset-AclInheritance -Path $_.FullName }
}
}
Function Set-FileObjectPermissions {
<#
.SYNOPSIS
Sets Folders or Files Permissions
.PARAMETER Object
Provide Object path
.PARAMETER Principal
Provide group or user set permissions for
.PARAMETER Permisssion
Provide type of Permissions. Possible parameters are FullControl, "FullControl,TakeOwnership", "CreateFiles, AppendData, Delete" Modify,ReadAndExecute
.PARAMETER Inheritance
Inheritance or None
.PARAMETER TypeOfAccess
Allow or Deny
.EXAMPLE
Set-FileObjectPermissions -Object $PathFolder -Principal Administrators -Permission FullControl -TypeOfAccess Allow
.EXAMPLE
Set-FileObjectPermissions -Object $PathFolder -Principal Administrators -Permission "CreateFiles, AppendData, Delete" -TypeOfAccess Allow -NoneInheritance
#>
param (
[parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]$Object,
[parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]$Principal,
[parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[ValidateSet("FullControl", "FullControl,TakeOwnership", "CreateFiles, AppendData, Delete", "Delete", "Modify", "ReadAndExecute")]
$Permission,
[switch]$NoneInheritance,
[parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[ValidateSet("Allow", "Deny")]
$TypeOfAccess
)
#Getting Acl from Object
$Acl = Get-Acl "$Object"
#Preparing AccessRule
if ($NoneInheritance) {
$AccessRule = New-Object System.Security.AccessControl.FileSystemAccessRule ("$Principal", "$Permission", "None", "None", "$TypeOfAccess");
}
else {
$AccessRule = New-Object System.Security.AccessControl.FileSystemAccessRule ("$Principal", "$Permission", "ContainerInherit,ObjectInherit", "None", "$TypeOfAccess");
}
##Setting AccessRule
$Acl.SetAccessRule($AccessRule);
$Acl | Set-Acl "$Object"
}
Function Get-OUExists {
[CmdletBinding()]
Param (
[string] $OUName
)
Process {
### Getting groupname without Tail (random number in the end of name of group)
#$GroupName
if ([adsi]::Exists("LDAP://$OUName")) {
return $true
Write-Verbose "Group $GroupName already exists!"
}
else {
return $false
Write-Verbose "Group $GroupName does not exist!"
}
}
}
Function Get-ADGroupExists {
[CmdletBinding()]
Param (
[string] $GroupName,
[string] $OUName
)
Process {
### Getting groupname without Tail (random number at the end of name of the group)
$GroupName = $GroupName -replace "_\w{4}$"
$Filter = "$($GroupName)_*"
#$GroupName
if (Get-ADGroup -SearchScope OneLevel -SearchBase $OUName -Filter { Name -like $Filter } -ErrorAction SilentlyContinue) {
$ExistedGroup = Get-ADGroup -SearchScope OneLevel -SearchBase $OUName -Filter { Name -like $Filter } | Select-Object -Expand Name
return $ExistedGroup
Write-Verbose "Group $GroupName already exists!"
}
else {
return $false
Write-Verbose "Group $GroupName does not exist!"
}
}
}
Function Get-RandomAlphanumericString {
#Required Get-RandomAlphanumericString function
[CmdletBinding()]
Param (
[int] $length = 4
)
Begin {
}
Process {
Write-Output ( -join ((48..57) + (97..122) | Get-Random -Count $length | ForEach-Object { [char]$_ }) )
}
}
Function Get-subDirectory {
[CmdletBinding()]
Param (
[string] $Directory,
[string] $SubDirectory,
[int] $LevelOfDepth
)
Process {
#Deleting \ at the end of dirs
$Directory = $Directory -replace '\\$'
$SubDirectory = $SubDirectory -replace '\\$'
$TempSubDir = $Directory + "\" + $SubDirectory
$SubDirsArr = $SubDirectory.Split('\')
#Calculating $LevelOfDepth for subdirs
if ($SubDirsArr) {
$SubLevelOfDepth = ($SubDirsArr | Measure-Object).count
$LevelOfDepth = $LevelOfDepth + $SubLevelOfDepth
}
else { $LevelOfDepth = $LevelOfDepth }
$DirsArr = @()
$Directories = Get-ChildItem -Depth $LevelOfDepth -Recurse -Directory -Path $Directory
ForEach ($Dir in $Directories) {
ForEach ($SubDir in $SubDirsArr) {
if ($Dir.FullName.EndsWith($SubDir) -and $Dir -notin $DirsArr ) {
$DirsArr += $Dir
}
}
if ($Dir.FullName.StartsWith($TempSubDir) -and $Dir -notin $DirsArr) {
$DirsArr += $Dir
}
}
return $DirsArr
}
}
Function Write-Log {
[CmdletBinding()]
Param(
[Parameter(Mandatory = $False)]
[ValidateSet("INFO", "WARN", "ERROR", "FATAL", "DEBUG")]
[String]
$Level = "INFO",
[Parameter(Mandatory = $True)]
[string]
$Message,
[Parameter(Mandatory = $False)]
[string]
$Logfile
)
$Stamp = (Get-Date).toString("yyyy/MM/dd HH:mm:ss")
$Line = "$Stamp $Level $Message"
If ($logfile) {
Add-Content $logfile -Value $Line -Encoding "utf8"
}
Else {
Write-Output $Line
}
}
#End functions block
$DomainAdminsGroup = "Администраторы домена"
#$DomainAdminsGroup = "Domain Admins"
#Path to shared folder
$BaseSharePath = "\\$ServerName\$ShareName"
$ScriptDir = (Resolve-Path .\).Path
#Logs directory and in script dir and log file in it
$LogsDir = "$ScriptDir\Logs"
$LogFile = $LogsDir + "\" + "Logfile" + "-" + (Get-Date).toString("yyyy-MM-dd_HH-mm-ss") + ".log"
#Begining main part of the script
#Begining Tracking time
Clear-Host
$time = 0
$time = TrackTime $time
#Basic tests
#Test base share exists
if (!(Test-Path $BaseSharePath)) {
Write-Error "Share $BaseSharePath not presence or not created!"
Write-Warning "Please create share $BaseSharePath and set full permissions for the user the script runas."
Exit(0)
}
#Test logs dir exists, if not created, create it.
if (!(Test-Path $LogsDir)) {
Write-Warning "Logs dir $LogsDir not presence or not created!"
Write-Verbose "Creating $LogsDir"
try {
New-Item -ItemType Directory -Force -Path $LogsDir -InformationAction SilentlyContinue
}
catch {
$message = "Failed to create $LogsDir" + $($error[0])
Write-Warning $message
Exit(0)
}
}
#Test Base File Share OU already exists
if (!(Get-OUExists -OUName $BaseFileShareOU)) {
$message = "Base OU $BaseFileShareOU does not exist!`n"
$message += "Please create Base OU $BaseFileShareOU and set full permissions for the user the script runas."
Write-Warning $message
Write-log -Level FATAL $message -Logfile $LogFile
Exit(0)
}
#Test BaseOU already exists and if doesn't, create it
$BaseOU = 'OU=' + $ShareName + ',' + $BaseFileShareOU
try {
if ((Get-OUExists -OUName $BaseOU)) {
Write-Verbose "OU $BaseOU already exists"
}
else {
Write-Verbose "Creating OU $ShareName"
New-ADOrganizationalUnit -Name "$ShareName" -Path "$BaseFileShareOU" -ProtectedFromAccidentalDeletion $false
}
}
catch {
$message = "Failed to create OU $ShareName in $BaseFileShareOU" + $($error[0])
Write-Warning $message
Write-log -Level ERROR $message -Logfile $LogFile
}
$FullGroupNameFullRW = "_tech_" + $GroupPrefix + "FULL_" + $ShareName
start-sleep 1
try {
if (!(Get-ADGroup -SearchScope OneLevel -SearchBase $BaseOU -Filter { Name -eq $FullGroupNameFullRW } -ErrorAction SilentlyContinue) ) {
Write-Verbose "Creating GROUP $FullGroupNameFullRW"
New-ADGroup -Name $FullGroupNameFullRW -GroupCategory Security -GroupScope DomainLocal `
-DisplayName $FullGroupNameFullRW -Path $BaseOU `
-Description "Super FULL RW for $BaseSharePath"
}
else {
Write-Verbose "Group like $FullGroupNameFullRW already exists"
}
}
catch {
$message = "Failed to create group $FullGroupNameFullRW in $BaseOU" + $($error[0])
Write-Warning $message
Write-log -Level ERROR $message -Logfile $LogFile
}
$FullGroupNameFullRW = (Get-adgroup $FullGroupNameFullRW -ErrorAction Stop).Name
###Create OUS, groups of share and set rights to it
#Calculating $SublevelofDepth
$SubDirsArr = $SubDir.Split('\')
if ($LevelOfDepth -lt 0)
{ Write-warning "LevelOfDepth cant be less than 0"; exit }
if ($LevelOfDepth -gt 0 -and !($SubDirsArr)) { $SubLevelOfDepth = $LevelOfDepth - 1 }
elseif ($SubDirsArr) {
$SubLevelOfDepth = ($SubDirsArr | Measure-Object).count
$SubLevelOfDepth = $LevelOfDepth + $SubLevelOfDepth
}
else { $SubLevelOfDepth = $LevelOfDepth }
#Calculating $NameLength
$GroupNameLength = 64 - ($GroupPrefix.Length + 15)
$OUNameLength = 64
#Base work
#GroupCount and FolderCount counters variables that I use for statistics
$GroupCount = 0
$OUsCount = 0
##Getting folders from share and subdir
$Folders = Get-subDirectory -Directory $BaseSharePath -SubDirectory $Subdir -LevelOfDepth $LevelOfDepth | Select-Object -Expand FullName
#Init progress items
$prog_i = 1
$prog_s = 1
#Generating OUs names from folders names, creates groups and set permissions for groups to share folders
$Folders | ForEach-Object {
$NestOU = ''
$GroupDescription = $_
$FolderFullPath = $_
#Deleting \\servername\sharename\
#If you want, please create better regex for it )
$FolderWithOutSRVname = $_ -replace "^\\\\(.*?)\\(.*?)\\"
$OUS = (Split-Path $FolderWithOutSRVname -Parent).Split('\')
#Reversing Array
[array]::Reverse($OUS)
$OUS | ForEach-Object {
if ($_.Length -eq 0) {
return
}
#Removing spec symbols
$strNSS = remove-SpecSymbols -strName $_
#Rename long names for full 64 length
$strOU = Rename-Longnames -strName $strNSS -Char "~" -length $OUNameLength
$NestOU = $NestOU + 'OU=' + $strOU + ','
}
$NestOU += $BaseOU
$LeafOU = Split-Path $_ -Leaf
#removing spec symbols from LeafOU names
$LeafOU = remove-SpecSymbols -strName $LeafOU
#truncate LeafOU to 64 symbols
$LeafOU = Rename-Longnames -strName $LeafOU -Char "~" -length $OUNameLength
#getting GroupName from LeafOU
$GroupName = [string]$LeafOU
#truncate GroupName to $GroupNameLength
$GroupName = Rename-Longnames -strName $GroupName -Char "~" -length $GroupNameLength
#Creating pseudo random 4 symbol string for tail of groups names
$TailGroup = Get-RandomAlphanumericString
#Generating Groups names
$FullGroupNameLIST = "_tech_" + $GroupPrefix + "_" + "LS_" + $GroupName + "_" + $TailGroup
$FullGroupNameDYdel = "_tech_" + $GroupPrefix + "_" + "DD_" + $GroupName + "_" + $TailGroup
$FullGroupNameRO = $GroupPrefix + "_" + "RO_" + $GroupName + "_" + $TailGroup
$FullGroupNameRW = $GroupPrefix + "_" + "RW_" + $GroupName + "_" + $TailGroup
$FullGroupNameFL = $GroupPrefix + "_" + "FL_" + $GroupName + "_" + $TailGroup
$FullGroupNameDY = $GroupPrefix + "_" + "DY_" + $GroupName + "_" + $TailGroup
#Adding groups names to the GroupsHash
$GroupsHash = @{
'FullGroupNameFL' = $FullGroupNameFL
'FullGroupNameDY' = $FullGroupNameDY
'FullGroupNameRO' = $FullGroupNameRO
'FullGroupNameRW' = $FullGroupNameRW
'FullGroupNameLIST' = $FullGroupNameLIST
'FullGroupNameDYdel' = $FullGroupNameDYdel
}
#Creating OUs for from share structure
try {
#Generating GroupOU from LeafOU and NestOU
$GroupOU = 'OU=' + "$LeafOU" + "," + "$NestOU"
if ((Get-OUExists -OUName $GroupOU)) {
Write-Verbose "OU $GroupOU already exists"
}
else {
$PercentComplete = ($prog_s / ($Folders | Measure-Object).count * 100)
Write-Progress -id 5 -Activity "Creating SubOUs in Base OU " -status "SubOU $LeafOU" `
-PercentComplete $PercentComplete
$prog_s++
Write-Verbose "Creating OU $LeafOU"
New-ADOrganizationalUnit -Name $LeafOU -Path $NestOU -ProtectedFromAccidentalDeletion $false
$OUsCount += 1
}
}
catch {
$message = "Failed to create OU $LeafOU in $NestOU" + $($error[0])
Write-Warning $message
Write-log -Level ERROR $message -Logfile $LogFile
}
#Creating Access Groups
#Init progress item
$prog_q = 1
foreach ($hkey in $($GroupsHash.keys)) {
#Here we are adding old ones groups if they are already exist
try {
if ((Get-ADGroupExists -GroupName $GroupsHash[$hkey] -OuName $GroupOU) ) {
Write-Verbose "Group like $($GroupsHash[$hkey]) already exists!!!"
$GroupsHash[$hkey] = (Get-ADGroupExists -GroupName $GroupsHash[$hkey] -OuName $GroupOU)
}
else {
$PercentComplete = ($prog_q / ($($GroupsHash.keys) | Measure-Object).count * 100)
Write-Progress -id 10 -ParentId 5 -Activity "Creating spec Groups for $GroupName" -status "Group $($GroupsHash[$hkey])" `
-PercentComplete $PercentComplete
$prog_q++
Write-Verbose "Creating GROUP $($GroupsHash[$hkey])"
New-ADGroup -Name $GroupsHash[$hkey] -GroupCategory Security -GroupScope DomainLocal `
-DisplayName $GroupsHash[$hkey] -Path $GroupOU `
-Description $GroupDescription
$GroupCount += 1
}
}
catch {
$message = "Failed to create Group $($GroupsHash[$hkey]) in $GroupOU" + $($error[0])
Write-Warning $message
Write-log -Level ERROR $message -Logfile $LogFile
}
}
try {
#Adding group RW to denyDel Group
if (Get-adgroup $($GroupsHash['FullGroupNameRW'])) {
Add-ADGroupMember $($GroupsHash['FullGroupNameDYdel']) -Members $($GroupsHash['FullGroupNameRW'])
}
}
catch {
$message = "Failed to add Group $($GroupsHash['FullGroupNameRW']) to $FullGroupNameDYdel" + $($error[0])
Write-Warning $message
Write-log -Level ERROR $message -Logfile $LogFile
}
#Adding groups to the NestListGroup
$regexLSGroup = "_tech_" + $GroupPrefix + "_" + "LS_*"
$NestListGroup = Get-ADGroup -SearchScope OneLevel -SearchBase $NestOU -filter { Name -like $regexLSGroup }
foreach ($hkey in $($GroupsHash.keys)) {
if (Get-adgroup $GroupsHash[$hkey]) {
#Adding all groups to NestListGroup exept DY groups $FullGroupNameDYdel
if ($NestListGroup -and $GroupsHash[$hkey] -ne $GroupsHash['FullGroupNameDY'] -and $GroupsHash[$hkey] -ne $GroupsHash['FullGroupNameDYdel']) {
Write-Verbose "Adding $($GroupsHash[$hkey]) to $(($NestListGroup).Name)"
Add-ADGroupMember $NestListGroup -Members $GroupsHash[$hkey]
}
}
}
#Getting variables from Hash back
#Sorry, some AD commandlets have strange behavior with hash keys
$FullGroupNameFL = $GroupsHash['FullGroupNameFL']
$FullGroupNameDY = $GroupsHash['FullGroupNameDY']
$FullGroupNameRO = $GroupsHash['FullGroupNameRO']
$FullGroupNameRW = $GroupsHash['FullGroupNameRW']
$FullGroupNameLIST = $GroupsHash['FullGroupNameLIST']
$FullGroupNameDYdel = $GroupsHash['FullGroupNameDYdel']
#Assigning rights to the folders
if ($SetFolderRights) {
$PercentComplete = ($prog_i / ($Folders | Measure-Object).count * 100)
Write-Progress -id 20 -Activity "Setting group $GroupName rights for Folder" -status "Folder $FolderFullPath" `
-PercentComplete $PercentComplete
$prog_i++
try {
#Setting Deny Del Rights to the Folder
if (Get-adgroup -Filter { SamAccountName -eq $FullGroupNameDYdel }) {
Write-Verbose "Setting Deny Del for $FolderFullPath"
#Set-FileObjectPermissions -Object $FolderFullPath -Principal $FullGroupNameDYdel -Permission "CreateFiles, AppendData, Delete" -TypeOfAccess Deny -NoneInheritance
Set-FileObjectPermissions -Object $FolderFullPath -Principal $FullGroupNameDYdel -Permission "Delete" -TypeOfAccess Deny -NoneInheritance
}
#Setting Deny Del Rights to the Folder
if (Get-adgroup -Filter { SamAccountName -eq $FullGroupNameDY }) {
Write-Verbose "Setting Deny Read for $FolderFullPath"
Set-FileObjectPermissions -Object $FolderFullPath -Principal $FullGroupNameDY -Permission "ReadAndExecute" -TypeOfAccess Deny
}
#Setting RW Rights to the Folder
if (Get-adgroup -Filter { SamAccountName -eq $FullGroupNameRW }) {
Write-Verbose "Setting RW for $FolderFullPath"
Set-FileObjectPermissions -Object $FolderFullPath -Principal $FullGroupNameRW -Permission "Modify" -TypeOfAccess Allow
$FoldersRW = Get-subDirectory -Directory $FolderFullPath -SubDirectory $Subdir -LevelOfDepth ($SubLevelOfDepth) | Select-Object -Expand FullName
#Init progress item
$prog_j = 1
$FoldersRW | ForEach-Object {
$FolderPathRW = $_
Write-Verbose "Set ACL for $FullGroupNameRW recursive for folder $_"
$PercentComplete = ($prog_j / ($FoldersRW | Measure-Object).count * 100)
Write-Progress -id 30 -ParentId 20 -Activity "Setting group $FullGroupNameRW rights to subfolders" -status "Folder $_" `
-PercentComplete $PercentComplete
$prog_j++
Set-FileObjectPermissions -Object $FolderPathRW -Principal $FullGroupNameRW -Permission "Modify" -TypeOfAccess Allow }
}
#Setting Full Rights to the Folder
if (Get-adgroup -Filter { SamAccountName -eq $FullGroupNameFL }) {
Write-Verbose "Setting FULL for $FolderFullPath"
Set-FileObjectPermissions -Object $FolderFullPath -Principal $FullGroupNameFL -Permission "Modify" -TypeOfAccess Allow
#Adding $FullGroupNameFL to subfolders recurcively
$FoldersFL = Get-subDirectory -Directory $FolderFullPath -SubDirectory $Subdir -LevelOfDepth ($SubLevelOfDepth) | Select-Object -Expand FullName
#Init progress item
$prog_k = 1
$FoldersFL | ForEach-Object {
$FolderPathFL = $_
Write-Verbose "Set ACL for $FullGroupNameFL recursive for folder $_"
$PercentComplete = ($prog_k / ($FoldersFL | Measure-Object).count * 100)
Write-Progress -id 40 -ParentId 20 -Activity "Setting group $FullGroupNameFL rights to subfolders" -status "Folder $_" `
-PercentComplete $PercentComplete
$prog_k++
Set-FileObjectPermissions -Object $FolderPathFL -Principal $FullGroupNameFL -Permission "Modify" -TypeOfAccess Allow }
}
#Setting RO Rights to the Folder
if (Get-adgroup -Filter { SamAccountName -eq $FullGroupNameRO }) {
Write-Verbose "Setting RO for $FolderFullPath"
Set-FileObjectPermissions -Object $FolderFullPath -Principal $FullGroupNameRO -Permission "ReadAndExecute" -TypeOfAccess Allow
#Adding $FullGroupNameRO to subfolders recurcively
$FoldersRO = Get-subDirectory -Directory $FolderFullPath -SubDirectory $Subdir -LevelOfDepth ($SubLevelOfDepth) | Select-Object -Expand FullName
#Init progress item
$prog_m = 1
$FoldersRO | ForEach-Object {
$FolderPathRO = $_
Write-Verbose "Set ACL for $FullGroupNameRO recursive for folder $_"
$PercentComplete = ($prog_m / ($FoldersRO | Measure-Object).count * 100)
Write-Progress -id 50 -ParentId 20 -Activity "Setting group $FullGroupNameRO rights to subfolders" -status "Folder $_" `
-PercentComplete $PercentComplete
$prog_m++
Set-FileObjectPermissions -Object $FolderPathRO -Principal $FullGroupNameRO -Permission "ReadAndExecute" -TypeOfAccess Allow }
}
#Setting List Rights to the Folder
if (Get-adgroup -Filter { SamAccountName -eq $FullGroupNameLIST }) {
Write-Verbose "Setting List for $FolderFullPath"
Set-FileObjectPermissions -Object $FolderFullPath -Principal $FullGroupNameLIST -Permission "ReadAndExecute" -TypeOfAccess Allow -NoneInheritance
}
}
catch {
$message = "Failed to assign permissions to $FolderFullPath" + $($error[0])
Write-Warning $message
Write-log -Level ERROR $message -Logfile $LogFile
}
}
else {
Write-Verbose "Switch parameter SetFolderRights is not set, nothing to do"
}
try {
#Set rights for super groups
Write-Verbose "Set ACL for $FullGroupNameFullRW recursive for folder $GroupDescription"
Set-FileObjectPermissions -Object $FolderFullPath -Principal $FullGroupNameFullRW -Permission "FullControl" -TypeOfAccess Allow
#Assign permissions for Creator OWNER group
if (Get-adgroup $DomainAdminsGroup -ErrorAction SilentlyContinue) {
Write-Verbose "Set Full perms for Domain admins for folder $GroupDescription"
Set-FileObjectPermissions -Object $FolderFullPath -Principal $DomainAdminsGroup -Permission "FullControl,TakeOwnership" -TypeOfAccess Allow
}
#Assign permissions for Creator OWNER group
Write-Verbose "Setting CREATOR OWNER perms for $FolderFullPath"
###Warnset
Set-FileObjectPermissions -Object $FolderFullPath -Principal "CREATOR OWNER" -Permission "Modify" -TypeOfAccess Allow
#Assign permissions for server admin adminsuser
<#if (Get-aduser adminuser -ErrorAction SilentlyContinue) {
### Write-Verbose "Set ACL for adminuser fullcontrol for folder $GroupDescription"
###Warnset
Set-FileObjectPermissions -Object $FolderFullPath -Principal "nrk" -Permission "FullControl,TakeOwnership" -TypeOfAccess Allow
}#>
}
catch {
$message = "Failed to assign built-in permissions to $FolderFullPath" + $($error[0])
Write-Warning $message
Write-log -Level ERROR $message -Logfile $LogFile
}
#Disable Inheritance if no SubDir
try {
if (!$Subdir) {
Write-Verbose "Reseting inheritance for $FolderFullPath"
$acl = Get-Acl $FolderFullPath
$acl.SetAccessRuleProtection($true, $false)
$acl | Set-Acl $FolderFullPath
}
else {
Write-Verbose "Variable DisableInheritance is not set, nothing to do"
}
}
catch {
$message = "Failed to disable inheritance for $FolderFullPath" + $($error[0])
Write-Warning $message
Write-log -Level ERROR $message -Logfile $LogFile
}
}
#Reenabling inheritance from +1 LevelOfDepth
if ($EnableSubInheritance) {
$FoldersDeep = @()
#$FoldersTemp = Get-ChildItem -Depth ($LevelOfDepth + 1) -Recurse -Directory -Path $BaseSharePath | Select-Object -Expand FullName
$FoldersTemp = Get-subDirectory -Directory $BaseSharePath -SubDirectory $Subdir -LevelOfDepth ($LevelOfDepth + 1) | Select-Object -Expand FullName
ForEach ($Folder in $FoldersTemp) {
if ($Folder -notin $Folders) {
Write-Verbose "Adding $Folder to FoldersDeep array"
$FoldersDeep += $Folder
}
}
#Init progress item
$prog_r = 1
$FoldersDeep | ForEach-Object {
try {
$PercentComplete = ($prog_r / ($FoldersDeep | Measure-Object).count * 100)
Write-Progress -id 60 -Activity "Enable inheritance for Folder" -status "Folder $_" `
-PercentComplete $PercentComplete
$prog_r++
Write-Verbose "Enable inheritance in $_"
Reset-AclInheritance -Path $_
}
catch {
$message = "Failed to enable inheritance to $_" + $($error[0])
Write-Warning $message
Write-log -Level ERROR $message -Logfile $LogFile
}
}
}
$time = TrackTime $time
$message = "All tasks complete!!!`n"
$message += "Created $GroupCount groups for $OusCount OUs`n"
$message += "Script run $($time.Hours) hours $($time.Minutes) minutes"
Write-Output $message
Write-log -Level INFO $message -Logfile $LogFile
Перед запуском скрипта прочитайте все рекомендации ниже.
Требования к окружению для запуска скрипта.
PowerShell 5 и выше
Сервер с установленными PowerShell модулями Active Directory
Также обязательно включите на шаре access based enumeration. Google подскажет как это сделать.
Подготовка шары к выполнению скрипта.
Скрип сработает правильно, если на всех папках ресурса (шары) будет выключено наследование. Сделать это можно при помощи другого скрипта.
Скрипт подготовки ресурса
Function Remove-ACL {
[CmdletBinding(SupportsShouldProcess=$True)]
Param(
[parameter(Mandatory=$true,ValueFromPipeline=$true,Position=0)]
[ValidateNotNullOrEmpty()]
[ValidateScript({Test-Path $_ -PathType Container})]
[String[]]$Folder,
[String[]]$AdminUser,
[Switch]$Recurse
)
Process {
foreach ($f in $Folder) {
if ($Recurse) {$Folders = $(Get-ChildItem $f -Recurse -Directory ).FullName} else {$Folders = $f}
if ($Folders -ne $null) {
$Folders | ForEach-Object {
# Remove inheritance
$acl = Get-Acl $_
$acl.SetAccessRuleProtection($true,$true)
Set-Acl $_ $acl
# Remove ACL
$acl = Get-Acl $_
$acl.Access | %{$acl.RemoveAccessRule($_)} | Out-Null
# Add local admin
$permission = "BUILTIN\Administrators","FullControl", "ContainerInherit,ObjectInherit","None","Allow"
$rule = New-Object System.Security.AccessControl.FileSystemAccessRule $permission
$acl.SetAccessRule($rule)
Set-Acl $_ $acl
# Add SPEC GROUP PERMS
$acl = Get-Acl $_
$permission = "$AdminUser","FullControl", "ContainerInherit,ObjectInherit","None","Allow"
$rule = New-Object System.Security.AccessControl.FileSystemAccessRule $permission
$acl.SetAccessRule($rule)
Set-Acl $_ $acl
#>
Write-Verbose "Remove-HCacl: Inheritance disabled and permissions removed from $_"
}
}
else {
Write-Verbose "Remove-HCacl: No subfolders found for $f"
}
}
}
}
#Path to shared folder
$BaseSharePath = "F:\Fileshare\"
$AdminUser = "scripuser"
Function TrackTime($Time){
If (!($Time)) { Return Get-Date } Else {
Return ((get-date) - $Time)
}
}
$time = 0
$time = TrackTime $time
Remove-ACL $BaseSharePath -Recurse -AdminUser $AdminUser -Verbose
$time = TrackTime $time
Write-host "It run $($time.Hours) hours $($time.Minutes) minutes $($time.seconds) seconds"
Задайте переменные в скрипте
Путь к папке ресурса $BaseSharePath = "\\FileServer\Fileshare\"
Пользователь от которого будете запускать основной скрипт $AdminUser = "scripuser"
После подготовки ресурса (шары) запускайте основной скрипт.
Пример команды запуска основного скрипта с параметрами.
.\create-FoldersOU.ps1 -ServerName nn-FileServer01 -ShareName "FilesShare1" `
-BaseFileShareOU "OU=FileShares,OU=PermissionsGroups,OU=Groups,DC=example,DC=com" `
-SetFolderRights -EnableSubInheritance -LevelOfDepth 1 `
-GroupPrefix "GRFS"
Параметры скрипта:
-ServerName
имя сервера с ресурсом;
-ShareName
название ресурса (шары) на сервере;
-BaseFileShareOU
название OU, где будет создана структура шары;
-LevelOfDepth
желаемый уровень вложения;
-GroupPrefix
префикс создаваемых групп;
-SetFolderRights
применение прав к папкам;
-EnableSubInheritance
включение наследования в папках ниже построенной структуры.
Также скрипт поддерживает параметр -Verbose
, с ним он будет подробно писать, что делает.
Что сделает скрипт после запуска
Далее скрипт создаст структуру OU согласно структуре вашей шары. В каждой OU будет созданы группы 5 видов.
Создаст OU для вашей ресурса.
Создаст одну служебную супер группу
_tech_GRFS_DYDel_имя_ресурса
- эта группа необходима, чтобы запретить группам RW изменять структуру.Создаст одну служебную группу
_tech_GRFS_FULL_имя_ресурса
- эта группа будет иметь полный доступ ко всему ресурсу, режим Бога в шаре (подарок шифровальщикам).Скрипт создаст в текущей директории папку
Logs
, - там будут лежать логи.
Служебная группа
_tech_GRFS_LS_Имя-папки_gwp5
GRFS_DY_Имя-папки_gwp5
- запрет на чтение папкиGRFS_FL_Имя-папки_gwp5
- полный доступ к папке, в том числе с изменением структурыGRFS_RO_Имя-папки_gwp5
- только чтение-
GRFS_RW_Имя-папки_gwp5
- полный доступ без возможности изменять структуру папокGRFS
- префикс, обозначающий предназначение группы: GR - group, FS - file server.gwp5
- случайная последовательность, для уникальности каждой группы.
Во время выполнения скрипт автоматически вложит группы FL, RO, RW в группу LS и вложит в нее еще LS группы нижележащих папок для обеспечения возможности давать доступ пользователю в любой части структуры.
Скрипт автоматически выдаст нужные права всем группам на папки ресурса по всей структуре. Выключит наследование прав до заданного уровня вложения (сканирования) и включит наследование прав ниже уровня вложения.
Если запустить скрипт повторно на уже выстроенной структуре, то он просканирует структуру шары и если структура шары соответствует структуре OU созданной раньше, то скрипт ничего не будет создавать, а только заново выставит права на уже созданные группы.
Если же вы или ваши пользователи создали в структуре шары какие-либо папки, то скрипт при следующем запуске достроит структуру учитывая новые папки.
В независимости от того, созданы новые папки или нет, скрипт всегда проходит еще раз по папкам и выставляет заданные права для групп. Таким образом обеспечивается необходимое постоянное состояние структуры OU, которое всегда соответствует ресурсу. Но скрипт ничего не делает, если какая-либо из папок была удалена. При удалении папки из ресурса, необходимо вручную удалять OU и группы.
Как быть, если у вас не везде необходимо выдержать один и тот же уровень вложения скрипта, но в некоторые папки опуститься глубже?
После первого запуска скрипта и создания базовой структуры запустите скрипт с параметром -SubDir и укажите папку ресурса для которой необходимо выстроить структуру с более глубоким уровнем вложения.
P.S!
Конечно, поместить пользователей в группы доступа вам будет необходимо самостоятельно и поэтому раздача прав не совсем уж такая автоматическая, но практическая польза скрипта от этого все равно не уменьшается.
Просьба профессиональных программистов не судить строго за то, как написан скрипт. Я не являюсь программистом, а просто иногда пишу код для решения рутинных задач.
Некоторые функции для скрипта я взял у коллег из Сети, например, функцию включения наследования с заменой прав у наследников. И скрипт подготовки ресурса для выполнения основного скрипта.
Также извините за справку и комментарии в скрипте на ломаном английском, но скриптом пользуются зарубежные коллеги и лучше такие комментарии, чем их полное отсутствие.
Очень приветствуются конструктивные дополнения к скрипту и логике его работы.
Всем желаю экономить время и делать ручной работы как можно меньше!
Спасибо!
Комментарии (18)
edo1h
17.10.2021 23:27что-то мне кажется, что этот скрипт создаёт больше проблем, чем решает — миллионы групп, членством в которых так же нужно управлять.
perlestius
18.10.2021 00:08+1ИМХО по опыту, отключение наследования в NTFS - зло.
И называть группы доступа по именам самих папок тоже сомнительное решение. Переименует пользователь папку - а вы за ним будете группу переименовывать? Или будете плодить несоответствие?
Мы с коллегами в свое время решили пойти таким путём: договорились с бизнесом, что права раздаются не глубже 3го уровня иерархии и совместно с бизнесом сформировали целевое дерево папок. Потом начался долгий, но неизбежный переход от существовавшей ранее файлопомойки к структурированному виду.
Например, группа для чтения папки Подразделения/Управление малого бизнеса/Отдел продаж называется fs0001_0022_0015_ro и в ее описании в AD указан путь к папке. NTFS-права настроены так, что даже имея доступ на редактирование, пользователи не могут переименовывать папки выше 4го уровня и это делается только через запрос в ИТ. Аналогично - с добавлением новых папок. Другими словами, за глобальный порядок отвечает ИТ, а за порядок в папке 3го уровня и согласование доступов - ответственный за папку от бизнеса.
Ну и квоты ещё прикрутили, т.к. место на файловике не резиновое
scruff
18.10.2021 13:22Квоты - так себе решение, ибо они еще быстрее заканчиваются чем место на сервере. Угадайте, куда юзер побежит после вашего отказа в повышении квоты, которая скорее всего чётенько зарегламентированна в Вашей Политике ИТ? Правильно - к своему начальнику, который в свою очередь начнет давить через топ-менеджемент уже на ИТ, особенно если мы говорим про Маркетинг-отдел - они-то давить умеют и им абсолютно навалить на то какие там у вас политики и кем они утверждены))). В итоге квотирование превращается в проблему для вас самих же. Зато есть куда более изящное решение - FSRM (разумеется если у вас винда). Вот это штука реально поэффективнее квот - дает возможность запретить загрузку на сервак определенных типов файлов - фильмы, музыка, картинки определенного размера. При внедрении этой штуковины удалось высвободить более 50% места, которое было занято интернами, кухней, шансоном и прочими пляжными фоточками. В итоге после аудита, выпиливания всего личного хлама и последующего внедрения FSRM, на 2ТБ хранилище прекрасно сосуществовало годами под тысячу юзеров и всем всего хватало. Опять же - не забывайте чем больше занято места - тем больше нужно времени и места под бэкап. И да - именование групп и папок должно быть идентичным, иначе это будет каша - просто режьте права на переименование папки на всех уровнях и всё, иначе один тупой юзер переименует папку, в которой работает толпа таких же балбесов. Завтра же они к вам прибегут всем отделом с просьбой вернуть все взад, а тому юзеру даже ничего не будет. На файл сервере дожно работать основное правило ИТ - то что явно не разрешено - должно быть запрещено, иначе это будет хаос. Есть еще другой момент - архивация. Вы когда нибудь задумывались, с какой долей вероятности юзеру могут потребоваться файлы, скажем 3-4 летней давности? Правильно - с долей, близкой к 0. Поэтому, периодически можно архивировать всё содержимое сервера с удалением файлов, которые старше, ну скажем лет 5-ти. Архив этот ложить на простой хард и в сейф - тоже высвобождает немало места . Немножко рисково, затратно по времени, зато прекрасно работает для шараг, жадных на апгрейд простого файл-сервака.
perlestius
18.10.2021 14:25Квоты - это и есть одна из возможностей FSRM. Если Вы так топите за FSRM, должны быть в курсе. И, к слову, есть мягкие квоты, которые позволяют просто видеть превышение, не накладывая запрет на запись в папку. А потом на основании созданных отчётов можно показать руководству, кто из отделов ест много места на файловике и при желании даже посчитать, во сколько это обходится компании.
А то, о чем Вы говорите, - это, судя по всему, другой компонент FSRM, Управление классификациями
scruff
18.10.2021 18:43Я не особо помню, что там является компонентом чего. Было давно, возможно даже на 2003-й винде - сперва были жёсткие квоты с постоянными жалобами на токсичность админов. Потом был поставлен ФСРМ, упразднены квоты, и "жизнь наладилась". Кстати и сетевая нагрузка на сервак упала заметно, т.к. юзеры перестали крутить свой медиа-контент. По поводу ваших отчетов - руководству абсолютно не интересно кто там и на сколько превысил квоту. Руководству интересены отчеты о прибыли/затратах, но никак не статистика файлхранилища. Руководство перед ИТ обычно ставит четкую и ясную задачу - обеспечить работоспособность структуры, так чтобы она (структура) удовлетворяла потребностям компании. Цена вашему отчету - 2 жестких диска - даже для малого ентерпрайса это мелочь - вам одобрят расширение хранилища, не сомневайтесь. И скорее попросят подобные отчеты больше не приносить.
perlestius
18.10.2021 19:06Есть такое понятие, как стоимость хранения информации.
даже для малого ентерпрайса
Судя по этой фразе, у вас был SOHO-вариант инфраструктуры. А если брать "кровавый энтерпрайз", где нормальное резервирование, бэкапы и объемы данных далеко не 1ТБ на всю компанию, то стоимость эта будет далека от стоимости 2х жёстких дисков. Так что всё относительно.
scruff
18.10.2021 19:33+1Если вы читали мой предыдущий комент, то 1000 юзеров это уже точно далеко не соха, а такой крепкий средний энтерпрайс. В "кровавом" ентрепрайсе совсем другой CAPEX крутится - там отличие на 4-5 порядков по сравнению с сохой. Вам не то что 2 два харда не зажмут, вам даже одобрят пару новых СХД с конфой, которую вы попросите. Лишь бы работало, без всяких даун-таймов, вызванных закончившейся квотой. Там на Файловое хранилище выделяется отдельный человек а может и подразделение, при условии что CIO адекватный, а не был поставлен чтобы экономить и
распиливатьоптимизировать.
NoOne
18.10.2021 16:34А что делать, если приходит отдел продаж через руководство, и говорит, что у них есть папка "Проект Х" в отделе, и к нему надо дать доступ отделу У из другого подразделения для совместной работы?
А потом приходит Бухгалтерия и говорит, что вот у нас проходит аудит, и нам надо поздравление/управление/бухгалтерия/аудит/основные средства/филиал 1, филиал 2, филиал 3
Причём, каждый филиал должен видеть только свои данные, а бухгалтерия все вместе
И кроме основных средств такую же структуру аудита для другого чего-нибудь.
Как вы такое разруливаете?
Kenoby
18.10.2021 17:44Насущный вопрос, и на мой взгляд здесь применим принцип того же Teams от Microsoft. Для каждой отдельной задачи - отдельная Команда (Папка верхнего уровня).
Если все таки речь про обычный файловый сервер, как вариант, еще использование junction link для папок. Папка хранится в одном месте, а в другом на нее жесткая ссылка. Если папки разнесены, то использовать DFS.
mvv-rus
19.10.2021 02:57ИМХО по опыту, отключение наследования в NTFS — зло.
По моему опыту — не зло не особое: совсем нетрудно написать скрипт (точнее, строчку команды в PS), которая выведет список всех папок с отключенным наследованием внутри указанной.
Kuznetsov_pa
18.10.2021 07:41Структура групп и папок это часть дела. А как сделать Делегирование по управлению и согласованию получения доступа и чтобы не айти отдел кликал, а сам пользователь. Мы например прикрутили IDM Midpoint evolveum.
Kenoby
18.10.2021 12:27Недавно решал такую же задачу. Но на мой взгляд источником подобной информации был AD, в котором по средствам объектов типа "Общая Папка/Shared folder" создал нужную структуру каталогов с указанием пути. После чего скрипт пробегает по АД, создаёт папки в указанном месте, создает группы и назначает группы на папки. Папки и группы не ниже 2-3его уровня. Каждая новая задача, функция, отдел - новая папка в корне. А по поводу прав, только две группы RW и RO. На папки (и на уровне папки) где, кроме админа ничего не должен менять для всех стоит RO, на конечных папках RW. Бонусом - обнаружил что с помощью файлов desktop.ini можно для папки задавать локализованное имя. Т.е. все папки изначально на англ языке и без пробелов (это еще с cmd пошло, чтобы не плодить лишние кавычки и не переживать, за обработку.) А в дескопт.ини прописано название папки в человеческом виде, как и будет отображаться у пользователя.
NoOne
18.10.2021 16:45что делать, когда появляются спец запросы? Типа "чтобы могли копировать файлы в папку, но не могли изменять"
Kenoby
18.10.2021 17:40Спец. запросы это всегда боль. Если такой спец. запрос единичный - делать руками. Если постоянные - править логику работы скрипта. На мой взгляд.
perlestius
18.10.2021 18:53Помнится, на одной из предыдущих работ была папка для фотографий. Туда сотрудники (в основном маркетинг) выкладывали снимки с корпоративов. Систематически некоторые сотрудники других отделов удаляли добавленные маркетологами совместные фото (например, если сами там плохо получились), а другие потом жаловались, что пропали бесценные фото с их участием. Мы тогда настроили права так, что все пользователи могли смотреть всё и добавлять новое, но удалять могли только то, что добавили сами (те файлы и папки, где они владельцы)
alex-khv
19.10.2021 19:03Ну в 2011 писал для этого winform приложуху. Потому что структура папок была вывернута наизнанку, по отношению к структуре организации. Т.е чем глубже в папки, тем меньше людей имело доступ.
У кого нет прав не видел папок. Или бывало что надо дать права только на первый уровень и четвёртый, без доступа на промежуточные папки.
dth_apostle
Организовать иерархию папок так, чтобы наследование помоглл решать проблему? Понятно, что кейсы бывают разные, порой структура от вас не зависит, но в таком случае и нужно уточнять: "сейчас будут костыли и велосипеды потому, что у нас тут такое вот наследство ".
HPMan Автор
Да, конечно, к этому необходимо стремиться с самого начала.