Files
LIAM/Sonstiges/New-LiamNtfsDemoShareStructure.ps1
2026-06-18 16:19:14 +02:00

376 lines
12 KiB
PowerShell

<#
.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: \\<local-server>\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>', 'empty', 'none', 'remove')) {
$safeReplacement = ''
}
if ([string]::IsNullOrWhiteSpace($safeReplacement) -and $Replacement -notin @('<empty>', '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)