- Übersicht
- Variablenreferenz
- Step 1: WinSxS-Optimierung
- Step 2: Temp-Ordnerbereinigung
- Step 3: Logrotation
- Step 4: Eventlog-Wartung
- Step 5: Skriptstruktur
- Step 6: Temp-Bereinigungsskript
- Step 7: Logrotation
- Step 8: WinSxS-Optimierungsskript
- Step 9: Eventlog-Wartungsskript
- Step 10: Registrierung der geplanten Tasks
- Zusammenfassung
Übersicht
Dieser Artikel erklärt, wie regelmäßige Datenträgerbereinigung auf Windows Server in vier Aufgaben aufgeteilt und mit getrennten PowerShell-Skripten automatisiert wird:
- WinSxS-Optimierung (Reduzierung des Component Store)
- Bereinigung der Temp-Ordner (ältere Dateien löschen)
- Logrotation (mehrere Pfade, je eigener Aufbewahrungszeitraum)
- Eventlog-Backup und -Bereinigung (monatlich)
Variablenreferenz
| Variable | Beispiel | Bedeutung |
|---|---|---|
<<ADMIN_USER>> |
Administrator |
Benutzer, der geplante Tasks ausführt |
<<LOG_PATH>> |
C:\Maintenance\Logs |
Gemeinsames Logverzeichnis |
<<BACKUP_PATH>> |
C:\Maintenance\Backups |
Backupordner für Eventlogs |
<<DAILY_TEMP_TASK_NAME>> |
DailyTempCleanup |
Taskname: Temp-Bereinigung |
<<DAILY_LOG_TASK_NAME>> |
DailyLogRotation |
Taskname: Logrotation |
<<MONTHLY_WINSXS_TASK_NAME>> |
MonthlyWinSxSCleanup |
Taskname: WinSxS-Optimierung |
<<MONTHLY_EVENTLOG_TASK_NAME>> |
MonthlyEventLogMaintenance |
Taskname: Eventlog-Wartung |
Step 1: WinSxS-Optimierung
Der Ordner C:\Windows\WinSxS speichert Komponenten und Updates und wächst über die Zeit an. Optimierung erfolgt mit DISM:
Dism /Online /Cleanup-Image /AnalyzeComponentStore
Dism /Online /Cleanup-Image /StartComponentCleanup
Dism /Online /Cleanup-Image /StartComponentCleanup /ResetBase
/ResetBase entfernt alte Versionen endgültig. Monatliche Ausführung ist üblich.
Step 2: Temp-Ordnerbereinigung
In $env:TEMP und C:\Windows\Temp sammeln sich viele kurzlebige Dateien. Realistisch ist die Löschung von Dateien, die älter als eine definierte Anzahl von Tagen sind.
Step 3: Logrotation
Logverzeichnisse wachsen unbegrenzt. Mit einer PSD1-Konfigurationsdatei können mehrere Pfade und eigene Aufbewahrungszeiten definiert werden.
Step 4: Eventlog-Wartung
Eventlogs sollten gesichert werden, bevor sie geleert werden. Übliche Ausführung: monatlich.
wevtutil epl System C:\Logs\System_20250101.evtx
wevtutil cl System
Step 5: Skriptstruktur
Folgende Skripte werden verwendet:
cleanup_temp.ps1– löscht alte Temp-Dateienrotate_logs.ps1– rotiert Logs basierend auf PSD1optimize_winsxs.ps1– WinSxS cleanup mit Größenmessungmaintain_eventlogs.ps1– Eventlogs sichern und leeren
Verzeichnisstruktur:
C:\
└─ Maintenance\
├─ cleanup_temp.ps1
├─ rotate_logs.ps1
├─ optimize_winsxs.ps1
├─ maintain_eventlogs.ps1
├─ log_rotation.psd1
├─ Logs\
└─ Backups\
Step 6: Temp-Bereinigungsskript
cleanup_temp.ps1
param(
[string[]]$TempPaths = @("$env:TEMP", "C:\Windows\Temp"),
[int]$DaysToKeep = 7,
[string]$LogPath = "<<LOG_PATH>>"
)
if (-not (Test-Path $LogPath)) {
New-Item $LogPath -ItemType Directory -Force | Out-Null
}
$timestamp = Get-Date -Format 'yyyyMMdd_HHmmss'
$logFile = Join-Path $LogPath "cleanup_temp_$timestamp.log"
function Write-Log($Message) {
"[{0}] {1}" -f (Get-Date -Format "yyyy-MM-dd HH:mm:ss"), $Message |
Tee-Object -FilePath $logFile -Append
}
Write-Log "===== Temp cleanup started ====="
Write-Log "DaysToKeep = $DaysToKeep"
$limitDate = (Get-Date).AddDays(-$DaysToKeep)
foreach ($path in $TempPaths) {
if (-not (Test-Path $path)) {
Write-Log "Skip: Not found -> $path"
continue
}
Write-Log "Processing: $path"
Get-ChildItem -Path $path -Recurse -Force -ErrorAction SilentlyContinue |
Where-Object { $_.LastWriteTime -lt $limitDate } |
ForEach-Object {
try {
Remove-Item -LiteralPath $_.FullName -Force -Recurse -ErrorAction Stop
Write-Log "Deleted: $($_.FullName)"
}
catch {
Write-Log "Failed: $($_.FullName) - $($_.Exception.Message)"
}
}
}
Write-Log "===== Temp cleanup finished ====="
Step 7: Logrotation
PSD1-Konfiguration
log_rotation.psd1
@{
RotationTargets = @(
@{
Path = "C:\Logs\App1"
DaysToKeep = 7
},
@{
Path = "C:\Logs\App2"
DaysToKeep = 30
},
@{
Path = "<<LOG_PATH>>"
DaysToKeep = 7
}
)
}
Logrotation-Skript
rotate_logs.ps1
param(
[string]$ConfigPath = "C:\Maintenance\log_rotation.psd1",
[string]$LogPath = "<<LOG_PATH>>"
)
if (-not (Test-Path $LogPath)) {
New-Item $LogPath -ItemType Directory -Force | Out-Null
}
$timestamp = Get-Date -Format 'yyyyMMdd_HHmmss'
$logFile = Join-Path $LogPath "rotate_logs_$timestamp.log"
function Write-Log($Message) {
"[{0}] {1}" -f (Get-Date -Format "yyyy-MM-dd HH:mm:ss"), $Message |
Tee-Object -FilePath $logFile -Append
}
Write-Log "===== Log rotation started ====="
Write-Log "ConfigPath = $ConfigPath"
if (-not (Test-Path $ConfigPath)) {
Write-Log "Config file not found. Exit."
exit 1
}
try {
$config = Import-PowerShellDataFile -Path $ConfigPath
}
catch {
Write-Log "Config load failed: $($_.Exception.Message)"
exit 1
}
foreach ($target in $config.RotationTargets) {
$targetPath = $target.Path
$daysToKeep = [int]$target.DaysToKeep
if (-not (Test-Path $targetPath)) {
Write-Log "Skip (not found): $targetPath"
continue
}
$limitDate = (Get-Date).AddDays(-$daysToKeep)
Write-Log "Path=$targetPath DaysToKeep=$daysToKeep Limit=$limitDate"
Get-ChildItem -Path $targetPath -Recurse -Include *.log -ErrorAction SilentlyContinue |
Where-Object { $_.LastWriteTime -lt $limitDate } |
ForEach-Object {
try {
Remove-Item -LiteralPath $_.FullName -Force -ErrorAction Stop
Write-Log "Deleted: $($_.FullName)"
}
catch {
Write-Log "Failed: $($_.FullName) - $($_.Exception.Message)"
}
}
}
Write-Log "===== Log rotation finished ====="
Step 8: WinSxS-Optimierungsskript
optimize_winsxs.ps1
param(
[string]$LogPath = "<<LOG_PATH>>"
)
if (-not (Test-Path $LogPath)) {
New-Item $LogPath -ItemType Directory -Force | Out-Null
}
$timestamp = Get-Date -Format 'yyyyMMdd_HHmmss'
$logFile = Join-Path $LogPath "optimize_winsxs_$timestamp.log"
function Write-Log($Message) {
"[{0}] {1}" -f (Get-Date -Format "yyyy-MM-dd HH:mm:ss"), $Message |
Tee-Object -FilePath $logFile -Append
}
Write-Log "===== WinSxS optimization started ====="
$winsxsPath = "C:\Windows\WinSxS"
Write-Log "Measuring WinSxS size before..."
$sizeBefore = (Get-ChildItem $winsxsPath -Recurse -Force -ErrorAction SilentlyContinue |
Measure-Object Length -Sum).Sum
$sizeBeforeGB = [math]::Round($sizeBefore / 1GB, 2)
Write-Log "Before: $sizeBeforeGB GB"
Dism /Online /Cleanup-Image /AnalyzeComponentStore |
Out-File (Join-Path $LogPath "dism_before_$timestamp.txt")
Write-Log "Running StartComponentCleanup /ResetBase..."
Dism /Online /Cleanup-Image /StartComponentCleanup /ResetBase |
Out-File (Join-Path $LogPath "dism_exec_$timestamp.txt")
Write-Log "Measuring WinSxS size after..."
$sizeAfter = (Get-ChildItem $winsxsPath -Recurse -Force -ErrorAction SilentlyContinue |
Measure-Object Length -Sum).Sum
$sizeAfterGB = [math]::Round($sizeAfter / 1GB, 2)
Write-Log "After: $sizeAfterGB GB"
Write-Log ("Reduced: {0} GB" -f ([math]::Round(($sizeBefore - $sizeAfter)/1GB,2)))
Dism /Online /Cleanup-Image /AnalyzeComponentStore |
Out-File (Join-Path $LogPath "dism_after_$timestamp.txt")
Write-Log "===== WinSxS optimization finished ====="
Step 9: Eventlog-Wartungsskript
maintain_eventlogs.ps1
param(
[string]$LogPath = "<<LOG_PATH>>",
[string]$BackupPath = "<<BACKUP_PATH>>",
[string[]]$EventLogs = @("System", "Application", "Security")
)
if (-not (Test-Path $LogPath)) {
New-Item $LogPath -ItemType Directory -Force | Out-Null
}
if (-not (Test-Path $BackupPath)) {
New-Item $BackupPath -ItemType Directory -Force | Out-Null
}
$timestamp = Get-Date -Format 'yyyyMMdd_HHmmss'
$logFile = Join-Path $LogPath "eventlog_maint_$timestamp.log"
function Write-Log($Message) {
"[{0}] {1}" -f (Get-Date -Format "yyyy-MM-dd HH:mm:ss"), $Message |
Tee-Object -FilePath $logFile -Append
}
Write-Log "===== Event log maintenance started ====="
Write-Log "BackupPath = $BackupPath"
foreach ($name in $EventLogs) {
$destEvtx = Join-Path $BackupPath ("{0}_{1}.evtx" -f $name, $timestamp)
try {
Write-Log "Export: $name -> $destEvtx"
wevtutil epl $name $destEvtx
Write-Log "Clear: $name"
wevtutil cl $name
}
catch {
Write-Log "Failed: $name - $($_.Exception.Message)"
}
}
Write-Log "===== Event log maintenance finished ====="
Step 10: Registrierung der geplanten Tasks
Weitere Details zu schtasks.exe finden Sie hier:
Automatisierte Verwaltung geplanter Tasks mit schtasks.exe
Tägliche Temp-Bereinigung
schtasks /Create `
/TN "<<DAILY_TEMP_TASK_NAME>>" `
/TR "powershell.exe -NoProfile -ExecutionPolicy Bypass -File C:\Maintenance\cleanup_temp.ps1 -DaysToKeep 7 -LogPath <<LOG_PATH>>" `
/SC DAILY `
/ST 02:00 `
/RU "<<ADMIN_USER>>" `
/RL HIGHEST `
/F
Tägliche Logrotation
schtasks /Create `
/TN "<<DAILY_LOG_TASK_NAME>>" `
/TR "powershell.exe -NoProfile -ExecutionPolicy Bypass -File C:\Maintenance\rotate_logs.ps1 -ConfigPath C:\Maintenance\log_rotation.psd1 -LogPath <<LOG_PATH>>" `
/SC DAILY `
/ST 02:30 `
/RU "<<ADMIN_USER>>" `
/RL HIGHEST `
/F
Monatliche WinSxS-Optimierung
schtasks /Create `
/TN "<<MONTHLY_WINSXS_TASK_NAME>>" `
/TR "powershell.exe -NoProfile -ExecutionPolicy Bypass -File C:\Maintenance\optimize_winsxs.ps1 -LogPath <<LOG_PATH>>" `
/SC MONTHLY `
/D 1 `
/ST 03:00 `
/RU "<<ADMIN_USER>>" `
/RL HIGHEST `
/F
Monatliche Eventlog-Wartung
schtasks /Create `
/TN "<<MONTHLY_EVENTLOG_TASK_NAME>>" `
/TR "powershell.exe -NoProfile -ExecutionPolicy Bypass -File C:\Maintenance\maintain_eventlogs.ps1 -LogPath <<LOG_PATH>>" `
/SC MONTHLY `
/D 1 `
/ST 03:30 `
/RU "<<ADMIN_USER>>" `
/RL HIGHEST `
/F
Zusammenfassung
Dieser Artikel zeigt eine strukturierte, skriptbasierte Lösung für die regelmäßige Datenträgerwartung auf Windows Server:
- Temp-Dateien: tägliche Bereinigung nach Aufbewahrungszeit
- Logrotation: flexible PSD1-Konfiguration, mehrere Pfade
- WinSxS-Optimierung: monatlich, inklusive Größenvergleich
- Eventlog-Wartung: monatliches Backup und Clear
Damit entsteht eine robuste und erweiterbare Wartungsautomatisierung für produktive Serverumgebungen.
