Windows Updates mit PowerShell automatisieren
Wenn du Windows Updates automatisieren möchtest, ist PowerShell eine ziemlich praktische Lösung. Genau dafür habe ich mir dieses Windows Updater Script gebaut: Es installiert Windows Updates kontrolliert, kann optional Winget-Pakete aktualisieren, Defender-Signaturen gezielt separat einspielen und informiert dich danach per ntfy, E-Mail oder Microsoft Teams. Also genau das, was man gerne hätte, wenn man nicht jeden Server einzeln von Hand anstupsen möchte.
Ich wollte kein überladenes Management-Tool, sondern ein nachvollziehbares Script, das auf Windows Clients und Servern sauber läuft. Mit Logs, Ausschlüssen, Reboot-Steuerung, Kommandozeilen-Parametern und einer Konfiguration, die man auch nach drei Wochen noch versteht. Das ist manchmal mehr wert als ein Dashboard mit 27 bunten Kacheln.

🧭 Was macht das Windows Updater Script?
Kurz gesagt: Das Script kümmert sich um Windows Updates, ohne dass du dich jedes Mal durch die Windows-Oberfläche klicken musst. Im Hintergrund nutzt es das bekannte PowerShell-Modul PSWindowsUpdate. Falls das Modul auf dem System noch nicht vorhanden ist, versucht das Script es zuerst online aus der PowerShell Gallery zu installieren.
Falls das nicht klappt, zum Beispiel wegen Firewall, Proxy oder einem Server ohne Internetzugang, nutzt es die mitgelieferte Offline-Kopie aus dem Repository. Das ist gerade bei Servern praktisch, die nicht jeden Ausflug ins Internet machen dürfen.
- Windows Updates installieren
- Defender-Definitionsupdates separat installieren
- Microsoft Update optional einbinden, also zum Beispiel auch Office, .NET und Treiber
- bestimmte KB-Updates oder Treiber-Updates ausschließen
- fehlgeschlagene Updates mehrfach versuchen
- Winget-Updates optional mit ausführen
- Logs pro Tag schreiben und alte Logs automatisch aufräumen
- Benachrichtigungen per ntfy, E-Mail und Microsoft Teams senden
- Neustarts sofort, verzögert oder zu einer festen Uhrzeit auslösen
- Neustarts bei Bedarf sogar erzwingen
- Pre- und Post-Update-Hooks ausführen
- Update-History als XLSX oder CSV speichern
Das Script erkennt außerdem das Betriebssystem und schreibt in die Logs, ob ein WSUS konfiguriert ist. Gerade letzteres spart bei der Fehlersuche gerne mal ein paar Minuten. Oder Stunden. Je nachdem, wie sehr WSUS heute mitspielen möchte.
🧩 Voraussetzungen
Du brauchst nicht viel, aber ein paar Dinge müssen passen. Das Script ist für Windows 10, Windows 11 und Windows Server 2016, 2019, 2022 sowie 2025 gedacht. Wichtig ist außerdem PowerShell 5.1 und eine Ausführung mit Administratorrechten.
Wenn du das Script später über die Aufgabenplanung laufen lässt, sollte es mit höchsten Privilegien ausgeführt werden. Ich nutze dafür gerne den Benutzer SYSTEM, weil das für Server-Jobs sauber und unabhängig von einer Benutzeranmeldung funktioniert.
⚙️ Installation und erster Start
Ich lege solche Scripte gerne unter C:\scripts ab. Das ist kein Muss, aber schön eindeutig. Wichtig ist nur, dass das Script und die config.ini später im gleichen Verzeichnis liegen.
git clone https://git.techniverse.net/scriptos/windows-updater.git C:\scripts\windows-updater
Danach kopierst du die Beispielkonfiguration:
Copy-Item C:\scripts\windows-updater\config.example.ini C:\scripts\windows-updater\config.ini
Die config.ini ist die zentrale Schaltstelle. Dort legst du fest, ob Updates nur geprüft oder wirklich installiert werden, ob alle Updates oder nur Defender-Signaturen laufen sollen, ob ein Reboot erlaubt ist, welche Benachrichtigungskanäle aktiv sind und ob Winget mitlaufen soll.
config.ini für den Start
Für den ersten Test würde ich immer mit DryRun = true starten. Dann scannt das Script nach verfügbaren Updates, installiert aber noch nichts. Das ist die entspannte Variante, um Logging, Modul-Ladung und Benachrichtigungen zu prüfen.
[General] LogPath = C:\logs\windows-updater LogRetentionDays = 365 DryRun = true UpdateType = All [WindowsUpdate] UseMicrosoftUpdate = true ExcludeKBs = ExcludeDrivers = false RetryCount = 3 [Reboot] Enabled = false ForceReboot = false
Wenn der Test sauber aussieht, kannst du DryRun auf false setzen. Falls du Treiber-Updates grundsätzlich nicht automatisch installieren möchtest, setzt du zusätzlich ExcludeDrivers = true. Mit UpdateType = DefenderOnly kannst du die Config außerdem so einstellen, dass nur Defender-Definitionsupdates installiert werden.
Script manuell ausführen
Starte eine administrative PowerShell und führe das Script so aus:
powershell.exe -ExecutionPolicy Bypass -NoProfile -File "C:\scripts\windows-updater\windows-updater.ps1"
Du kannst das Verhalten aber auch direkt beim Aufruf überschreiben. Das ist besonders praktisch, wenn du dasselbe Script für verschiedene Aufgaben verwenden möchtest.
# Nur Defender-Definitionsupdates installieren powershell.exe -ExecutionPolicy Bypass -NoProfile -File "C:\scripts\windows-updater\windows-updater.ps1" -DefenderOnly # Nur prüfen, nichts installieren powershell.exe -ExecutionPolicy Bypass -NoProfile -File "C:\scripts\windows-updater\windows-updater.ps1" -DryRun # Alternative Konfigurationsdatei verwenden powershell.exe -ExecutionPolicy Bypass -NoProfile -File "C:\scripts\windows-updater\windows-updater.ps1" -ConfigFile "D:\configs\server-config.ini"
Parameter haben Vorrang vor der Config. Wenn in der config.ini also DryRun = false steht, du das Script aber mit -DryRun startest, wird trotzdem nur geprüft. Sehr angenehm für Tests, weil man nicht jedes Mal an der Config herumfummeln muss.
Das Script schreibt eine tägliche Logdatei unterhalb des konfigurierten Log-Pfads. Standardmäßig ist das:
C:\logs\windows-updater\windows-updater_YYYY-MM-DD.log

