Files
windows-updater/docs/task-scheduler.md
2026-07-07 10:14:27 +02:00

8.1 KiB

Aufgabenplanung (Task Scheduler)

Das Script kann über die Windows Aufgabenplanung automatisch ausgeführt werden, z.B. täglich zu einer bestimmten Uhrzeit.

Voraussetzungen

  • Das Script muss als Administrator (bzw. SYSTEM) ausgeführt werden
  • Eine config.ini muss im Script-Verzeichnis vorhanden sein
  • PowerShell 5.1 muss verfügbar sein (auf allen unterstützten OS standardmäßig vorhanden)

Aufgabe per PowerShell erstellen (empfohlen)

Folgende Befehle in einer administrativen PowerShell ausführen:

Täglich um 04:00 Uhr

$action = New-ScheduledTaskAction `
    -Execute "powershell.exe" `
    -Argument "-ExecutionPolicy Bypass -NoProfile -File `"C:\scripts\windows-updater\windows-updater.ps1`""

$trigger = New-ScheduledTaskTrigger -Daily -At "04:00"

$principal = New-ScheduledTaskPrincipal `
    -UserId "SYSTEM" `
    -RunLevel Highest `
    -LogonType ServiceAccount

$settings = New-ScheduledTaskSettingsSet `
    -AllowStartIfOnBatteries `
    -DontStopIfGoingOnBatteries `
    -StartWhenAvailable `
    -ExecutionTimeLimit (New-TimeSpan -Hours 2)

Register-ScheduledTask `
    -TaskName "Windows Updater" `
    -Action $action `
    -Trigger $trigger `
    -Principal $principal `
    -Settings $settings `
    -Description "Automatische Windows Updates mit Benachrichtigung"

Wöchentlich (z.B. Sonntags um 03:00 Uhr)

$trigger = New-ScheduledTaskTrigger -Weekly -DaysOfWeek Sunday -At "03:00"

Den Rest wie oben, nur den Trigger austauschen.

Monatlich (z.B. erster Montag im Monat um 04:00 Uhr)

$trigger = New-ScheduledTaskTrigger -Weekly -DaysOfWeek Monday -WeeksInterval 1 -At "04:00"

