В 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 видов.

  1. Создаст OU для вашей ресурса.

  2. Создаст одну служебную супер группу _tech_GRFS_DYDel_имя_ресурса - эта группа необходима, чтобы запретить группам RW изменять структуру.

  3. Создаст одну служебную группу _tech_GRFS_FULL_имя_ресурса - эта группа будет иметь полный доступ ко всему ресурсу, режим Бога в шаре (подарок шифровальщикам).

  4. Скрипт создаст в текущей директории папку 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)


  1. dth_apostle
    17.10.2021 18:30
    +1

    А что делать, если необходимо, например, раздать доступ на ресурсе, в котором 200 папок? 

    Организовать иерархию папок так, чтобы наследование помоглл решать проблему? Понятно, что кейсы бывают разные, порой структура от вас не зависит, но в таком случае и нужно уточнять: "сейчас будут костыли и велосипеды потому, что у нас тут такое вот наследство ".


    1. HPMan Автор
      17.10.2021 22:30

      Да, конечно, к этому необходимо стремиться с самого начала.


  1. edo1h
    17.10.2021 23:27

    что-то мне кажется, что этот скрипт создаёт больше проблем, чем решает — миллионы групп, членством в которых так же нужно управлять.


  1. perlestius
    18.10.2021 00:08
    +1

    ИМХО по опыту, отключение наследования в NTFS - зло.

    И называть группы доступа по именам самих папок тоже сомнительное решение. Переименует пользователь папку - а вы за ним будете группу переименовывать? Или будете плодить несоответствие?

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

    Например, группа для чтения папки Подразделения/Управление малого бизнеса/Отдел продаж называется fs0001_0022_0015_ro и в ее описании в AD указан путь к папке. NTFS-права настроены так, что даже имея доступ на редактирование, пользователи не могут переименовывать папки выше 4го уровня и это делается только через запрос в ИТ. Аналогично - с добавлением новых папок. Другими словами, за глобальный порядок отвечает ИТ, а за порядок в папке 3го уровня и согласование доступов - ответственный за папку от бизнеса.

    Ну и квоты ещё прикрутили, т.к. место на файловике не резиновое


    1. scruff
      18.10.2021 13:22

      Квоты - так себе решение, ибо они еще быстрее заканчиваются чем место на сервере. Угадайте, куда юзер побежит после вашего отказа в повышении квоты, которая скорее всего чётенько зарегламентированна в Вашей Политике ИТ? Правильно - к своему начальнику, который в свою очередь начнет давить через топ-менеджемент уже на ИТ, особенно если мы говорим про Маркетинг-отдел - они-то давить умеют и им абсолютно навалить на то какие там у вас политики и кем они утверждены))). В итоге квотирование превращается в проблему для вас самих же. Зато есть куда более изящное решение - FSRM (разумеется если у вас винда). Вот это штука реально поэффективнее квот - дает возможность запретить загрузку на сервак определенных типов файлов - фильмы, музыка, картинки определенного размера. При внедрении этой штуковины удалось высвободить более 50% места, которое было занято интернами, кухней, шансоном и прочими пляжными фоточками. В итоге после аудита, выпиливания всего личного хлама и последующего внедрения FSRM, на 2ТБ хранилище прекрасно сосуществовало годами под тысячу юзеров и всем всего хватало. Опять же - не забывайте чем больше занято места - тем больше нужно времени и места под бэкап. И да - именование групп и папок должно быть идентичным, иначе это будет каша - просто режьте права на переименование папки на всех уровнях и всё, иначе один тупой юзер переименует папку, в которой работает толпа таких же балбесов. Завтра же они к вам прибегут всем отделом с просьбой вернуть все взад, а тому юзеру даже ничего не будет. На файл сервере дожно работать основное правило ИТ - то что явно не разрешено - должно быть запрещено, иначе это будет хаос. Есть еще другой момент - архивация. Вы когда нибудь задумывались, с какой долей вероятности юзеру могут потребоваться файлы, скажем 3-4 летней давности? Правильно - с долей, близкой к 0. Поэтому, периодически можно архивировать всё содержимое сервера с удалением файлов, которые старше, ну скажем лет 5-ти. Архив этот ложить на простой хард и в сейф - тоже высвобождает немало места . Немножко рисково, затратно по времени, зато прекрасно работает для шараг, жадных на апгрейд простого файл-сервака.


      1. perlestius
        18.10.2021 14:25

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

        А то, о чем Вы говорите, - это, судя по всему, другой компонент FSRM, Управление классификациями


        1. scruff
          18.10.2021 18:43

          Я не особо помню, что там является компонентом чего. Было давно, возможно даже на 2003-й винде - сперва были жёсткие квоты с постоянными жалобами на токсичность админов. Потом был поставлен ФСРМ, упразднены квоты, и "жизнь наладилась". Кстати и сетевая нагрузка на сервак упала заметно, т.к. юзеры перестали крутить свой медиа-контент. По поводу ваших отчетов - руководству абсолютно не интересно кто там и на сколько превысил квоту. Руководству интересены отчеты о прибыли/затратах, но никак не статистика файлхранилища. Руководство перед ИТ обычно ставит четкую и ясную задачу - обеспечить работоспособность структуры, так чтобы она (структура) удовлетворяла потребностям компании. Цена вашему отчету - 2 жестких диска - даже для малого ентерпрайса это мелочь - вам одобрят расширение хранилища, не сомневайтесь. И скорее попросят подобные отчеты больше не приносить.


          1. perlestius
            18.10.2021 19:06

            Есть такое понятие, как стоимость хранения информации.

            даже для малого ентерпрайса

            Судя по этой фразе, у вас был SOHO-вариант инфраструктуры. А если брать "кровавый энтерпрайз", где нормальное резервирование, бэкапы и объемы данных далеко не 1ТБ на всю компанию, то стоимость эта будет далека от стоимости 2х жёстких дисков. Так что всё относительно.


            1. scruff
              18.10.2021 19:33
              +1

              Если вы читали мой предыдущий комент, то 1000 юзеров это уже точно далеко не соха, а такой крепкий средний энтерпрайс. В "кровавом" ентрепрайсе совсем другой CAPEX крутится - там отличие на 4-5 порядков по сравнению с сохой. Вам не то что 2 два харда не зажмут, вам даже одобрят пару новых СХД с конфой, которую вы попросите. Лишь бы работало, без всяких даун-таймов, вызванных закончившейся квотой. Там на Файловое хранилище выделяется отдельный человек а может и подразделение, при условии что CIO адекватный, а не был поставлен чтобы экономить и распиливать оптимизировать.


    1. NoOne
      18.10.2021 16:34

      А что делать, если приходит отдел продаж через руководство, и говорит, что у них есть папка "Проект Х" в отделе, и к нему надо дать доступ отделу У из другого подразделения для совместной работы?

      А потом приходит Бухгалтерия и говорит, что вот у нас проходит аудит, и нам надо поздравление/управление/бухгалтерия/аудит/основные средства/филиал 1, филиал 2, филиал 3

      Причём, каждый филиал должен видеть только свои данные, а бухгалтерия все вместе

      И кроме основных средств такую же структуру аудита для другого чего-нибудь.

      Как вы такое разруливаете?


      1. Kenoby
        18.10.2021 17:44

        Насущный вопрос, и на мой взгляд здесь применим принцип того же Teams от Microsoft. Для каждой отдельной задачи - отдельная Команда (Папка верхнего уровня).
        Если все таки речь про обычный файловый сервер, как вариант, еще использование junction link для папок. Папка хранится в одном месте, а в другом на нее жесткая ссылка. Если папки разнесены, то использовать DFS.


    1. mvv-rus
      19.10.2021 02:57

      ИМХО по опыту, отключение наследования в NTFS — зло.

      По моему опыту — не зло не особое: совсем нетрудно написать скрипт (точнее, строчку команды в PS), которая выведет список всех папок с отключенным наследованием внутри указанной.


  1. Kuznetsov_pa
    18.10.2021 07:41

    Структура групп и папок это часть дела. А как сделать Делегирование по управлению и согласованию получения доступа и чтобы не айти отдел кликал, а сам пользователь. Мы например прикрутили IDM Midpoint evolveum.


  1. Kenoby
    18.10.2021 12:27

    Недавно решал такую же задачу. Но на мой взгляд источником подобной информации был AD, в котором по средствам объектов типа "Общая Папка/Shared folder" создал нужную структуру каталогов с указанием пути. После чего скрипт пробегает по АД, создаёт папки в указанном месте, создает группы и назначает группы на папки. Папки и группы не ниже 2-3его уровня. Каждая новая задача, функция, отдел - новая папка в корне. А по поводу прав, только две группы RW и RO. На папки (и на уровне папки) где, кроме админа ничего не должен менять для всех стоит RO, на конечных папках RW. Бонусом - обнаружил что с помощью файлов desktop.ini можно для папки задавать локализованное имя. Т.е. все папки изначально на англ языке и без пробелов (это еще с cmd пошло, чтобы не плодить лишние кавычки и не переживать, за обработку.) А в дескопт.ини прописано название папки в человеческом виде, как и будет отображаться у пользователя.

     


    1. NoOne
      18.10.2021 16:45

      что делать, когда появляются спец запросы? Типа "чтобы могли копировать файлы в папку, но не могли изменять"


      1. Kenoby
        18.10.2021 17:40

        Спец. запросы это всегда боль. Если такой спец. запрос единичный - делать руками. Если постоянные - править логику работы скрипта. На мой взгляд.


      1. perlestius
        18.10.2021 18:53

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


  1. alex-khv
    19.10.2021 19:03

    Ну в 2011 писал для этого winform приложуху. Потому что структура папок была вывернута наизнанку, по отношению к структуре организации. Т.е чем глубже в папки, тем меньше людей имело доступ.

    У кого нет прав не видел папок. Или бывало что надо дать права только на первый уровень и четвёртый, без доступа на промежуточные папки.