Wenn etwas schiefgeht, ist diese Datei deine erste Anlaufstelle. Dort siehst du zum Beispiel, ob das Modul geladen wurde, ob ein WSUS erkannt wurde, welche Updates gefunden wurden und ob ein Neustart ansteht.
🔔 Benachrichtigungen einrichten
Updates sind schön. Zu wissen, was passiert ist, ist schöner. Deshalb kann das Script Benachrichtigungen über ntfy, E-Mail und Microsoft Teams senden. Du kannst die Kanäle einzeln oder gemeinsam aktivieren.
ntfy ist dabei mein Favorit für schnelle Push-Meldungen. Kein großes Setup, keine halbe Monitoring-Plattform, einfach eine Nachricht aufs Handy oder in den Browser.
[Notification] NtfyEnabled = true NtfyUrl = https://ntfy.sh NtfyTopic = windows-updates-meinserver NtfyToken = NtfyPriority = default

Für E-Mail hinterlegst du SMTP-Server, Absender und Empfänger. Optional kann das Script einen HTML-Report mit Update-Tabelle versenden. Das ist besonders angenehm, wenn du mehrere Systeme pflegst und später nachvollziehen willst, was wo installiert wurde.
[Notification] EmailEnabled = true SmtpServer = smtp.example.com SmtpPort = 587 SmtpUseSsl = true SmtpUsername = user@example.com SmtpPassword = geheim123 EmailFrom = updater@example.com EmailTo = admin@example.com EmailHtmlReport = true
Außerdem kannst du steuern, welche Ereignisse überhaupt eine Meldung auslösen. Wenn dich zum Beispiel „Keine Updates verfügbar“ nicht interessiert, schaltest du genau dieses Ereignis ab. Fehler, Reboot-Hinweise und Update-Zusammenfassungen würde ich persönlich aber eingeschaltet lassen.
🔁 Reboot, Winget und Service-Watchdog
Beim Thema Updates kommt irgendwann immer die Frage: Darf der Server danach neu starten? Das Script kennt drei Modi. Sofort, verzögert oder geplant zu einer Uhrzeit. Gerade auf Servern nutze ich lieber eine geplante Uhrzeit, damit der Neustart in ein Wartungsfenster fällt.
[Reboot] Enabled = true ForceReboot = false Mode = scheduled ScheduledTime = 03:00
Wenn du Enabled = false setzt und trotzdem ein Neustart erforderlich ist, beendet sich das Script mit Exit-Code 2. Das ist praktisch für Monitoring oder die Aufgabenplanung, weil du den Zustand sauber erkennen kannst.
Neu ist außerdem ForceReboot. Damit kannst du einen Neustart auch dann auslösen, wenn Windows selbst keinen offenen Reboot meldet. Das würde ich nicht blind überall setzen, aber für bestimmte Wartungsfenster kann es sinnvoll sein. Wichtig: ForceReboot = true funktioniert nur sinnvoll, wenn auch Enabled = true gesetzt ist.
Winget kannst du zusätzlich aktivieren. Auf Windows 10 und Windows 11 ist das spannend, weil damit installierte Pakete aktualisiert werden können. Auf Windows Server ist Winget standardmäßig nicht vorhanden. Das Script erkennt das und überspringt den Teil automatisch, statt beleidigt umzufallen.
[Winget] Enabled = true
Der Service-Watchdog ist für Fälle gedacht, in denen nach Updates kein Reboot nötig ist, aber bestimmte Dienste neu gestartet werden sollen. Zum Beispiel ein Webserver, ein Spooler oder ein anderer Dienst, der nach Updates gerne einen kleinen Schubs braucht.
[ServiceWatchdog] Enabled = true Services = Spooler,W3SVC WaitSeconds = 30 RetryCount = 3
Wichtig: Verwende hier den Dienstnamen, nicht den Anzeigenamen. Also zum Beispiel Spooler statt „Druckwarteschlange“. Windows ist da manchmal pingelig. Man kennt sich.
🛡️ Defender-Updates separat ausführen
Ein Punkt, der mir im Alltag wichtig geworden ist: Defender-Signaturen dürfen gerne häufiger laufen als volle Windows Updates. Dafür gibt es den Defender-only-Modus. Das Script filtert dann auf die Kategorie Definition Updates und installiert nur diese Updates.
In diesem Modus werden Winget, Reboot-Handling und Service-Watchdog bewusst übersprungen. Das ist auch logisch, denn Defender-Signaturen brauchen normalerweise keinen Neustart und sollen möglichst leise durchlaufen.
powershell.exe -ExecutionPolicy Bypass -NoProfile -File "C:\scripts\windows-updater\windows-updater.ps1" -DefenderOnly
Alternativ kannst du den Modus dauerhaft in der Config setzen:
[General] UpdateType = DefenderOnly
Ich würde meistens den Parameter bevorzugen. Dann bleibt die Config auf volle Updates eingestellt und du steuerst über die Aufgabenplanung, wann nur Defender und wann alles laufen soll.
🕒 Windows Updates automatisieren
Richtig nützlich wird das Script, wenn es automatisch läuft. Dafür reicht die Windows Aufgabenplanung völlig aus. Ich lasse solche Jobs gerne als SYSTEM ausführen, mit höchsten Privilegien und einem festen Zeitfenster.
Tägliche Aufgabe erstellen
Dieses Beispiel erstellt eine Aufgabe, die täglich um 04:00 Uhr läuft:
$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 `
-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"
Für Defender-Signaturen kannst du zusätzlich eine zweite Aufgabe anlegen, die alle vier Stunden läuft. Die Ausführungszeit ist kürzer, weil hier nicht die komplette Update-Runde inklusive Reboot-Logik gefahren wird.
$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 `
-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"
Ein typisches Setup wäre dann: Defender alle paar Stunden, volle Windows Updates monatlich oder in deinem üblichen Wartungsfenster. So bleiben Signaturen frisch, ohne dass du ständig volle Update-Läufe auf deinen Servern anstößt.
Nach dem ersten Lauf solltest du das Log prüfen und dir in der Aufgabenplanung den letzten Rückgabewert ansehen.
$today = Get-Date -Format "yyyy-MM-dd"
Get-Content "C:\logs\windows-updater\windows-updater_$today.log"
Get-ScheduledTaskInfo -TaskName "Windows Updater" |
Select-Object LastRunTime, LastTaskResult
Die wichtigsten Exit-Codes sind schnell erklärt: 0 bedeutet Erfolg, 1 bedeutet Fehler und 2 heißt, dass ein Neustart erforderlich wäre, Auto-Reboot aber deaktiviert ist.
🧪 Dry-Run und sichere Einführung
Ich würde das Script nicht direkt am ersten Tag auf allen Produktivservern scharf schalten. Besser ist ein kleiner Ablauf, der langweilig klingt, aber Nerven spart:
- Erst auf einem Testsystem mit
DryRun = trueausführen. - Logs und Benachrichtigungen prüfen.
- Danach auf einem unkritischen System echte Updates installieren.
- Reboot-Verhalten bewusst testen.
- Erst danach auf weitere Systeme ausrollen.
Bei kritischen Servern nutze ich gerne einen monatlichen Dry-Run und entscheide anschließend bewusst, wann installiert wird. Defender-Signaturen dürfen dagegen deutlich häufiger laufen, zum Beispiel alle vier Stunden mit -DefenderOnly. Bei weniger kritischen Systemen kann man das Script auch direkt mit geplantem Reboot laufen lassen. Es kommt wie immer darauf an, was auf der Maschine passiert und wer sich beschwert, wenn sie morgens um 03:00 Uhr neu startet.
🧯 Typische Stolperfallen
Ein paar Dinge solltest du im Hinterkopf behalten:
- Das Script muss als Administrator laufen.
- Wenn du es als
SYSTEMausführst, sind Netzlaufwerke nicht verfügbar. - Der Log-Pfad muss für den ausführenden Benutzer beschreibbar sein.
- Winget ist auf Windows Servern standardmäßig nicht vorhanden.
- Ein WSUS kann beeinflussen, welche Updates überhaupt angeboten werden.
-DefenderOnlyüberspringt Winget, Reboot-Handling und Service-Watchdog bewusst.-DryRunüberschreibt die Config und installiert nichts, auch wenn dortDryRun = falsesteht.- Bei
-ConfigFilemuss der angegebene Pfad aus Sicht des ausführenden Benutzers erreichbar sein. ForceRebootsolltest du nur in einem klaren Wartungsfenster nutzen.
Wenn du eine Fehlermeldung bekommst, prüfe zuerst das Tageslog. Danach sind Klassiker wie Windows Update Dienst, Netzwerk, Proxy, PSGallery-Zugriff und WSUS-Freigaben dran.