# New-ScheduledTaskTrigger bietet kein -Monthly mit Wochentag-Option,
# daher wird der CIM-Trigger verwendet (entspricht GUI → "Monatlich"):
$trigger = New-CimInstance -CimClass (
    Get-CimClass -Namespace "Root/Microsoft/Windows/TaskScheduler" `
        -ClassName "MSFT_TaskMonthlyDOWTrigger"
) -ClientOnly -Property @{
    DaysOfWeek   = [int16]2          # Montag (So=1, Mo=2, Di=4, Mi=8, Do=16, Fr=32, Sa=64)
    WeeksOfMonth = [int16]1          # Erste Woche
    Months       = [int16]4095       # Alle 12 Monate (Bit-Summe: 1+2+4+...+2048)
    Enabled      = $true
}
$trigger.StartBoundary = (Get-Date "04:00").ToString("yyyy-MM-dd'T'HH:mm:ss")

Den Rest wie beim täglichen Beispiel, nur den Trigger austauschen.

Defender-Updates alle 4 Stunden

$action = New-ScheduledTaskAction `
    -Execute "powershell.exe" `
    -Argument "-ExecutionPolicy Bypass -NoProfile -File `"C:\scripts\windows-updater\windows-updater.ps1`" -DefenderOnly"

$trigger = New-ScheduledTaskTrigger -Once -At "00:00" -RepetitionInterval (New-TimeSpan -Hours 4)

$principal = New-ScheduledTaskPrincipal `
    -UserId "SYSTEM" `
    -RunLevel Highest `
    -LogonType ServiceAccount

$settings = New-ScheduledTaskSettingsSet `
    -AllowStartIfOnBatteries `
    -DontStopIfGoingOnBatteries `
    -StartWhenAvailable `
    -ExecutionTimeLimit (New-TimeSpan -Minutes 30)

Register-ScheduledTask `
    -TaskName "Windows Updater - Defender" `
    -Action $action `
    -Trigger $trigger `
    -Principal $principal `
    -Settings $settings `
    -Description "Defender-Definitionsupdates alle 4 Stunden"

Dual-Setup: Defender häufig + volle Updates monatlich

Zwei Aufgaben anlegen — eine für Defender (häufig), eine für alle Updates (selten):

# Aufgabe 1: Defender alle 4 Stunden (wie oben)
# Aufgabe 2: Volle Updates monatlich am ersten Sonntag um 03:00
$action = New-ScheduledTaskAction `
    -Execute "powershell.exe" `
    -Argument "-ExecutionPolicy Bypass -NoProfile -File `"C:\scripts\windows-updater\windows-updater.ps1`""

$trigger = New-CimInstance -CimClass (
    Get-CimClass -Namespace "Root/Microsoft/Windows/TaskScheduler" `
        -ClassName "MSFT_TaskMonthlyDOWTrigger"
) -ClientOnly -Property @{
    DaysOfWeek   = [int16]1          # Sonntag
    WeeksOfMonth = [int16]1          # Erste Woche
    Months       = [int16]4095       # Alle 12 Monate
    Enabled      = $true
}
$trigger.StartBoundary = (Get-Date "03:00").ToString("yyyy-MM-dd'T'HH:mm:ss")

$principal = New-ScheduledTaskPrincipal `
    -UserId "SYSTEM" `
    -RunLevel Highest `
    -LogonType ServiceAccount

$settings = New-ScheduledTaskSettingsSet `
    -AllowStartIfOnBatteries `
    -DontStopIfGoingOnBatteries `
    -StartWhenAvailable `
    -ExecutionTimeLimit (New-TimeSpan -Hours 2)

Register-ScheduledTask `
    -TaskName "Windows Updater - Monthly" `
    -Action $action `
    -Trigger $trigger `
    -Principal $principal `
    -Settings $settings `
    -Description "Vollständige Windows Updates monatlich"

Bestehende Aufgabe ändern

# Uhrzeit ändern
$trigger = New-ScheduledTaskTrigger -Daily -At "02:00"
Set-ScheduledTask -TaskName "Windows Updater" -Trigger $trigger

# Aufgabe deaktivieren
Disable-ScheduledTask -TaskName "Windows Updater"

# Aufgabe aktivieren
Enable-ScheduledTask -TaskName "Windows Updater"

# Aufgabe löschen
Unregister-ScheduledTask -TaskName "Windows Updater" -Confirm:$false

Aufgabe manuell starten (zum Testen)

Start-ScheduledTask -TaskName "Windows Updater"

Aufgabe per GUI erstellen

  1. taskschd.msc öffnen (oder "Aufgabenplanung" im Startmenü suchen)
  2. Rechtsklick auf "Aufgabenplanungsbibliothek" → "Aufgabe erstellen..."
  3. Reiter Allgemein:
    • Name: Windows Updater
    • "Mit höchsten Privilegien ausführen" aktivieren
    • "Unabhängig von der Benutzeranmeldung ausführen" wählen
    • Benutzer auf SYSTEM ändern
  4. Reiter Trigger:
    • Neuer Trigger → Täglich → Startzeit z.B. 04:00
  5. Reiter Aktionen:
    • Neue Aktion → "Programm starten"
    • Programm: powershell.exe
    • Argumente: -ExecutionPolicy Bypass -NoProfile -File "C:\scripts\windows-updater\windows-updater.ps1"
  6. Reiter Einstellungen:
    • "Aufgabe so schnell wie möglich nach einem verpassten Start ausführen" aktivieren
    • "Aufgabe beenden, falls sie länger ausgeführt wird als:" 2 Stunden

Wichtige Hinweise

SYSTEM-Kontext

Das Script läuft als SYSTEM-Benutzer. Beachte dabei:

  • Netzlaufwerke sind nicht verfügbar (SYSTEM hat keine zugeordneten Laufwerke)
  • Umgebungsvariablen wie %USERPROFILE% zeigen auf C:\Windows\system32\config\systemprofile
  • winget muss ggf. über den vollen Pfad aufgerufen werden (das Script behandelt dies automatisch)
  • Log-Dateien müssen in einem Verzeichnis liegen, auf das SYSTEM Schreibrechte hat (z.B. C:\logs\)

Execution Policy

Das Script wird mit -ExecutionPolicy Bypass aufgerufen, damit es ohne vorherige Änderung der systemweiten Execution Policy läuft. Dies ist sicher, da nur dieses spezifische Script betroffen ist.

Timeout

Die Standard-Ausführungszeitbegrenzung ist auf 2 Stunden gesetzt. Bei Servern mit vielen Updates kann dies ggf. erhöht werden:

$settings = New-ScheduledTaskSettingsSet `
    -ExecutionTimeLimit (New-TimeSpan -Hours 4)
Set-ScheduledTask -TaskName "Windows Updater" -Settings $settings

Log prüfen

Nach der ersten automatischen Ausführung die Logs prüfen:

# Heutiges Log anzeigen
$today = Get-Date -Format "yyyy-MM-dd"
Get-Content "C:\logs\windows-updater\windows-updater_$today.log"

# Letzten Lauf in der Aufgabenplanung prüfen
Get-ScheduledTaskInfo -TaskName "Windows Updater" | Select-Object LastRunTime, LastTaskResult

Exit-Codes in der Aufgabenplanung

Task Result Bedeutung
0 Erfolg
1 Fehler (Details im Log)
2 Neustart erforderlich (Auto-Reboot deaktiviert)
0x1 Allgemeiner Fehler
0x41301 Aufgabe läuft gerade

Empfohlene Zeitplanung

Szenario Empfehlung
Arbeitsstation Täglich, 12:00 (Mittagspause) mit DryRun, Manuelles Update später
Server (nicht-kritisch) Täglich, 03:00-05:00 mit Auto-Reboot
Server (kritisch/Produktion) Wöchentlich Sonntag, 03:00 mit DryRun. Manuell nach Prüfung
Testumgebung Täglich, 02:00 mit Auto-Reboot
Defender-Signaturen Alle 4 Stunden mit -DefenderOnly, zusätzlich zu einem der obigen Szenarien