Mit PowerShell einen Dateiserver bestandsaufnehmen ── Kapazitätsanalyse und Zugriffsrechte-(ACL)-Audit
· Go Komura · PowerShell, Windows, Dateiserver, ACL, Zugriffsrechte, Betriebsoptimierung, Sicherheit, Skript
„Dem Dateiserver bleibt weniger als 10 % freier Speicherplatz.“ ── Arbeit, die mit dieser Warnung beginnt, ist meist deprimierend. Niemand weiß, was die Kapazität frisst. Niemand kann beurteilen, ob ein zehn Jahre alter Ordner, den niemand mehr anfasst, gefahrlos gelöscht werden kann. Und niemand kann auf Anhieb sagen, wer worauf zugreifen kann oder ob Berechtigungen von Ausgeschiedenen noch bestehen. Eine Untersuchung, bei der man im Explorer die Eigenschaften öffnet, wird bei einer Freigabe im Terabyte-Bereich niemals fertig.
Jedes Mal, wenn wir uns bei so einer Anfrage die Hände schmutzig machen, kommt uns derselbe Gedanke: Dateiserver aufzuräumen ist keine „Löschtechnik“, sondern eine „Bestandsaufnahmetechnik“. Fragen Sie „dürfen wir das löschen?“ ohne Zahlen und Liste, werden Abteilungen niemals zustimmen. Legen Sie eine Tabelle vor, die sagt „allein dieser Ordner umfasst 800 GB, davon 620 GB seit über drei Jahren unverändert“, bewegt sich das Gespräch sofort vorwärts.
Dieser Artikel richtet sich an IT- und Betriebsverantwortliche in kleinen und mittleren Unternehmen und legt das praktische Vorgehen dar, um die Kapazität und die Zugriffsrechte (ACLs) eines Dateiservers mit PowerShell bis hin zu einem CSV-Bericht zu bestandsaufnehmen. Der Grundsatz bleibt durchgehend: zuerst lesen, Änderungen zuletzt, mit Sicherungen und -WhatIf.
1. Zunächst das Ergebnis
- Die Kapazitätsanalyse läuft im Kern über Get-ChildItem -Recurse + Measure-Object -Sum. Aggregieren Sie nicht den gesamten Baum auf einmal, sondern arbeiten Sie je Ordner der ersten Ebene, um ein grobes Gefühl dafür zu bekommen, wo sich der Großteil befindet.12
- Verschlucken Sie Zugriffsverweigerungen nicht ── protokollieren Sie sie. Erfassen Sie im Gegenzug für das Nichtstoppen des Scans mit -ErrorAction SilentlyContinue stets die verweigerten Orte mit -ErrorVariable. Ein Ort, den Sie nicht zählen konnten, ist nicht „null“, sondern „unbekannt“.3
- Stützen Sie Ihre Beurteilung „alter Dateien“ auf LastWriteTime. In vielen Umgebungen sind die Aktualisierungen der NTFS-Zugriffszeit deaktiviert (oder systemgesteuert), sodass sie sich nicht als Beleg heranziehen lassen.45
- Extrahieren Sie Duplikat-„Kandidaten“ mit Get-FileHash (standardmäßig SHA256). Vor der Hash-Berechnung nach Größe einzugrenzen ist die übliche Art, E/A zu sparen.6
- Die ACL-Bestandsaufnahme ist die Eigenschaft Access von Get-Acl. Übertragen Sie IdentityReference (wer), FileSystemRights (was) und IsInherited (vererbt oder direkt) in eine CSV, und nutzen Sie AreAccessRulesProtected, um Orte mit unterbrochener Vererbung zu erkennen.78
- Was dieser Artikel bestandsaufnimmt, sind NTFS-Berechtigungen. Der effektive Zugriff über eine Freigabe (SMB) ergibt sich aus der Kombination von Freigabeberechtigungen und NTFS-ACL. Listen Sie die Freigabeseite separat mit Get-SmbShareAccess auf und lesen Sie sie zusammen mit dem NTFS-Register.9
- Geben Sie die Zeichenkodierung Ihrer CSV-Berichte ausdrücklich an. Der Standard von Export-Csv unter Windows PowerShell 5.1 ist ASCII, was Nicht-ASCII-Text beschädigt, während der Standard von PowerShell 7 UTF-8 ohne BOM ist, was sich mit dem Standardverhalten von Excel schlecht verträgt.1011
- Nehmen Sie Änderungen (Set-Acl) erst vor, wenn Bestandsaufnahme und Abteilungsbestätigung abgeschlossen sind. Sichern Sie mit icacls /save, bestätigen Sie die Ziele mit -WhatIf und wenden Sie dann an.1213
2. Kapazitätsanalyse ── „Wo ist es aufgebläht?“ in Zahlen fassen
Als Erstes ermitteln Sie die Gesamtgröße jedes Ordners direkt unterhalb der Freigabewurzel. Zählen Sie Dateien mit Get-ChildItem rekursiv auf und summieren Sie Length mit Measure-Object.12
$root = 'D:\share' # Geht davon aus, dass Sie auf dem Dateiserver ausführen. Hinweise zur Ausführung über UNC siehe Kapitel 6
$denied = @()
# Je Ordner der ersten Ebene aggregieren ── ein grobes Bild kommt vor der Gesamtsumme
# Dieselben Optionen auch auf das äußere Get-ChildItem anwenden, damit Fehlschläge beim Auflisten der Wurzel ebenfalls in $denied landen
$report = foreach ($dir in Get-ChildItem -LiteralPath $root -Directory `
-ErrorAction SilentlyContinue -ErrorVariable +denied) {
# SilentlyContinue, damit Zugriffsverweigerungen uns nicht stoppen, doch Verweigerungen sammeln sich weiterhin in $denied
# Die Auflistung direkt in Measure-Object leiten (eine Zuweisung an eine Variable würde jedes
# FileInfo-Objekt im Speicher halten, was bei Ordnern mit Millionen von Dateien schmerzhaft wird)
$stats = Get-ChildItem -LiteralPath $dir.FullName -Recurse -File `
-ErrorAction SilentlyContinue -ErrorVariable +denied |
Measure-Object -Property Length -Sum
[PSCustomObject]@{
Folder = $dir.Name
SizeGB = [math]::Round([double]$stats.Sum / 1GB, 2)
FileCount = $stats.Count
}
}
# Dateien, die direkt in der Wurzel liegen, ebenfalls als eine Zeile zählen (sie fehlen sonst bei der ordnerweisen Aggregation)
$rootStats = Get-ChildItem -LiteralPath $root -File `
-ErrorAction SilentlyContinue -ErrorVariable +denied |
Measure-Object -Property Length -Sum
if ($rootStats.Count -gt 0) {
$report += [PSCustomObject]@{
Folder = '(direkt in der Wurzel)'
SizeGB = [math]::Round([double]$rootStats.Sum / 1GB, 2)
FileCount = $rootStats.Count
}
}
$report | Sort-Object SizeGB -Descending | Format-Table -AutoSize
# Immer ein Protokoll dessen, „was nicht gezählt werden konnte“ führen ── ist dies nicht leer, ist die Summe unvollständig
$denied | ForEach-Object { $_.TargetObject } | Sort-Object -Unique |
Set-Content -Path .\denied-paths.txt
Zwei Dinge sind zu beachten. Erstens: -ErrorAction SilentlyContinue ist kein Schalter, der bedeutet „so tun, als wäre kein Fehler aufgetreten“. Er unterdrückt nur die Anzeige und läuft weiter; der Fehler tritt weiterhin auf.3 Deshalb fangen Sie ihn mit -ErrorVariable auf. Ein vorangestelltes + vor dem Variablennamen bewirkt ein Anhängen statt eines Überschreibens, sodass Sie die Verweigerungen der gesamten Schleife in einer einzigen Variable sammeln können.3 Ordner, für die Ihnen der Zugriff verweigert wurde, fehlen in den Summen, sodass der Bericht sie kleiner darstellt als in Wirklichkeit. Ist denied-paths.txt nicht leer, weisen Sie im Bericht unbedingt darauf hin. Möchten Sie beim Thema Fehlerbehandlung eine Ebene tiefer gehen, ist „Fehlerbehandlung und Wiederholungsdesign in PowerShell“, gleichzeitig veröffentlicht, eine nützliche Referenz.
Zweitens das Problem tiefer Hierarchien. Eine seit Jahren genutzte Freigabe wird nahezu sicher Pfade enthalten, die 260 Zeichen (MAX_PATH) überschreiten, und manche Werkzeuge scheitern dort bei der Auflistung.14 Zeigen Ihre Scan-Ergebnisse eine unnatürliche Lücke, verdächtigen Sie zuerst die Pfadlänge. Das Gesamtbild dieser Beschränkung und der Umgang damit ist in „MAX_PATH und die Fallstricke von Windows-Pfaden und -Dateinamen“ zusammengefasst.
3. Alte Dateien und Duplikatkandidaten ── mit einer Liste beginnen, nicht mit dem Löschen
3.1. Eine Liste „seit drei Jahren unangetasteter Dateien“ erstellen
Sobald Sie wissen, wo es aufgebläht ist, geht es als Nächstes darum, Material für „Löschkandidaten“ zu sammeln. Das Kriterium dafür ist LastWriteTime (letzter Änderungszeitpunkt).
$cutoff = (Get-Date).AddYears(-3)
# Eine Liste von Dateien, die seit drei Jahren oder länger nicht geändert wurden. Nichts wird gelöscht ── dies ist Material, das zuerst der Abteilung gezeigt wird
# Orte, deren Auflistung fehlschlägt, mit -ErrorVariable festhalten, um sie später offenzulegen
Get-ChildItem -LiteralPath $root -Recurse -File `
-ErrorAction SilentlyContinue -ErrorVariable oldEnumErrors |
Where-Object LastWriteTime -lt $cutoff |
Select-Object FullName, LastWriteTime,
@{ Name = 'SizeMB'; Expression = { [math]::Round($_.Length / 1MB, 2) } } |
Sort-Object SizeMB -Descending |
Export-Csv -Path .\old-files.csv -NoTypeInformation -Encoding utf8BOM # Bei 5.1 UTF8 (beide sind mit BOM)
# Orte, die nicht aufgelistet werden konnten, immer offenlegen, damit die Liste nicht „vollständig“ wirkt
$oldEnumErrors | ForEach-Object { $_.TargetObject } |
Set-Content -Path .\old-files-uninspected.txt
Es ist verlockend zu denken, „mit der letzten Zugriffszeit (LastAccessTime) lässt sich sogar feststellen, was gar nicht gelesen wurde“ ── doch das ist eine Falle. Weil sich die Aktualisierung der letzten Zugriffszeit auf die Leistung von NTFS auswirkt, wird über den Befehl fsutil behavior und die Registrierung (NtfsDisableLastAccessUpdate) gesteuert, ob die Aktualisierung aktiviert oder deaktiviert ist,4 und seit Windows Vista ist sie standardmäßig deaktiviert (bei neueren Windows-10-Versionen und später systemgesteuert, auf Servern deaktiviert).5 Mit anderen Worten: Dateien, die tatsächlich geöffnet werden, deren Zeitstempel aber alt bleibt, sind völlig normal. Verwenden Sie in Ihren Bestandsaufnahmeunterlagen LastWriteTime ── „das Datum der letzten inhaltlichen Änderung“ ── und erläutern Sie ausdrücklich, was das bedeutet; das hält das Gespräch mit der Abteilung geordnet.
3.2. Duplikatdatei-„Kandidaten“ ermitteln
Freigegebene Ordner sammeln massenhaft „Endversion“, „Endversion_überarbeitet“ und „Kopie von …“. Ob der Inhalt identisch ist, lässt sich mit Get-FileHash feststellen. Der Standardalgorithmus ist SHA256, und stimmen die Hashwerte überein, können Sie den Dateiinhalt als identisch betrachten.6
# Das Hashen aller Dateien ist E/A-intensiv. Zunächst auf „Dateien gleicher Größe“ eingrenzen
# Orte, deren Auflistung fehlschlägt (Zugriffsverweigerung usw.), mit -ErrorVariable zur späteren Offenlegung festhalten
$candidates = Get-ChildItem -LiteralPath $root -Recurse -File `
-ErrorAction SilentlyContinue -ErrorVariable enumErrors |
Group-Object -Property Length |
Where-Object { $_.Count -ge 2 -and [long]$_.Name -gt 0 } | # Dateien der Größe 0 ausschließen
ForEach-Object { $_.Group }
# Nur für die eingegrenzten Kandidaten Hashes berechnen und übereinstimmende Gruppen als Duplikat-„Kandidaten“ ausgeben
$candidates |
Get-FileHash -ErrorAction SilentlyContinue -ErrorVariable hashErrors | # standardmäßig SHA256
Group-Object -Property Hash |
Where-Object Count -ge 2 |
ForEach-Object { $_.Group } |
Select-Object Hash, Path |
Export-Csv -Path .\duplicate-candidates.csv -NoTypeInformation -Encoding utf8BOM # Bei 5.1 UTF8
# Ordner, die nicht aufgelistet werden konnten, sowie Dateien, deren Hash sich wegen Sperrung,
# fehlender Leseberechtigung usw. nicht berechnen ließ, immer als „nicht geprüft“ in einer Liste festhalten
# (ein stillschweigendes Verwerfen macht sie von „keine Duplikate“ ununterscheidbar)
@($enumErrors) + @($hashErrors) | ForEach-Object { $_.TargetObject } |
Set-Content -Path .\hash-uninspected.txt
Bewusst werden sie „Kandidaten“ genannt, weil die Entscheidung, welche Version erhalten bleibt, eine geschäftliche und keine technische Frage ist. Auch bei identischem Inhalt können „das Original der Abteilung A“ und „die Referenzkopie der Abteilung B“ unterschiedliche Bedeutung haben. Löschen Sie nicht mechanisch ── der Aufgabenbereich dieses Skripts reicht nur bis zur Liste, mit der Sie das Gespräch mit den Beteiligten führen. Die Eigenschaften des Hashing-Werkzeugs selbst (was garantiert ist und was nicht) werden in „Ein praktisches Vorgehen zur Identifizierung des Verfahrens hinter einer Hash-Zeichenfolge“ behandelt.
4. Zugriffsrechte-(ACL-)Bestandsaufnahme ── „wer kann was“ in eine Tabelle bringen
Neben der Kapazität ist ein weiteres Bestandsaufnahmeziel die Berechtigungen. Get-Acl ruft den Sicherheitsdeskriptor einer Datei oder eines Ordners ab, und aus der Eigenschaft Access können Sie die Liste der Zugriffssteuerungseinträge (ACEs) der DACL auslesen.7
# Die ACLs der Ordner zwei Ebenen ab der Freigabewurzel bestandsaufnehmen
# Der „Stamm“ eines Berechtigungsdesigns konzentriert sich auf die flachen Ebenen, dort zuerst prüfen
$aclErrors = @() # -ErrorVariable mit + hängt an, daher bei jedem Lauf neu initialisieren, um den vorigen Lauf nicht mitzuschleppen
$targets = @(Get-Item -LiteralPath $root `
-ErrorAction SilentlyContinue -ErrorVariable +aclErrors) +
@(Get-ChildItem -LiteralPath $root -Directory -Recurse -Depth 1 `
-ErrorAction SilentlyContinue -ErrorVariable +aclErrors)
$aclReport = foreach ($t in $targets) {
# Abruffehler (nicht lesbar, während des Scans gelöscht usw.) festhalten, um sie später offenzulegen
$acl = Get-Acl -LiteralPath $t.FullName -ErrorAction SilentlyContinue -ErrorVariable +aclErrors
if (-not $acl) { continue }
if (@($acl.Access).Count -eq 0) {
# Auch für Ordner mit leerer DACL (null explizite Einträge) eine Zeile behalten, damit Informationen zu unterbrochener Vererbung nicht aus dem Register verschwinden
[PSCustomObject]@{
Path = $t.FullName; Identity = '(keine Einträge)'; Rights = $null; Type = $null
IsInherited = $null; Inheritance = $null
InheritanceBroken = $acl.AreAccessRulesProtected
}
continue
}
foreach ($ace in $acl.Access) {
[PSCustomObject]@{
Path = $t.FullName
Identity = $ace.IdentityReference # Wer (Benutzer/Gruppe)
Rights = $ace.FileSystemRights # Was er/sie tun kann
Type = $ace.AccessControlType # Allow / Deny
IsInherited = $ace.IsInherited # Vom übergeordneten Element vererbt oder direkt vergeben
# Für dieselben Rechte unterscheiden, ob sie „nur diesen Ordner“ oder „auch die darunterliegenden Dateien/Ordner“ betreffen
Inheritance = "$($ace.InheritanceFlags)/$($ace.PropagationFlags)"
InheritanceBroken = $acl.AreAccessRulesProtected # Ist die Vererbung an diesem Ordner unterbrochen
}
}
}
$aclReport | Export-Csv -Path .\acl-report.csv -NoTypeInformation -Encoding utf8BOM # Bei 5.1 UTF8
# Auch Ziele, deren ACL sich nicht abrufen ließ, immer als Lücken im Register offenlegen (dieselbe Disziplin wie bei den anderen Untersuchungen)
$aclErrors | ForEach-Object { $_.TargetObject } |
Set-Content -Path .\acl-uninspected.txt
Öffnen Sie diese CSV, filtern Sie sie aus folgenden Blickwinkeln, um nach Auffälligkeiten zu suchen.
| Zu prüfende Spalte | Anzeichen für ein Problem | Typischer Hintergrund |
|---|---|---|
| Identity | Ein individueller Benutzername erscheint direkt | Die Ansammlung von „diesem gebe ich erst mal Zugriff“. Verrottet bei Abteilungswechsel und Austritt |
| Identity | Wird als rohe SID wie S-1-5-21-... angezeigt |
Berechtigungen, die von einem gelöschten Konto zurückgeblieben sind (erster Kandidat für die Bereinigung) |
| IsInherited = False | Direkt vergebene ACEs sind tief im Baum verstreut | Ad-hoc-Einzelvergaben. Ausnahmen, die aus dem Design gefallen sind |
| InheritanceBroken = True | Ein Ordner, an dem die Vererbung unterbrochen wurde | Eine frühere Reaktion auf „diesen Ordner möchte ich nicht sichtbar haben“. Ein zentraler Schwerpunkt der Bestandsaufnahme |
| Type = Deny | Es existiert ein Deny-ACE | Unter sonst gleichen Bedingungen hat Verweigerung Vorrang vor Zulassung. Explizite ACEs werden jedoch vor vererbten ACEs ausgewertet, sodass es Reihenfolgen gibt, in denen eine explizite Zulassung eine vererbte Verweigerung übertrumpft. Verfolgen Sie den Ursprung des ACE, um die Auswirkung zu beurteilen, und bestätigen Sie sie letztlich über den effektiven Zugriff |
Einträge, bei denen IsInherited False ist, sind „Berechtigungen, die jemand von Hand an diesem Ordner hinzugefügt hat“, und Ordner, bei denen AreAccessRulesProtected True ist, sind „Inseln, die Änderungen an den Berechtigungen des übergeordneten Elements nie erreichen“.78 Der größte Teil der Berechtigungsprobleme konzentriert sich auf diese beiden, beginnen Sie Ihr Register also dort. Eine Einschränkung sei ausdrücklich genannt: Dieses Beispiel ist ein flacher Scan mit -Depth 1, sodass Ordner, die tiefer als das die Vererbung unterbrechen, nicht erfasst werden und auch kein Fehler auftritt. Möchten Sie eine vollständige Untersuchung unterbrochener Vererbung, entfernen Sie entweder die -Depth-Beschränkung und lassen Sie dasselbe Skript laufen (es wird Zeit kosten), oder führen Sie es erneut mit einem verdächtigen Abteilungsordner als Wurzel aus. Beachten Sie außerdem, dass Get-Acl den Sicherheitsdeskriptor auch im SDDL-Zeichenfolgenformat anzeigen kann,7 doch für Bestandsaufnahmezwecke reicht die Tabelle aus der Eigenschaft Access aus. Es besteht keine Eile, sich mit dem Lesen von SDDL zu befassen, bevor Sie auf ein Phänomen stoßen, das diese Tabelle nicht erklären kann.
Ein praktischer Hinweis: Geben Sie die Zeichenkodierung Ihrer CSV-Dateien immer ausdrücklich an. Export-Csv von Windows PowerShell 5.1 gibt standardmäßig ASCII aus, was Nicht-ASCII-Text beschädigt.10 Der Standard für die PowerShell-7er-Reihe ist utf8NoBOM (UTF-8 ohne BOM),11 was beim direkten Öffnen in Excel in manchen Umgebungen als Zeichensalat angezeigt wird. Für eine Datei, die Sie an eine Abteilung weitergeben, ist es sicherer, sich mit -Encoding UTF8BOM (bei 5.1 -Encoding UTF8) auf UTF-8 mit BOM festzulegen.
5. Wie Sie Änderungen vornehmen ── die Wahl zwischen icacls und Set-Acl, Sicherungen und -WhatIf
Erst wenn Bestandsaufnahme und Abteilungsbestätigung abgeschlossen sind, betreten Sie die Änderungsphase. Hier gibt es zwei Werkzeugfamilien.
| Zweck | Werkzeug | Grund |
|---|---|---|
| Untersuchung und Berichterstattung | Get-Acl | Ergebnisse sind Objekte, daher sind Filtern und CSV-Export einfach7 |
| Sicherung vor der Änderung | icacls /save | Speichert ACLs in einer Datei und stellt sie mit /restore unverändert wieder her13 |
| Routinemäßige Vergabe und Entfernung von Berechtigungen | icacls /grant, /remove | In einer Zeile erledigt, mit /t für rekursive Anwendung und der Möglichkeit, die Vererbung wieder zu aktivieren (/inheritancelevel)13 |
| Komplexe bedingte Änderungen | Set-Acl | Regeln lassen sich im Code zusammenstellen. Unterstützt -WhatIf12 |
| Wiederaufbau beschädigter ACLs | icacls /reset | Ersetzt durch die standardmäßig vererbte ACL (hohe Auswirkung; letztes Mittel)13 |
Unabhängig davon, was Sie nutzen, ist die Reihenfolge festgelegt. Sichern → -WhatIf (oder Zielbestätigung) → anwenden → im Nachhinein prüfen.
# 1. Vor der Änderung die ACLs in einer Datei sichern (/t für alles darunter, /c um bei Fehlern fortzufahren)
icacls "D:\share\sales" /save "C:\aclbackup\sales-acl.txt" /t /c
# Ohne Sicherung nicht zu Änderungen übergehen. Erfolg oder Misserfolg von icacls über den Exitcode prüfen
if ($LASTEXITCODE -ne 0) {
throw "ACL-Sicherung fehlgeschlagen (icacls ExitCode=$LASTEXITCODE). Änderungsvorgang wird abgebrochen"
}
# 2. Bei Änderung mit Set-Acl: drei Schritte ── abrufen, Regeln bearbeiten, anwenden
$path = 'D:\share\sales\estimate'
$acl = Get-Acl -LiteralPath $path
# Eine Regel, die der Vertriebsgruppe Änderungsrechte einräumt (an Unterordner und Dateien vererbt)
$rule = New-Object System.Security.AccessControl.FileSystemAccessRule(
'CONTOSO\SalesTeam', 'Modify', 'ContainerInherit,ObjectInherit', 'None', 'Allow')
# Zusätzliche Vergaben mit AddAccessRule vornehmen. SetAccessRule ersetzt bestehende Allow-Regeln
# für denselben Benutzer/dieselbe Gruppe, wodurch fein abgestufte, bereits konfigurierte Berechtigungen dieser Gruppe still verschwinden können
$acl.AddAccessRule($rule)
# 3. Zunächst mit -WhatIf bestätigen, „wo dies angewendet wird“, dann zum echten Lauf übergehen
Set-Acl -LiteralPath $path -AclObject $acl -WhatIf
# Wenn alles unauffällig ist: Set-Acl -LiteralPath $path -AclObject $acl
Da Set-Acl so funktioniert, dass es „den mit Get-Acl abgerufenen Sicherheitsdeskriptor als Modell anwendet“,12 führt eine Verwechslung des Ziels zwischen Abruf und Anwendung zu einem ungewollten kompletten Ersetzen einer ACL. Unglamouröse Gewohnheiten ── wie oben den Abrufpfad und den Anwendungspfad in derselben Variable zu halten und vor dem Anwenden ein -WhatIf einzuschieben ── verhindern Zwischenfälle. Auch das Unterbrechen der Vererbung (SetAccessRuleProtection) lässt sich innerhalb desselben Rahmens vornehmen,12 doch wie in Kapitel 4 gesehen wird unterbrochene Vererbung zu künftigen Verwaltungskosten, beschränken Sie neue Fälle daher auf wirklich notwendige Orte.
Die Gestaltung (SupportsShouldProcess, das Führen eines Nachweises, die Registrierung in der Aufgabenplanung), um diese Arbeitsfolge zu einem regelmäßig laufenden Skript weiterzuentwickeln, wird in „PowerShell-Skripte in der Praxis ── Protokollauswertung, Archivierung und Reporting sicher automatisieren“ behandelt und lässt sich unverändert übernehmen.
6. Wo Sie es ausführen ── Scans über UNC und das ausführende Konto
Zum Schluss ein Thema, das die Ergebnisse überraschend stark beeinflusst: „wo und als wer“ Sie ausführen.
- Als Regel gilt: Große Scans auf dem Server selbst ausführen. Eine rekursive Auflistung über einen UNC-Pfad (
\\fs01\share) macht den Metadatenabruf jeder einzelnen Datei zu einem Netzwerk-Roundtrip, sodass sich im Umfang von Hunderttausenden Dateien die benötigte Zeit um Größenordnungen ändert. Möchten Sie von einer Verwaltungsarbeitsstation aus arbeiten, ist die praktische Lösung, das Skript über PowerShell Remoting auf dem Server auszuführen und nur die resultierende CSV-Datei einzusammeln. Wie das geht, zeigt der gleichzeitig veröffentlichte Artikel „Einführung in PowerShell Remoting (WinRM)“. - Die Ergebnisse sind ein Schnappschuss der Berechtigungen des ausführenden Kontos. Orte, auf die Sie keinen Zugriff haben, fehlen sowohl in der Auflistung als auch bei Get-Acl. Führen Sie die Untersuchung mit einem Administratorkonto durch, und nehmen Sie Orte, die dennoch verweigert wurden (denied-paths.txt aus Kapitel 2), als „nicht geprüfte Orte“ in den Bericht auf.
- Bei Ausführung über UNC sollten Sie auch auf Fallstricke bei Anmeldeinformationen und Sitzungen achten. Laufwerksbuchstaben sind an die Anmeldesitzung gebunden, Sie können sich nicht mit mehreren Anmeldeinformationssätzen mit demselben Server verbinden (Fehler 1219), und weitere Fallstricke speziell beim Freigabezugriff sind in „Fallstricke bei Netzlaufwerken und UNC-Pfaden“ zusammengefasst.
7. Bewährte Praxis (Entscheidungstabelle)
| Frage | Optionen | Entscheidungshinweis |
|---|---|---|
| Umfang der Kapazitätsprüfung | Alles auf einmal / Je Ordner der ersten Ebene | Zunächst auf flacher Ebene ein grobes Bild gewinnen, dann nur bei den aufgeblähten Orten tiefer graben12 |
| Umgang mit Fehlern | Stoppen / Ignorieren / Protokollieren und fortsetzen | SilentlyContinue + ErrorVariable für „fortsetzen, aber alles protokollieren“. Verweigerte Zonen ausdrücklich im Bericht nennen3 |
| Kriterium für alte Dateien | LastAccessTime / LastWriteTime | Zugriffszeiten sind in vielen Umgebungen deaktiviert, daher ungeeignet. Mit der Abteilung auf Änderungszeit + Zeitraum einigen45 |
| Duplikaterkennung | Alle Dateien hashen / Nach Größe eingrenzen, dann hashen | Hashing ist E/A-intensiv. Nur für Gruppen gleicher Größe berechnen6 |
| Tiefe der ACL-Prüfung | Jeder Ordner / Flache Ebenen + Orte mit unterbrochener Vererbung | Ist die Vererbung intakt, verrät das übergeordnete Element alles. Schwerpunkt auf IsInherited=False und Protected=True78 |
| Werkzeug für Berechtigungsänderungen | icacls / Set-Acl | icacls für routinemäßige Änderungen und Sicherungen. Set-Acl + -WhatIf für Massenverarbeitung mit bedingten Verzweigungen1312 |
| Durchführung von Löschungen | Sofort löschen / Isolieren → beobachten → löschen | Nach Abteilungsbestätigung der Liste zunächst in einen Isolationsordner verschieben und löschen, wenn über einen festgelegten Zeitraum nichts auffällt |
8. Zusammenfassung
- Das Aufräumen eines Dateiservers beginnt mit einer Bestandsaufnahme, nicht mit dem Löschen. Erstellen Sie vier CSV-Artefakte ── eine Kapazitätsrangliste der ersten Ebene, eine Liste alter Dateien, Duplikatkandidaten und ein ACL-Register ── und nutzen Sie sie als Material für das Gespräch mit den Abteilungen.
- Protokollieren Sie im Gegenzug für das Nicht-Stoppen des Scans mit SilentlyContinue stets die verweigerten Orte mit ErrorVariable. Ein Ort, den Sie nicht zählen konnten, ist nicht „null“, sondern „unbekannt“.
- Stützen Sie die Beurteilung „alt“ auf LastWriteTime. Die NTFS-Zugriffszeiten sind in vielen Umgebungen deaktiviert und nicht vertrauenswürdig.
- Grenzen Sie Duplikate nach Größe ein und nutzen Sie dann Get-FileHash (standardmäßig SHA256). Eine Übereinstimmung ist ein „Kandidat“; die Entscheidung, was erhalten bleibt, treffen Sie mit der Fachseite.
- Exportieren Sie die Eigenschaft Access von Get-Acl als CSV und konzentrieren Sie sich auf direkte Einzelvergaben, Einträge, die nur eine SID zeigen, und unterbrochene Vererbung (AreAccessRulesProtected). Ein tiefes Eintauchen in SDDL ist nicht nötig.
- Nehmen Sie Änderungen in dieser Reihenfolge vor: Sicherung mit icacls /save → -WhatIf oder Zielbestätigung → anwenden → im Nachhinein prüfen. Führen Sie große Scans auf dem Server (oder über Remoting) mit Administratorrechten aus.
Verwandte Artikel
- Fallstricke bei Netzlaufwerken und UNC-Pfaden ── Der praktische Umgang mit Dateiservern (Netzwerkfreigaben) in Fachanwendungen
- MAX_PATH und die Fallstricke von Windows-Pfaden und -Dateinamen ── die 260-Zeichen-Grenze, reservierte Namen, abschließende Punkte und Groß-/Kleinschreibung
- Einführung in PowerShell Remoting (WinRM) ── Mehrere Windows-Rechner gebündelt verwalten
- Fehlerbehandlung und Wiederholungsdesign in PowerShell
- PowerShell-Skripte in der Praxis ── Protokollauswertung, Archivierung und Reporting sicher automatisieren
- Ein praktisches Vorgehen zur Identifizierung des Verfahrens hinter einer Hash-Zeichenfolge
Verwandte Beratungsbereiche
Die KomuraSoft LLC unterstützt bei der Erstellung von Skripten zur Bestandsaufnahme von Dateiserver-Kapazität und -Berechtigungen, übernimmt die Untersuchung im Rahmen einer Überarbeitung des Berechtigungsdesigns und automatisiert regelmäßige Berichterstattung (bis hin zum Betrieb über die Aufgabenplanung). Wir freuen uns auch über Anfragen bereits in der Phase „wir möchten zunächst nur den Ist-Zustand in Zahlen fassen“.
- Technische Beratung & Designüberprüfung
- Modernisierung & Wartung von Windows-Umgebungen
- Fehleruntersuchung & Ursachenanalyse
- Kontakt
Referenzlinks
-
Microsoft Learn, Get-ChildItem. Zur Auflistung von Elementen mit Get-ChildItem, zur Rekursion mit -Recurse und der Tiefenbeschränkung mit -Depth, sowie dazu, dass bei Verwendung von -Recurse die Angabe der Ziele mit -LiteralPath empfohlen wird, um eine Interpretation als Platzhalter zu vermeiden. ↩ ↩2 ↩3
-
Microsoft Learn, Measure-Object. Dazu, dass Measure-Object mit Optionen wie -Property Length -Sum Summe, Maximum, Minimum und Durchschnitt von Dateigrößen berechnen kann, sowie zum Beispiel der Aggregation von Dateien in einem Verzeichnis in Kombination mit Get-ChildItem. ↩ ↩2 ↩3
-
Microsoft Learn, about_CommonParameters. Dazu, dass -ErrorAction SilentlyContinue die Fehleranzeige unterdrückt und die Ausführung fortsetzt, dass -ErrorVariable Fehlerdatensätze in einer angegebenen Variable speichert, sowie dazu, dass ein vorangestelltes + beim Variablennamen ein Anhängen statt eines Überschreibens bewirkt. ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, fsutil behavior. Dazu, dass die Aktivierung bzw. Deaktivierung der Aktualisierung der letzten NTFS-Zugriffszeit über den Parameter disablelastaccess von fsutil behavior und den Registrierungswert NtfsDisableLastAccessUpdate gesteuert wird, sowie dazu, dass die Deaktivierung der Aktualisierung zur Beschleunigung des Datei-/Verzeichniszugriffs bereitgestellt wird. ↩ ↩2 ↩3
-
Microsoft Learn, [MS-FSA]: Appendix A: Product Behavior. Dazu, dass die Aktualisierung der letzten Zugriffszeit bei NTFS/ReFS seit Windows Vista standardmäßig deaktiviert ist, ab Windows 10 Version 1803 systemgesteuert wird und bei Serversystemen die Aktualisierung der letzten Zugriffszeit stets deaktiviert ist. ↩ ↩2 ↩3
-
Microsoft Learn, Get-FileHash. Dazu, dass der Standardalgorithmus von Get-FileHash SHA256 ist, dass zwei Dateien mit übereinstimmenden Hashwerten als inhaltlich identisch gelten können, sowie dazu, dass sich der Hashwert bei geändertem Dateinamen oder geänderter Erweiterung nicht ändert. ↩ ↩2 ↩3
-
Microsoft Learn, Get-Acl. Dazu, dass Get-Acl den Sicherheitsdeskriptor einer Datei oder Ressource abruft, standardmäßig die Liste der Zugriffssteuerungseinträge der DACL (Access) anzeigt, sowie dazu, dass sich dies auch im SDDL-Format (Eigenschaft Sddl) abrufen lässt. ↩ ↩2 ↩3 ↩4 ↩5 ↩6
-
Microsoft Learn, ObjectSecurity.AreAccessRulesProtected Property. Dazu, dass die Eigenschaft AreAccessRulesProtected zurückgibt, ob die DACL des Sicherheitsdeskriptors geschützt ist (keine Vererbung vom übergeordneten Element erhält). ↩ ↩2 ↩3
-
Microsoft Learn, about_Character_Encoding. Dazu, dass die Standardkodierungen in Windows PowerShell (5.1) je Cmdlet uneinheitlich sind, wobei Export-Csv Dateien in ASCII erstellt, sowie dazu, dass der Standard ab PowerShell 6 einheitlich auf utf8NoBOM festgelegt wurde. ↩ ↩2
-
Microsoft Learn, Export-Csv. Dazu, dass Export-Csv eine CSV-Datei mit den einzelnen Eigenschaften eines Objekts als Spalten erstellt, sowie dazu, dass der Standardwert von -Encoding in der PowerShell-7er-Reihe UTF8NoBOM ist, wobei sich UTF8BOM und andere ausdrücklich angeben lassen. ↩ ↩2
-
Microsoft Learn, Set-Acl. Dazu, dass Set-Acl die ACL eines Ziels unter Verwendung des über AclObject übergebenen Sicherheitsdeskriptors als Modell ändert, -WhatIf/-Confirm unterstützt, zum Vorgehen beim Erstellen einer FileSystemAccessRule und deren Hinzufügen mit SetAccessRule(), sowie zum Beispiel der Deaktivierung der Vererbung mit SetAccessRuleProtection() (mit der Option, bestehende vererbte Regeln beizubehalten). ↩ ↩2 ↩3 ↩4 ↩5
-
Microsoft Learn, icacls. Dazu, dass icacls der Befehl zum Anzeigen und Ändern von DACLs ist, dass sich ACLs mit /save in einer Datei speichern und mit /restore anwenden (wiederherstellen) lassen, sowie zu den wichtigsten Optionen /t (rekursiv), /c (bei Fehler fortsetzen), /grant, /remove, /reset und /inheritancelevel. ↩ ↩2 ↩3 ↩4 ↩5
-
Microsoft Learn, Maximum Path Length Limitation. Dazu, dass die Pfadlängenbegrenzung MAX_PATH der Windows-API 260 Zeichen beträgt, sowie dazu, dass die Beschränkung für viele Win32-Funktionen nur dann gelockert wird, wenn sowohl der Registrierungswert LongPathsEnabled als auch die Deklaration longPathAware der Anwendung vorhanden sind. ↩
Verwandte Artikel
Aktuelle Artikel mit denselben Schlagwörtern führen zu verwandten Themen weiter.
Zugangsdaten in PowerShell sicher handhaben — Klartext-Passwörter aus Ihren Skripten verbannen
Ein praktischer Leitfaden, um Klartext-Passwörter aus PowerShell-Skripten zu entfernen und sicher zu speichern: was SecureString wirklich...
Einführung in PowerShell Remoting (WinRM) ── Mehrere Windows-Rechner gebündelt verwalten
Eine Einführung in die gebündelte Verwaltung mehrerer Windows-Rechner mit PowerShell Remoting (WinRM). Behandelt die Funktionsweise und d...
PowerShell-Ausführungsrichtlinien und Skriptsignatur — Ein praktischer Leitfaden zum Abschied vom „Zudecken mit Bypass“
Die Ausführungsrichtlinie von PowerShell ist „eine Sicherheitsfunktion, keine Sicherheitsgrenze“. Dieser Artikel behandelt die Unterschie...
PowerShell absichern — Protokollierung, AMSI, Sprachmodi und JEA
Ein praxisorientierter Leitfaden, um PowerShell sicher zu nutzen, statt es zu verbieten. Behandelt das Aktivieren von Script Block Loggin...
Wo Sie nachsehen sollten, wenn ein PowerShell-Skript langsam ist — Arrays, Pipelines und Abgleich
Die klassischen Ursachen langsamer PowerShell-Skripte im Überblick. Warum += bei einem Array O(n²) ist, der Unterschied zwischen Pipeline...
Verwandte Themen
Diese Seiten ordnen den Artikel in einen größeren Leistungs- und Entscheidungskontext ein.
Technische Windows-Themen
Portal zu Windows-Entwicklung, Fehleranalyse und der Nutzung bestehender Assets.
Leistungen zu diesem Thema
Dieser Artikel ist direkt mit den folgenden Leistungen verbunden.
Windows-App-Entwicklung
Geschäftsanwendungen, Geräteintegration und Kommunikationstools von den Anforderungen bis zur Umsetzung.
Häufige Fragen
Fragen, die in Beratungen zu diesem Artikelthema häufig gestellt werden.
- Wie ermittle ich mit PowerShell die Gesamtgröße jedes Ordners?
- Zählen Sie Dateien mit Get-ChildItem und -Recurse auf, und summieren Sie sie anschließend mit Measure-Object über -Property Length -Sum. Statt alles auf einmal zu aggregieren, ist es in der Praxis sinnvoll, zunächst je Ordner der ersten Ebene direkt unterhalb der Freigabewurzel zu aggregieren, um ein grobes Gefühl dafür zu bekommen, wo sich der Großteil befindet. Nutzen Sie dabei -ErrorAction SilentlyContinue zusammen mit -ErrorVariable, und protokollieren Sie stets die Orte, die Sie wegen Zugriffsverweigerungen nicht zählen konnten. Verschlucken Sie die Fehler stillschweigend, erscheinen die aus der Summe ausgeschlossenen Orte als „Größe null“.
- Ist es zulässig, LastAccessTime (letzter Zugriffszeitpunkt) zur Bestimmung alter Dateien zu verwenden?
- Das wird nicht empfohlen. Auf NTFS ist die Aktualisierung der letzten Zugriffszeit in vielen Umgebungen aus Leistungsgründen standardmäßig deaktiviert (oder systemgesteuert), sodass eine Datei gelesen werden kann, ohne dass sich ihr Zeitstempel jemals ändert. Verwenden Sie LastWriteTime als Grundlage für Ihre Bestandsaufnahme und stellen Sie es den Abteilungen als die seit der letzten inhaltlichen Änderung verstrichene Zeit dar. Treffen Sie Archivierungsentscheidungen nicht mechanisch, sondern lassen Sie die Beteiligten die Liste prüfen, bevor Sie fortfahren.
- Was kann ich mit Get-Acl herausfinden? Muss ich SDDL lesen?
- Get-Acl ruft den Sicherheitsdeskriptor einer Datei oder eines Ordners ab, und aus der Eigenschaft Access können Sie ablesen, wer (IdentityReference), was tun darf (FileSystemRights), ob es sich um eine Zulassung oder Verweigerung handelt (AccessControlType) und ob es vererbt oder direkt vergeben ist (IsInherited). Ein Blick auf AreAccessRulesProtected verrät zudem, ob die Vererbung an diesem Ordner unterbrochen wurde. Es gibt auch eine Zeichenfolgenform namens SDDL, doch für Bestandsaufnahmezwecke lässt sich die Eigenschaft Access leichter als CSV mit Beteiligten teilen, und ein tiefes Eintauchen in SDDL ist nicht nötig.
- Sollte ich ACL-Änderungen mit PowerShells Set-Acl oder mit icacls vornehmen?
- Als Faustregel gilt: Nutzen Sie Get-Acl für Untersuchung und Berichterstattung, da es Ihnen Objekte liefert, und machen Sie icacls zu Ihrer ersten Wahl für tatsächliche Änderungen. icacls kann ACLs mit /save in einer Datei sichern und sie mit /restore unverändert wiederherstellen, was Sicherungen vor der Änderung und ein Zurückrollen einfach macht. Nutzen Sie Set-Acl, wird es zu einem dreistufigen Vorgang ── Abrufen mit Get-Acl, Bearbeiten der Regeln, dann Anwenden ── und Sie bestätigen die Ziele mit -WhatIf, bevor Sie es ausführen. In beiden Fällen gilt der Grundsatz, die Bestandsaufnahme und die Bestätigung durch die zuständigen Abteilungen abzuschließen, bevor Sie zu Änderungen übergehen.
- Ist es in Ordnung, die Dateiserverprüfung über einen UNC-Pfad auszuführen?
- Bei geringen Mengen ist das kein Problem, aber ein vollständiger Scan im Umfang von Hunderttausenden Dateien ist über das Netzwerk drastisch langsamer. Führen Sie ihn nach Möglichkeit direkt auf dem Dateiserver aus (oder über PowerShell Remoting) und bringen Sie nur die resultierende CSV-Datei mit zurück. Zudem hängen die Scanergebnisse von den Berechtigungen des ausführenden Kontos ab. Ordner, auf die Sie keinen Zugriff haben, fehlen in der Auflistung, führen Sie die Prüfung daher mit einem Administratorkonto aus und protokollieren Sie anschließend Orte, die weiterhin verweigert wurden, im Bericht als „nicht geprüfte Orte“.
Autorenprofil
Profilseite des Artikelautors.
Go Komura
Geschäftsführer von KomuraSoft LLC
Spezialisiert auf Windows-Softwareentwicklung, technische Beratung und Fehleranalyse, insbesondere bei bestehenden Systemen und schwer reproduzierbaren Störungen.