🧩 Fazit
Das Windows Updater Script ist kein Ersatz für ein großes Patch-Management in riesigen Umgebungen. Das soll es auch gar nicht sein. Es ist ein pragmatisches Werkzeug für Admins, Homelabs, kleine Umgebungen und Server, bei denen man Windows Updates sauber automatisieren möchte, ohne gleich eine komplette Plattform auszurollen.
Mir war wichtig, dass der Ablauf transparent bleibt: Konfiguration in einer INI-Datei, Logs auf dem System, klare Exit-Codes, Benachrichtigungen und genug Stellschrauben für Reboot, Ausschlüsse, eigene Hooks und verschiedene Ausführungsarten. Gerade die Parameter -DefenderOnly, -DryRun und -ConfigFile machen das Script flexibler, ohne dass du für jeden Zweck eine eigene Kopie pflegen musst.
Und ganz ehrlich: Wenn ein Script Updates installiert, Logs schreibt, dich informiert und danach nicht heimlich um 11:17 Uhr mitten im Meeting neustartet, dann hat es schon einen sehr guten Tag.
👥 Techniverse Community
Lust auf Austausch rund um Matrix, Selfhosting und andere smarte IT-Lösungen?
In der Techniverse Community triffst du Gleichgesinnte, kannst Fragen stellen oder einfach nerdigen Talk genießen. 🚀
👉 Jetzt der Gruppe auf Matrix beitreten
~ Direkte Raumadresse: #community:techniverse.net
👉 Für lockere Gespräche abseits der Kernthemen komm in den Talkraum
~ Direkte Raumadresse: #talk:techniverse.net
Wir freuen uns, wenn du dabei bist!


