[CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High')] param( [Parameter(Mandatory = $true, Position = 0)] [ValidateNotNullOrEmpty()] [string]$Path ) Set-StrictMode -Version Latest $ErrorActionPreference = 'Stop' function Test-OrphanedSid { param( [Parameter(Mandatory = $true)] [System.Security.Principal.SecurityIdentifier]$Sid ) try { [void]$Sid.Translate([System.Security.Principal.NTAccount]) return $false } catch [System.Security.Principal.IdentityNotMappedException] { return $true } } function Get-TargetDirectories { param( [Parameter(Mandatory = $true)] [string]$RootPath ) $rootItem = Get-Item -LiteralPath $RootPath if (-not $rootItem.PSIsContainer) { throw "Der Pfad '$RootPath' ist kein Ordner." } $directories = New-Object System.Collections.Generic.List[System.IO.DirectoryInfo] $directories.Add([System.IO.DirectoryInfo]$rootItem) foreach ($directory in Get-ChildItem -LiteralPath $RootPath -Directory -Recurse -Force) { if (($directory.Attributes -band [System.IO.FileAttributes]::ReparsePoint) -ne 0) { Write-Warning "Ueberspringe Reparse-Point: $($directory.FullName)" continue } $directories.Add([System.IO.DirectoryInfo]$directory) } return $directories } function Remove-OrphanedAclEntriesFromDirectory { param( [Parameter(Mandatory = $true)] [System.IO.DirectoryInfo]$Directory ) $acl = Get-Acl -LiteralPath $Directory.FullName $rules = $acl.GetAccessRules($true, $false, [System.Security.Principal.SecurityIdentifier]) $orphanedRules = @() foreach ($rule in $rules) { $sid = [System.Security.Principal.SecurityIdentifier]$rule.IdentityReference if (Test-OrphanedSid -Sid $sid) { $orphanedRules += $rule } } if ($orphanedRules.Count -eq 0) { return [pscustomobject]@{ Path = $Directory.FullName CheckedRuleCount = $rules.Count RemovedRuleCount = 0 RemovedIdentities = @() Changed = $false } } $removedIdentities = New-Object System.Collections.Generic.List[string] if ($PSCmdlet.ShouldProcess($Directory.FullName, "Remove $($orphanedRules.Count) orphaned ACL entries")) { foreach ($rule in $orphanedRules) { [void]$acl.RemoveAccessRuleSpecific($rule) $removedIdentities.Add($rule.IdentityReference.Value) } Set-Acl -LiteralPath $Directory.FullName -AclObject $acl } return [pscustomobject]@{ Path = $Directory.FullName CheckedRuleCount = $rules.Count RemovedRuleCount = $orphanedRules.Count RemovedIdentities = $removedIdentities.ToArray() Changed = $orphanedRules.Count -gt 0 } } $resolvedPath = (Resolve-Path -LiteralPath $Path).Path $results = New-Object System.Collections.Generic.List[object] $errorCount = 0 foreach ($directory in Get-TargetDirectories -RootPath $resolvedPath) { try { $result = Remove-OrphanedAclEntriesFromDirectory -Directory $directory $results.Add($result) if ($result.RemovedRuleCount -gt 0) { Write-Host ("[{0}] {1} verwaiste ACL-Eintraege in {2}" -f ($(if ($WhatIfPreference) { 'WHATIF' } else { 'OK' })), $result.RemovedRuleCount, $result.Path) foreach ($identity in $result.RemovedIdentities) { Write-Host (" - {0}" -f $identity) } } } catch { $errorCount++ Write-Warning ("Fehler bei {0}: {1}" -f $directory.FullName, $_.Exception.Message) } } $checkedDirectories = $results.Count $changedDirectories = @($results | Where-Object { $_.Changed }).Count $removedRules = ($results | Measure-Object -Property RemovedRuleCount -Sum).Sum if ($null -eq $removedRules) { $removedRules = 0 } Write-Host '' Write-Host 'Zusammenfassung' Write-Host ('Gepruefte Ordner : {0}' -f $checkedDirectories) Write-Host ('Geaenderte Ordner: {0}' -f $changedDirectories) Write-Host ('Entfernte ACEs : {0}' -f $removedRules) Write-Host ('Fehler : {0}' -f $errorCount)