From b0cfed750c53378298ad84dc198960a8536961fb Mon Sep 17 00:00:00 2001 From: Meik Date: Thu, 18 Jun 2026 16:19:14 +0200 Subject: [PATCH] Add LIAM NTFS demo share setup script --- Sonstiges/New-LiamNtfsDemoShareStructure.ps1 | 375 +++++++++++++++++++ 1 file changed, 375 insertions(+) create mode 100644 Sonstiges/New-LiamNtfsDemoShareStructure.ps1 diff --git a/Sonstiges/New-LiamNtfsDemoShareStructure.ps1 b/Sonstiges/New-LiamNtfsDemoShareStructure.ps1 new file mode 100644 index 0000000..da1e499 --- /dev/null +++ b/Sonstiges/New-LiamNtfsDemoShareStructure.ps1 @@ -0,0 +1,375 @@ +<# +.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)