Automatisierte Datenträgerbereinigung und Wartung

Ü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:

  1. cleanup_temp.ps1 – löscht alte Temp-Dateien
  2. rotate_logs.ps1 – rotiert Logs basierend auf PSD1
  3. optimize_winsxs.ps1 – WinSxS cleanup mit Größenmessung
  4. maintain_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.