<# .SYNOPSIS Creates a local LIAM NTFS demo share structure with AGDLP groups and ACLs. .DESCRIPTION This script mirrors the LIAM NTFS demo configuration for the imagoverum.com environment: - local folder: C:\file_shares\share2 - optional SMB share: \\\file_shares\share2 - group strategy: Ntfs_AGDLP - group tags: FS for global groups, UG for domain-local groups - access tags: _O, _W, _R, _T Run with -WhatIf first. The ActiveDirectory module is only required when AD group creation and ACL assignment are enabled. #> [CmdletBinding(SupportsShouldProcess = $true)] param( [string]$ShareRootPath = 'C:\file_shares', [string]$RootFolderName = 'share2', [string]$SmbShareName = 'file_shares', [string]$GroupOuDN = 'OU=AGP,OU=LIAM,OU=Global,DC=imagoverum,DC=com', [string[]]$AdditionalFolderRelativePaths = @(), [string]$GroupNameSanitizeReplacement = '_', [switch]$PreserveAdGroupNameCase, [switch]$SkipAdGroups, [switch]$SkipNtfsAcl, [switch]$SkipSmbShare ) Set-StrictMode -Version Latest $ErrorActionPreference = 'Stop' if ([System.Environment]::OSVersion.Platform -ne [System.PlatformID]::Win32NT) { throw 'Dieses Script kann nur unter Windows ausgefuehrt werden.' } $rootPath = Join-Path -Path $ShareRootPath -ChildPath $RootFolderName $readRights = [System.Security.AccessControl.FileSystemRights]0x200A9 $writeRights = [System.Security.AccessControl.FileSystemRights]0x301BF $ownerRights = [System.Security.AccessControl.FileSystemRights]0x1F01FF function ConvertTo-LiamSafeNameSegment { param( [Parameter(Mandatory = $true)] [string]$Value, [Parameter(Mandatory = $true)] [string]$Replacement, [Parameter(Mandatory = $true)] [bool]$PreserveCase ) $safeReplacement = if ($null -eq $Replacement) { '_' } else { $Replacement.Trim() } if ($safeReplacement -in @('', 'empty', 'none', 'remove')) { $safeReplacement = '' } if ([string]::IsNullOrWhiteSpace($safeReplacement) -and $Replacement -notin @('', 'empty', 'none', 'remove')) { $safeReplacement = '_' } $safeValue = [regex]::Replace($Value, '[\x00-\x1F\x7F/\\\[\]:;\|=,\+\*\?<>]', $safeReplacement) if ($PreserveCase) { return $safeValue } return $safeValue.ToUpperInvariant() } function Get-LiamFolderToken { param( [Parameter(Mandatory = $true)] [string]$FolderPath ) $itemName = Split-Path -Path $FolderPath -Leaf if ([string]::IsNullOrWhiteSpace($itemName)) { $itemName = $RootFolderName } ConvertTo-LiamSafeNameSegment ` -Value $itemName ` -Replacement $GroupNameSanitizeReplacement ` -PreserveCase $PreserveAdGroupNameCase.IsPresent } function Get-LiamGroupSet { param( [Parameter(Mandatory = $true)] [string]$FolderPath ) $name = Get-LiamFolderToken -FolderPath $FolderPath [pscustomobject]@{ FolderPath = $FolderPath Name = $name GlobalOwner = "FS_${name}_O" GlobalWrite = "FS_${name}_W" GlobalRead = "FS_${name}_R" GlobalTraverse = "FS_${name}_T" LocalOwner = "UG_${name}_O" LocalWrite = "UG_${name}_W" LocalRead = "UG_${name}_R" } } function Ensure-Folder { param( [Parameter(Mandatory = $true)] [string]$Path ) if (Test-Path -LiteralPath $Path) { return } if ($PSCmdlet.ShouldProcess($Path, 'Create directory')) { New-Item -ItemType Directory -Path $Path -Force | Out-Null } } function Ensure-SmbShare { param( [Parameter(Mandatory = $true)] [string]$Name, [Parameter(Mandatory = $true)] [string]$Path ) $existingShare = Get-SmbShare -Name $Name -ErrorAction SilentlyContinue if ($existingShare) { if ($existingShare.Path -ne $Path) { Write-Warning "SMB share '$Name' already points to '$($existingShare.Path)', not '$Path'." } return } if ($PSCmdlet.ShouldProcess($Name, "Create SMB share for '$Path'")) { New-SmbShare -Name $Name -Path $Path -ChangeAccess 'Authenticated Users' | Out-Null } } function Ensure-AdGroup { param( [Parameter(Mandatory = $true)] [string]$Name, [Parameter(Mandatory = $true)] [ValidateSet('Global', 'DomainLocal')] [string]$Scope, [Parameter(Mandatory = $true)] [string]$Description ) $existingGroup = Get-ADGroup -Identity $Name -ErrorAction SilentlyContinue if ($existingGroup) { return $existingGroup } if ($PSCmdlet.ShouldProcess($Name, "Create AD group in '$GroupOuDN'")) { New-ADGroup ` -Name $Name ` -SamAccountName $Name ` -GroupCategory Security ` -GroupScope $Scope ` -Path $GroupOuDN ` -Description $Description | Out-Null } return Get-ADGroup -Identity $Name -ErrorAction SilentlyContinue } function Ensure-AdGroupMembership { param( [Parameter(Mandatory = $true)] [string]$ParentGroup, [Parameter(Mandatory = $true)] [string]$MemberGroup ) $parent = Get-ADGroup -Identity $ParentGroup -Properties member -ErrorAction SilentlyContinue $member = Get-ADGroup -Identity $MemberGroup -ErrorAction SilentlyContinue if (-not $parent -or -not $member) { if ($WhatIfPreference) { if ($PSCmdlet.ShouldProcess($ParentGroup, "Add member group '$MemberGroup'")) { } return } throw "Cannot add '$MemberGroup' to '$ParentGroup' because at least one group does not exist." } if ($parent.member -contains $member.DistinguishedName) { return } if ($PSCmdlet.ShouldProcess($ParentGroup, "Add member group '$MemberGroup'")) { Add-ADGroupMember -Identity $parent -Members $member } } function Ensure-LiamGroups { param( [Parameter(Mandatory = $true)] [pscustomobject]$GroupSet ) $groups = @( @{ Name = $GroupSet.GlobalOwner; Scope = 'Global'; Description = "$($GroupSet.Name) - _O" } @{ Name = $GroupSet.GlobalWrite; Scope = 'Global'; Description = "$($GroupSet.Name) - _W" } @{ Name = $GroupSet.GlobalRead; Scope = 'Global'; Description = "$($GroupSet.Name) - _R" } @{ Name = $GroupSet.LocalOwner; Scope = 'DomainLocal'; Description = "$($GroupSet.Name) - _O" } @{ Name = $GroupSet.LocalWrite; Scope = 'DomainLocal'; Description = "$($GroupSet.Name) - _W" } @{ Name = $GroupSet.LocalRead; Scope = 'DomainLocal'; Description = "$($GroupSet.Name) - _R" } @{ Name = $GroupSet.GlobalTraverse; Scope = 'Global'; Description = "$($GroupSet.Name) - _T" } ) foreach ($group in $groups) { Ensure-AdGroup -Name $group.Name -Scope $group.Scope -Description $group.Description | Out-Null } Ensure-AdGroupMembership -ParentGroup $GroupSet.LocalOwner -MemberGroup $GroupSet.GlobalOwner Ensure-AdGroupMembership -ParentGroup $GroupSet.LocalWrite -MemberGroup $GroupSet.GlobalWrite Ensure-AdGroupMembership -ParentGroup $GroupSet.LocalRead -MemberGroup $GroupSet.GlobalRead } function Add-FolderAccessRule { param( [Parameter(Mandatory = $true)] [string]$Path, [Parameter(Mandatory = $true)] [string]$Account, [Parameter(Mandatory = $true)] [System.Security.AccessControl.FileSystemRights]$Rights ) if (-not (Test-Path -LiteralPath $Path)) { if ($WhatIfPreference) { if ($PSCmdlet.ShouldProcess($Path, "Add NTFS ACL '$Rights' for '$Account'")) { } return } throw "Path not found: $Path" } $acl = Get-Acl -LiteralPath $Path $identity = [System.Security.Principal.NTAccount]::new($Account) try { $sid = $identity.Translate([System.Security.Principal.SecurityIdentifier]) } catch [System.Security.Principal.IdentityNotMappedException] { if ($WhatIfPreference) { if ($PSCmdlet.ShouldProcess($Path, "Add NTFS ACL '$Rights' for '$Account'")) { } return } throw } foreach ($rule in $acl.Access) { if ($rule.AccessControlType -ne [System.Security.AccessControl.AccessControlType]::Allow) { continue } if ($rule.IdentityReference.Translate([System.Security.Principal.SecurityIdentifier]).Value -ne $sid.Value) { continue } if (($rule.FileSystemRights -band $Rights) -eq $Rights) { return } } $accessRule = [System.Security.AccessControl.FileSystemAccessRule]::new( $sid, $Rights, [System.Security.AccessControl.InheritanceFlags]'ContainerInherit,ObjectInherit', [System.Security.AccessControl.PropagationFlags]::None, [System.Security.AccessControl.AccessControlType]::Allow ) $acl.AddAccessRule($accessRule) if ($PSCmdlet.ShouldProcess($Path, "Add NTFS ACL '$Rights' for '$Account'")) { Set-Acl -LiteralPath $Path -AclObject $acl } } function Ensure-LiamNtfsAcl { param( [Parameter(Mandatory = $true)] [pscustomobject]$GroupSet, [Parameter(Mandatory = $true)] [string]$DomainNetBiosName ) Add-FolderAccessRule -Path $GroupSet.FolderPath -Account "$DomainNetBiosName\$($GroupSet.LocalOwner)" -Rights $ownerRights Add-FolderAccessRule -Path $GroupSet.FolderPath -Account "$DomainNetBiosName\$($GroupSet.LocalWrite)" -Rights $writeRights Add-FolderAccessRule -Path $GroupSet.FolderPath -Account "$DomainNetBiosName\$($GroupSet.LocalRead)" -Rights $readRights } $relativeFolders = @($AdditionalFolderRelativePaths) | Where-Object { -not [string]::IsNullOrWhiteSpace($_) } | ForEach-Object { $_.Trim().TrimStart('\', '/') } | Sort-Object -Unique Ensure-Folder -Path $ShareRootPath Ensure-Folder -Path $rootPath foreach ($relativeFolder in $relativeFolders) { Ensure-Folder -Path (Join-Path -Path $rootPath -ChildPath $relativeFolder) } if (-not $SkipSmbShare) { Ensure-SmbShare -Name $SmbShareName -Path $ShareRootPath } $foldersToManage = @($rootPath) foreach ($relativeFolder in $relativeFolders) { $foldersToManage += (Join-Path -Path $rootPath -ChildPath $relativeFolder) } $domainNetBiosName = $null if (-not $SkipAdGroups -or -not $SkipNtfsAcl) { Import-Module ActiveDirectory -ErrorAction Stop $domainNetBiosName = (Get-ADDomain).NetBIOSName } $summary = foreach ($folder in $foldersToManage) { $groupSet = Get-LiamGroupSet -FolderPath $folder if (-not $SkipAdGroups) { Ensure-LiamGroups -GroupSet $groupSet } if (-not $SkipNtfsAcl) { Ensure-LiamNtfsAcl -GroupSet $groupSet -DomainNetBiosName $domainNetBiosName } [pscustomobject]@{ Folder = $folder LocalOwnerAcl = $groupSet.LocalOwner LocalWriteAcl = $groupSet.LocalWrite LocalReadAcl = $groupSet.LocalRead GlobalOwner = $groupSet.GlobalOwner GlobalWrite = $groupSet.GlobalWrite GlobalRead = $groupSet.GlobalRead GlobalTraverse = $groupSet.GlobalTraverse } } $summary Write-Host '' Write-Host 'LIAM provider RootPath for this machine:' Write-Host ("\\{0}\{1}\{2}" -f $env:COMPUTERNAME, $SmbShareName, $RootFolderName)