Geautomatiseerde schijfopruiming en onderhoud

Overzicht

In dit artikel wordt uitgelegd hoe u periodieke schijfopruiming op Windows Server in vier taken opdeelt en per doel een eigen PowerShell-script gebruikt om de uitvoering te automatiseren:

  • Optimalisatie van WinSxS (verkleinen van de component store)
  • Opschonen van Temp-mappen (op basis van bewaartermijn)
  • Logrotatie (meerdere paden, per pad eigen bewaartermijn)
  • Back-up en opschonen van eventlogs

Variabelenconventies

Variabelenaam Voorbeeld Opmerking
<<ADMIN_USER>> Administrator Gebruiker waarmee de geplande taak wordt uitgevoerd
<<LOG_PATH>> C:\Maintenance\Logs Gemeenschappelijke logdirectory
<<BACKUP_PATH>> C:\Maintenance\Backups Directory voor eventlog-back-ups
<<DAILY_TEMP_TASK_NAME>> DailyTempCleanup Taaknaam voor Temp-opruiming
<<DAILY_LOG_TASK_NAME>> DailyLogRotation Taaknaam voor logrotatie
<<MONTHLY_WINSXS_TASK_NAME>> MonthlyWinSxSCleanup Taaknaam voor WinSxS-optimalisatie
<<MONTHLY_EVENTLOG_TASK_NAME>> MonthlyEventLogMaintenance Taaknaam voor eventlog-onderhoud

Step 1: Overzicht WinSxS-mapoptimalisatie

C:\Windows\WinSxS is de component store waarin updates en componentversies worden opgeslagen. Deze map groeit naarmate het systeem langer in gebruik is. Optimalisatie gebeurt met DISM:

# Analyseren van de component store
Dism /Online /Cleanup-Image /AnalyzeComponentStore

# Opschonen van oude componenten
Dism /Online /Cleanup-Image /StartComponentCleanup

# Oude versies volledig verwijderen (rollback niet meer mogelijk)
Dism /Online /Cleanup-Image /StartComponentCleanup /ResetBase

Omdat /ResetBase de-installatie van oude updates onmogelijk maakt, is uitvoering bijvoorbeeld eens per maand tijdens een gepland onderhoudsvenster aan te raden.


Step 2: Overzicht Temp-mapopruiming

In $env:TEMP en C:\Windows\Temp worden tijdelijke bestanden van installers en applicaties verzameld. In de praktijk is het veiliger om “alle bestanden ouder dan X dagen te verwijderen” dan om alles zonder onderscheid te wissen.


Step 3: Overzicht logrotatie

Logdirectories groeien mee met het aantal applicaties en kunnen op termijn veel schijfruimte innemen. Omdat u per directory een eigen bewaartermijn wilt kunnen instellen, is een ontwerp op basis van een PSD1-configuratiebestand geschikt en flexibel.


Step 4: Overzicht eventlog-onderhoud

Eventlogs zijn essentieel voor storingsanalyse en audit. Ze mogen niet te vaak zonder back-up worden geleegd. Een veelgebruikt patroon is: eerst een back-up (.evtx) maken en daarna het log wissen, bijvoorbeeld ongeveer één keer per maand.

# Basisvoorbeeld voor back-up en leegmaken
wevtutil epl System C:\Logs\System_20250101.evtx
wevtutil cl System

Step 5: Ontwerp van de scripts

In dit artikel worden vier afzonderlijke scripts gebruikt:

  1. cleanup_temp.ps1
    Verwijdert oude bestanden uit Temp-mappen (bewaardagen als parameter).

  2. rotate_logs.ps1
    Verwijdert verouderde .log-bestanden onder paden die in een PSD1-configuratiebestand zijn vastgelegd.

  3. optimize_winsxs.ps1
    Voert /StartComponentCleanup /ResetBase op WinSxS uit en logt de verschilgrootte.

  4. maintain_eventlogs.ps1
    Maakt een back-up van opgegeven eventlogs en leegt deze daarna.

Met deze opzet kunnen de scripts per doel afzonderlijk vanuit Taakplanner worden uitgevoerd; dag- en maandtaken en beleidswijzigingen zijn eenvoudig aanpasbaar.

Voorbeeldentructuur:

C:\
└─ Maintenance\
   ├─ cleanup_temp.ps1               # Temp-mapopruiming (dagelijks)
   ├─ rotate_logs.ps1                # Logrotatie (dagelijks)
   ├─ optimize_winsxs.ps1            # WinSxS-optimalisatie (maandelijks)
   ├─ maintain_eventlogs.ps1         # Eventlog-back-up + clear (maandelijks)
   │
   ├─ log_rotation.psd1              # Configuratiebestand logrotatie (meerdere paden + dagen)
   │
   ├─ Logs\                          # Uitvoeringslogs van de scripts
   │    ├─ cleanup_temp_*.log
   │    ├─ rotate_logs_*.log
   │    ├─ optimize_winsxs_*.log
   │    └─ eventlog_maint_*.log
   │
   └─ Backups\                       # Back-up van eventlogs (EVTX)
        ├─ System_YYYYMMDD.evtx
        ├─ Application_YYYYMMDD.evtx
        └─ Security_YYYYMMDD.evtx

