Add orphaned folder ACL cleanup script

This commit is contained in:
Meik
2026-03-13 16:45:55 +01:00
parent b5981487d7
commit ba7d0fb600

View File

@@ -0,0 +1,133 @@
[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)