Step 6: Temp-opruimscript

Dit script verwijdert alle bestanden en mappen onder de in $TempPaths opgegeven paden die ouder zijn dan DaysToKeep dagen en schrijft het resultaat naar een logbestand.
DaysToKeep en LogPath kunnen via parameters worden overschreven.

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

Configuratiebestand voor logrotatie (PSD1)

Dit PSD1-bestand definieert meerdere doelmappen en bijbehorende bewaartermijnen. Voor een nieuwe logdirectory hoeft u alleen een extra entry toe te voegen.

log_rotation.psd1

@{
    RotationTargets = @(
        @{
            Path       = "C:\Logs\App1"
            DaysToKeep = 7
        },
        @{
            Path       = "C:\Logs\App2"
            DaysToKeep = 30
        },
        @{
            Path       = "<<LOG_PATH>>"
            DaysToKeep = 7
        }
    )
}

Logrotatiescript

Dit script leest RotationTargets uit de PSD1 en verwijdert .log-bestanden onder elk pad die ouder zijn dan de geconfigureerde bewaartermijn. Alle acties worden gelogd.

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-optimalisatiescript

Dit script meet de grootte van de WinSxS-directory vóór en na de opruiming en voert vervolgens /StartComponentCleanup /ResetBase uit. Zo kunt u de gerealiseerde besparing kwantificeren.

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-onderhoudsscript

Dit script maakt een .evtx-back-up van de opgegeven logs (standaard System / Application / Security) in de back-updirectory en leegt daarna het oorspronkelijke log. Het logbestand bevat alle details over succes en fouten.

maintain_eventlogs.ps1

param(
    [string]$LogPath = "<<LOG_PATH>>",                  # Opslaglocatie voor .log
    [string]$BackupPath = "<<BACKUP_PATH>>",            # Opslaglocatie voor EVTX-back-ups
    [string[]]$EventLogs = @("System", "Application", "Security")
)

# Logdirectory aanmaken
if (-not (Test-Path $LogPath)) {
    New-Item $LogPath -ItemType Directory -Force | Out-Null
}

# Back-updirectory aanmaken
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) {

    # Per log een unieke EVTX-bestandsnaam
    $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: Voorbeelden voor registratie in Taakplanner

Alle onderhoudsscripts worden hier met schtasks.exe geregistreerd.
Voor een volledige uitleg van opties zoals /SC, /D, /ST, /RU enzovoort, zie:

Beheer van Taakplanner met schtasks.exe


Dagelijkse Temp-opruiming

Dagelijks om 2:00 wordt cleanup_temp.ps1 uitgevoerd en worden Temp-bestanden ouder dan 7 dagen verwijderd.
De taak draait onder <<ADMIN_USER>> met hoogste machtigingen (/RL HIGHEST).

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

Dagelijkse logrotatie

Dagelijks om 2:30 wordt rotate_logs.ps1 uitgevoerd en worden .log-bestanden in meerdere directories op basis van de PSD1-configuratie geroteerd.
De bewaartermijnen worden volledig door log_rotation.psd1 bepaald.

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

Maandelijkse WinSxS-optimalisatie

Elke 1e dag van de maand om 3:00 wordt optimize_winsxs.ps1 uitgevoerd en wordt StartComponentCleanup /ResetBase inclusief maatmeting gedraaid.
Omdat rollback niet meer mogelijk is, moet dit binnen een gepland onderhoudsvenster gebeuren.

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

Maandelijks eventlog-onderhoud

Elke 1e dag van de maand om 3:30 wordt maintain_eventlogs.ps1 uitgevoerd en worden eventlogs zoals System / Application / Security naar de back-updirectory wegschreven en daarna geleegd.
Pas -EventLogs en -BackupPath aan volgens uw auditvereisten.

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

Samenvatting

Dit artikel beschrijft hoe u schijfonderhoud op Windows Server per doel in aparte scripts opsplitst en hoe u met een PSD1-configuratiebestand logrotatie flexibel beheert.

  • Temp-opruiming: dagelijks, met instelbare bewaartermijn
  • Logrotatie: meerdere paden en bewaartermijnen via PSD1
  • WinSxS-optimalisatie: maandelijks, inclusief logging van besparing
  • Eventlog-onderhoud: maandelijks, back-up plus clear

Zo ontstaat een robuuste, uitbreidbare automatisering van onderhoudstaken voor productieservers.