Um DefenderUpdatesOnly ergänzt
This commit is contained in:
32
README.md
32
README.md
@@ -27,6 +27,8 @@
|
|||||||
## Features
|
## Features
|
||||||
|
|
||||||
- **Windows Updates** automatisch installieren via PSWindowsUpdate-Modul
|
- **Windows Updates** automatisch installieren via PSWindowsUpdate-Modul
|
||||||
|
- **Defender-Only Modus** nur Defender-Definitionsupdates installieren (per Parameter oder Config) — ideal für häufige Ausführung alle paar Stunden
|
||||||
|
- **Kommandozeilen-Parameter** `-DefenderOnly`, `-DryRun`, `-ConfigFile` für flexible Steuerung ohne Config-Änderung
|
||||||
- **Winget-Updates** optional (`winget upgrade --all`), automatische Erkennung der Verfügbarkeit
|
- **Winget-Updates** optional (`winget upgrade --all`), automatische Erkennung der Verfügbarkeit
|
||||||
- **Benachrichtigungen** via ntfy, E-Mail und/oder Microsoft Teams (inkl. HTML-Report und Adaptive Cards)
|
- **Benachrichtigungen** via ntfy, E-Mail und/oder Microsoft Teams (inkl. HTML-Report und Adaptive Cards)
|
||||||
- **Benachrichtigungs-Ereignisse** granular steuerbar (je Ereignis einzeln ein-/ausschaltbar)
|
- **Benachrichtigungs-Ereignisse** granular steuerbar (je Ereignis einzeln ein-/ausschaltbar)
|
||||||
@@ -73,11 +75,41 @@ Danach `config.ini` mit einem Texteditor öffnen und die gewünschten Werte anpa
|
|||||||
### 3. Script ausführen (als Administrator)
|
### 3. Script ausführen (als Administrator)
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
|
# Alle Updates installieren (Standard)
|
||||||
powershell.exe -ExecutionPolicy Bypass -File "C:\scripts\windows-updater\windows-updater.ps1"
|
powershell.exe -ExecutionPolicy Bypass -File "C:\scripts\windows-updater\windows-updater.ps1"
|
||||||
|
|
||||||
|
# Nur Defender-Definitionsupdates
|
||||||
|
powershell.exe -ExecutionPolicy Bypass -File "C:\scripts\windows-updater\windows-updater.ps1" -DefenderOnly
|
||||||
|
|
||||||
|
# Dry-Run (nur prüfen, nicht installieren)
|
||||||
|
powershell.exe -ExecutionPolicy Bypass -File "C:\scripts\windows-updater\windows-updater.ps1" -DryRun
|
||||||
|
|
||||||
|
# Alternative Config-Datei verwenden
|
||||||
|
powershell.exe -ExecutionPolicy Bypass -File "C:\scripts\windows-updater\windows-updater.ps1" -ConfigFile "D:\configs\server-config.ini"
|
||||||
```
|
```
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
## Parameter
|
||||||
|
|
||||||
|
| Parameter | Typ | Beschreibung |
|
||||||
|
|---|---|---|
|
||||||
|
| `-DefenderOnly` | Switch | Nur Windows Defender Definitionsupdates installieren. Überspringt Winget, Reboot und Service Watchdog |
|
||||||
|
| `-DryRun` | Switch | Nur prüfen welche Updates verfügbar sind, ohne zu installieren. Überschreibt die Config-Einstellung |
|
||||||
|
| `-ConfigFile` | String | Pfad zu einer alternativen Konfigurationsdatei (Standard: `config.ini` im Script-Verzeichnis) |
|
||||||
|
|
||||||
|
Parameter überschreiben die entsprechenden Config-Werte. So kann dasselbe Script mit verschiedenen Parametern für verschiedene Aufgaben verwendet werden.
|
||||||
|
|
||||||
|
### Typisches Setup: Defender häufig, volle Updates monatlich
|
||||||
|
|
||||||
|
```
|
||||||
|
# Alle 4 Stunden: Defender-Updates (per Aufgabenplanung)
|
||||||
|
powershell.exe -ExecutionPolicy Bypass -File "C:\scripts\windows-updater\windows-updater.ps1" -DefenderOnly
|
||||||
|
|
||||||
|
# Einmal im Monat: Alle Updates (per Aufgabenplanung)
|
||||||
|
powershell.exe -ExecutionPolicy Bypass -File "C:\scripts\windows-updater\windows-updater.ps1"
|
||||||
|
```
|
||||||
|
|
||||||
### 4. Optional: Aufgabenplanung einrichten
|
### 4. Optional: Aufgabenplanung einrichten
|
||||||
|
|
||||||
Für automatische Ausführung per Windows Aufgabenplanung siehe [Aufgabenplanung einrichten](docs/task-scheduler.md).
|
Für automatische Ausführung per Windows Aufgabenplanung siehe [Aufgabenplanung einrichten](docs/task-scheduler.md).
|
||||||
|
|||||||
@@ -11,6 +11,9 @@ LogPath = C:\logs\windows-updater
|
|||||||
LogRetentionDays = 365
|
LogRetentionDays = 365
|
||||||
# Dry-run mode: scan and report only, do not install (true/false)
|
# Dry-run mode: scan and report only, do not install (true/false)
|
||||||
DryRun = false
|
DryRun = false
|
||||||
|
# Update type: All (all updates), DefenderOnly (only Defender definition updates)
|
||||||
|
# Can be overridden with -DefenderOnly parameter
|
||||||
|
UpdateType = All
|
||||||
|
|
||||||
[WindowsUpdate]
|
[WindowsUpdate]
|
||||||
# Use Microsoft Update (includes Office, .NET, drivers) instead of Windows Update only
|
# Use Microsoft Update (includes Office, .NET, drivers) instead of Windows Update only
|
||||||
|
|||||||
@@ -10,7 +10,8 @@ Alle Einstellungen werden in der Datei `config.ini` vorgenommen, die sich im sel
|
|||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| `LogPath` | String | `C:\logs\windows-updater` | Verzeichnis für Log-Dateien (eine Datei pro Tag) |
|
| `LogPath` | String | `C:\logs\windows-updater` | Verzeichnis für Log-Dateien (eine Datei pro Tag) |
|
||||||
| `LogRetentionDays` | Integer | `365` | Anzahl Tage, nach denen alte Log-Dateien gelöscht werden |
|
| `LogRetentionDays` | Integer | `365` | Anzahl Tage, nach denen alte Log-Dateien gelöscht werden |
|
||||||
| `DryRun` | Boolean | `false` | Nur prüfen welche Updates verfügbar sind, ohne zu installieren |
|
| `DryRun` | Boolean | `false` | Nur prüfen welche Updates verfügbar sind, ohne zu installieren. Kann per `-DryRun` Parameter überschrieben werden |
|
||||||
|
| `UpdateType` | String | `All` | Art der Updates: `All` (alle Updates), `DefenderOnly` (nur Defender-Definitionsupdates). Kann per `-DefenderOnly` Parameter überschrieben werden |
|
||||||
|
|
||||||
### [WindowsUpdate]
|
### [WindowsUpdate]
|
||||||
|
|
||||||
@@ -109,6 +110,27 @@ Steuert, welche Ereignisse eine Benachrichtigung auslösen. Alle Ereignisse sind
|
|||||||
| `Enabled` | Boolean | `false` | Update-History-Tracking aktivieren |
|
| `Enabled` | Boolean | `false` | Update-History-Tracking aktivieren |
|
||||||
| `HistoryPath` | String | `C:\logs\windows-updater\update-history` | Basispfad für die History-Datei (Dateiendung wird automatisch gewählt: `.xlsx` wenn ImportExcel verfügbar, sonst `.csv`) |
|
| `HistoryPath` | String | `C:\logs\windows-updater\update-history` | Basispfad für die History-Datei (Dateiendung wird automatisch gewählt: `.xlsx` wenn ImportExcel verfügbar, sonst `.csv`) |
|
||||||
|
|
||||||
|
## Kommandozeilen-Parameter
|
||||||
|
|
||||||
|
Das Script unterstützt Parameter, die Config-Werte überschreiben. So kann dasselbe Script mit einer Config-Datei für verschiedene Einsatzzwecke verwendet werden.
|
||||||
|
|
||||||
|
| Parameter | Überschreibt | Beschreibung |
|
||||||
|
|---|---|---|
|
||||||
|
| `-DefenderOnly` | `UpdateType` | Nur Windows Defender Definitionsupdates installieren |
|
||||||
|
| `-DryRun` | `DryRun` | Nur prüfen, nicht installieren |
|
||||||
|
| `-ConfigFile <Pfad>` | *(Config-Pfad)* | Alternative Konfigurationsdatei verwenden |
|
||||||
|
|
||||||
|
### DefenderOnly-Modus im Detail
|
||||||
|
|
||||||
|
Im DefenderOnly-Modus werden ausschließlich Updates der Kategorie "Definition Updates" (Windows Defender Signaturen) installiert. Folgende Funktionen werden dabei übersprungen:
|
||||||
|
|
||||||
|
- **Winget-Updates** — nicht relevant für Defender-Signaturen
|
||||||
|
- **Reboot-Handling** — Definitionsupdates erfordern keinen Neustart
|
||||||
|
- **Service Watchdog** — kein Neustart von Diensten nötig
|
||||||
|
- **Treiber-Ausschluss** (`ExcludeDrivers`) — nicht relevant, da nur Definitionsupdates geladen werden
|
||||||
|
|
||||||
|
Pre-/Post-Update Hooks, Logging, History und Benachrichtigungen funktionieren normal.
|
||||||
|
|
||||||
## Boolean-Werte
|
## Boolean-Werte
|
||||||
|
|
||||||
Folgende Werte werden als `true` erkannt (Groß-/Kleinschreibung egal):
|
Folgende Werte werden als `true` erkannt (Groß-/Kleinschreibung egal):
|
||||||
@@ -129,6 +151,44 @@ DryRun = false
|
|||||||
UseMicrosoftUpdate = true
|
UseMicrosoftUpdate = true
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Nur Defender-Updates (per Config)
|
||||||
|
|
||||||
|
```ini
|
||||||
|
[General]
|
||||||
|
LogPath = C:\logs\windows-updater
|
||||||
|
DryRun = false
|
||||||
|
UpdateType = DefenderOnly
|
||||||
|
|
||||||
|
[WindowsUpdate]
|
||||||
|
UseMicrosoftUpdate = true
|
||||||
|
```
|
||||||
|
|
||||||
|
### Dual-Setup: Defender häufig + volle Updates monatlich
|
||||||
|
|
||||||
|
Eine Config-Datei genügt. Die Unterscheidung erfolgt per Parameter:
|
||||||
|
|
||||||
|
```ini
|
||||||
|
[General]
|
||||||
|
LogPath = C:\logs\windows-updater
|
||||||
|
DryRun = false
|
||||||
|
# UpdateType = All (Standard, volle Updates)
|
||||||
|
|
||||||
|
[WindowsUpdate]
|
||||||
|
UseMicrosoftUpdate = true
|
||||||
|
ExcludeDrivers = true
|
||||||
|
RetryCount = 3
|
||||||
|
```
|
||||||
|
|
||||||
|
Aufgabenplanung:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# Alle 4 Stunden: nur Defender-Updates
|
||||||
|
powershell.exe -ExecutionPolicy Bypass -File "C:\scripts\windows-updater\windows-updater.ps1" -DefenderOnly
|
||||||
|
|
||||||
|
# Einmal im Monat (z.B. erster Sonntag, 03:00): alle Updates
|
||||||
|
powershell.exe -ExecutionPolicy Bypass -File "C:\scripts\windows-updater\windows-updater.ps1"
|
||||||
|
```
|
||||||
|
|
||||||
### Mit E-Mail Benachrichtigung
|
### Mit E-Mail Benachrichtigung
|
||||||
|
|
||||||
```ini
|
```ini
|
||||||
|
|||||||
@@ -70,6 +70,77 @@ $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.
|
Den Rest wie beim täglichen Beispiel, nur den Trigger austauschen.
|
||||||
|
|
||||||
|
### Defender-Updates alle 4 Stunden
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
$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):
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# 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
|
### Bestehende Aufgabe ändern
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
@@ -168,3 +239,4 @@ Get-ScheduledTaskInfo -TaskName "Windows Updater" | Select-Object LastRunTime, L
|
|||||||
| Server (nicht-kritisch) | Täglich, 03:00-05:00 mit Auto-Reboot |
|
| 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 |
|
| Server (kritisch/Produktion) | Wöchentlich Sonntag, 03:00 mit DryRun. Manuell nach Prüfung |
|
||||||
| Testumgebung | Täglich, 02:00 mit Auto-Reboot |
|
| Testumgebung | Täglich, 02:00 mit Auto-Reboot |
|
||||||
|
| Defender-Signaturen | Alle 4 Stunden mit `-DefenderOnly`, zusätzlich zu einem der obigen Szenarien |
|
||||||
|
|||||||
@@ -9,12 +9,18 @@ Author: Patrick Asmus
|
|||||||
Web: https://www.cleveradmin.de
|
Web: https://www.cleveradmin.de
|
||||||
Repository: https://git.techniverse.net/scriptos/windows-updater.git
|
Repository: https://git.techniverse.net/scriptos/windows-updater.git
|
||||||
License: MIT
|
License: MIT
|
||||||
Version: 2.7.2
|
Version: 2.8.0
|
||||||
Date: 06.07.2026
|
Date: 07.07.2026
|
||||||
Modifications: Guard TLS 1.3 with enum check and try/catch fallback to TLS 1.2
|
Modifications: Add -DefenderOnly, -DryRun, -ConfigFile parameters and UpdateType config option
|
||||||
#####################################################
|
#####################################################
|
||||||
#>
|
#>
|
||||||
|
|
||||||
|
param(
|
||||||
|
[switch]$DefenderOnly,
|
||||||
|
[switch]$DryRun,
|
||||||
|
[string]$ConfigFile
|
||||||
|
)
|
||||||
|
|
||||||
# ===================================================
|
# ===================================================
|
||||||
#region Administrator Check
|
#region Administrator Check
|
||||||
# ===================================================
|
# ===================================================
|
||||||
@@ -53,6 +59,9 @@ $script:UseXlsx = $false
|
|||||||
$script:UpdateResults = [System.Collections.ArrayList]::new()
|
$script:UpdateResults = [System.Collections.ArrayList]::new()
|
||||||
$script:WingetResults = [System.Collections.ArrayList]::new()
|
$script:WingetResults = [System.Collections.ArrayList]::new()
|
||||||
$script:ScriptStartTime = Get-Date
|
$script:ScriptStartTime = Get-Date
|
||||||
|
$script:DefenderOnlyMode = $DefenderOnly.IsPresent
|
||||||
|
$script:DryRunParam = $DryRun.IsPresent
|
||||||
|
$script:ConfigFilePath = $ConfigFile
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
@@ -765,6 +774,11 @@ function Get-AvailableUpdates {
|
|||||||
$params["MicrosoftUpdate"] = $true
|
$params["MicrosoftUpdate"] = $true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($script:DefenderOnlyMode) {
|
||||||
|
$params["Category"] = @("Definition Updates")
|
||||||
|
Write-Log "Filtering for Defender definition updates only"
|
||||||
|
}
|
||||||
|
|
||||||
if (-not [string]::IsNullOrWhiteSpace($excludeKBs)) {
|
if (-not [string]::IsNullOrWhiteSpace($excludeKBs)) {
|
||||||
$kbList = $excludeKBs -split ',' | ForEach-Object { $_.Trim() } | Where-Object { -not [string]::IsNullOrWhiteSpace($_) }
|
$kbList = $excludeKBs -split ',' | ForEach-Object { $_.Trim() } | Where-Object { -not [string]::IsNullOrWhiteSpace($_) }
|
||||||
if ($kbList.Count -gt 0) {
|
if ($kbList.Count -gt 0) {
|
||||||
@@ -773,7 +787,7 @@ function Get-AvailableUpdates {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($excludeDrivers) {
|
if ($excludeDrivers -and -not $script:DefenderOnlyMode) {
|
||||||
$params["NotCategory"] = "Drivers"
|
$params["NotCategory"] = "Drivers"
|
||||||
Write-Log "Excluding driver updates"
|
Write-Log "Excluding driver updates"
|
||||||
}
|
}
|
||||||
@@ -804,6 +818,10 @@ function Install-PendingUpdates {
|
|||||||
$params["MicrosoftUpdate"] = $true
|
$params["MicrosoftUpdate"] = $true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($script:DefenderOnlyMode) {
|
||||||
|
$params["Category"] = @("Definition Updates")
|
||||||
|
}
|
||||||
|
|
||||||
if (-not [string]::IsNullOrWhiteSpace($excludeKBs)) {
|
if (-not [string]::IsNullOrWhiteSpace($excludeKBs)) {
|
||||||
$kbList = $excludeKBs -split ',' | ForEach-Object { $_.Trim() } | Where-Object { -not [string]::IsNullOrWhiteSpace($_) }
|
$kbList = $excludeKBs -split ',' | ForEach-Object { $_.Trim() } | Where-Object { -not [string]::IsNullOrWhiteSpace($_) }
|
||||||
if ($kbList.Count -gt 0) {
|
if ($kbList.Count -gt 0) {
|
||||||
@@ -811,7 +829,7 @@ function Install-PendingUpdates {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($excludeDrivers) {
|
if ($excludeDrivers -and -not $script:DefenderOnlyMode) {
|
||||||
$params["NotCategory"] = "Drivers"
|
$params["NotCategory"] = "Drivers"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1158,16 +1176,30 @@ function Invoke-ServiceWatchdog {
|
|||||||
|
|
||||||
function Start-WindowsUpdater {
|
function Start-WindowsUpdater {
|
||||||
# --- Load Config ---
|
# --- Load Config ---
|
||||||
$configPath = Join-Path -Path $PSScriptRoot -ChildPath "config.ini"
|
if (-not [string]::IsNullOrWhiteSpace($script:ConfigFilePath)) {
|
||||||
|
$configPath = $script:ConfigFilePath
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$configPath = Join-Path -Path $PSScriptRoot -ChildPath "config.ini"
|
||||||
|
}
|
||||||
$script:Config = Read-IniFile -Path $configPath
|
$script:Config = Read-IniFile -Path $configPath
|
||||||
|
|
||||||
|
# --- Resolve DefenderOnly mode (parameter overrides config) ---
|
||||||
|
if (-not $script:DefenderOnlyMode) {
|
||||||
|
$configUpdateType = Get-ConfigValue -Config $script:Config -Section "General" -Key "UpdateType" -Default "All"
|
||||||
|
if ($configUpdateType -eq "DefenderOnly") {
|
||||||
|
$script:DefenderOnlyMode = $true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
# --- Initialize Logging ---
|
# --- Initialize Logging ---
|
||||||
$logPath = Get-ConfigValue -Config $script:Config -Section "General" -Key "LogPath" -Default "C:\logs\windows-updater"
|
$logPath = Get-ConfigValue -Config $script:Config -Section "General" -Key "LogPath" -Default "C:\logs\windows-updater"
|
||||||
$logRetention = Get-ConfigValue -Config $script:Config -Section "General" -Key "LogRetentionDays" -Default 365 -Type int
|
$logRetention = Get-ConfigValue -Config $script:Config -Section "General" -Key "LogRetentionDays" -Default 365 -Type int
|
||||||
Initialize-Logging -LogPath $logPath -RetentionDays $logRetention
|
Initialize-Logging -LogPath $logPath -RetentionDays $logRetention
|
||||||
|
|
||||||
|
$modeLabel = if ($script:DefenderOnlyMode) { "Defender Only" } else { "Full Update" }
|
||||||
Write-Log "=========================================="
|
Write-Log "=========================================="
|
||||||
Write-Log "Windows Updater started"
|
Write-Log "Windows Updater started (Mode: $modeLabel)"
|
||||||
Write-Log "=========================================="
|
Write-Log "=========================================="
|
||||||
|
|
||||||
# --- OS Detection ---
|
# --- OS Detection ---
|
||||||
@@ -1208,6 +1240,7 @@ function Start-WindowsUpdater {
|
|||||||
|
|
||||||
# --- Scan for Updates ---
|
# --- Scan for Updates ---
|
||||||
$dryRun = Get-ConfigValue -Config $script:Config -Section "General" -Key "DryRun" -Default $false -Type bool
|
$dryRun = Get-ConfigValue -Config $script:Config -Section "General" -Key "DryRun" -Default $false -Type bool
|
||||||
|
if ($script:DryRunParam) { $dryRun = $true }
|
||||||
$availableUpdates = Get-AvailableUpdates
|
$availableUpdates = Get-AvailableUpdates
|
||||||
|
|
||||||
if ($null -eq $availableUpdates) {
|
if ($null -eq $availableUpdates) {
|
||||||
@@ -1220,13 +1253,15 @@ function Start-WindowsUpdater {
|
|||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$updateLabel = if ($script:DefenderOnlyMode) { "Defender" } else { "Windows" }
|
||||||
|
|
||||||
if (@($availableUpdates).Count -eq 0) {
|
if (@($availableUpdates).Count -eq 0) {
|
||||||
Write-Log "No updates available"
|
Write-Log "No updates available"
|
||||||
Send-Notification -Subject "No Updates - $env:COMPUTERNAME" `
|
Send-Notification -Subject "No $updateLabel Updates - $env:COMPUTERNAME" `
|
||||||
-Body "No Windows updates are currently available for $env:COMPUTERNAME." `
|
-Body "No $updateLabel updates are currently available for $env:COMPUTERNAME." `
|
||||||
-EventName "NoUpdates"
|
-EventName "NoUpdates"
|
||||||
if (-not $dryRun) {
|
if (-not $dryRun) {
|
||||||
Invoke-WingetUpgrade
|
if (-not $script:DefenderOnlyMode) { Invoke-WingetUpgrade }
|
||||||
Export-UpdateHistory
|
Export-UpdateHistory
|
||||||
}
|
}
|
||||||
Invoke-UpdateHook -Phase Post | Out-Null
|
Invoke-UpdateHook -Phase Post | Out-Null
|
||||||
@@ -1250,7 +1285,7 @@ function Start-WindowsUpdater {
|
|||||||
if ($dryRun) {
|
if ($dryRun) {
|
||||||
Write-Log "DRY-RUN mode active - no updates will be installed"
|
Write-Log "DRY-RUN mode active - no updates will be installed"
|
||||||
|
|
||||||
$reportLines = @("Available Updates for $env:COMPUTERNAME ($updateCount):`n")
|
$reportLines = @("Available $updateLabel Updates for $env:COMPUTERNAME ($updateCount):`n")
|
||||||
foreach ($u in $availableUpdates) {
|
foreach ($u in $availableUpdates) {
|
||||||
$sizeStr = if ($u.Size) { "{0:N2} MB" -f ($u.Size / 1MB) } else { "N/A" }
|
$sizeStr = if ($u.Size) { "{0:N2} MB" -f ($u.Size / 1MB) } else { "N/A" }
|
||||||
$kbStr = if ($u.KB) { $u.KB } else { "N/A" }
|
$kbStr = if ($u.KB) { $u.KB } else { "N/A" }
|
||||||
@@ -1258,7 +1293,7 @@ function Start-WindowsUpdater {
|
|||||||
}
|
}
|
||||||
$reportBody = $reportLines -join "`n"
|
$reportBody = $reportLines -join "`n"
|
||||||
|
|
||||||
Send-Notification -Subject "Dry-Run Report - $env:COMPUTERNAME" -Body $reportBody -EventName "DryRun"
|
Send-Notification -Subject "$updateLabel Dry-Run Report - $env:COMPUTERNAME" -Body $reportBody -EventName "DryRun"
|
||||||
|
|
||||||
Invoke-UpdateHook -Phase Post | Out-Null
|
Invoke-UpdateHook -Phase Post | Out-Null
|
||||||
Write-Log "Windows Updater finished (dry-run)"
|
Write-Log "Windows Updater finished (dry-run)"
|
||||||
@@ -1268,8 +1303,10 @@ function Start-WindowsUpdater {
|
|||||||
# --- Install Updates ---
|
# --- Install Updates ---
|
||||||
Install-PendingUpdates
|
Install-PendingUpdates
|
||||||
|
|
||||||
# --- Winget ---
|
# --- Winget (skip in DefenderOnly mode) ---
|
||||||
Invoke-WingetUpgrade
|
if (-not $script:DefenderOnlyMode) {
|
||||||
|
Invoke-WingetUpgrade
|
||||||
|
}
|
||||||
|
|
||||||
# --- Export History ---
|
# --- Export History ---
|
||||||
Export-UpdateHistory
|
Export-UpdateHistory
|
||||||
@@ -1279,7 +1316,7 @@ function Start-WindowsUpdater {
|
|||||||
$failCount = ($script:UpdateResults | Where-Object { $_.Result -notmatch "^Installed|^Succeeded|^Success" } | Measure-Object).Count
|
$failCount = ($script:UpdateResults | Where-Object { $_.Result -notmatch "^Installed|^Succeeded|^Success" } | Measure-Object).Count
|
||||||
|
|
||||||
$summaryBody = @(
|
$summaryBody = @(
|
||||||
"Windows Update Summary for $env:COMPUTERNAME"
|
"$updateLabel Update Summary for $env:COMPUTERNAME"
|
||||||
"OS: $($script:OSInfo.Caption)"
|
"OS: $($script:OSInfo.Caption)"
|
||||||
"Date: $(Get-Date -Format 'dd.MM.yyyy HH:mm:ss')"
|
"Date: $(Get-Date -Format 'dd.MM.yyyy HH:mm:ss')"
|
||||||
""
|
""
|
||||||
@@ -1295,7 +1332,7 @@ function Start-WindowsUpdater {
|
|||||||
$summaryText = $summaryBody -join "`n"
|
$summaryText = $summaryBody -join "`n"
|
||||||
$hasFailures = $failCount -gt 0 -or $script:HasErrors
|
$hasFailures = $failCount -gt 0 -or $script:HasErrors
|
||||||
$priority = if ($hasFailures) { "high" } else { "default" }
|
$priority = if ($hasFailures) { "high" } else { "default" }
|
||||||
$subjectPrefix = if ($hasFailures) { "Updates (with errors)" } else { "Updates OK" }
|
$subjectPrefix = if ($hasFailures) { "$updateLabel Updates (with errors)" } else { "$updateLabel Updates OK" }
|
||||||
|
|
||||||
Send-Notification -Subject "$subjectPrefix - $env:COMPUTERNAME" `
|
Send-Notification -Subject "$subjectPrefix - $env:COMPUTERNAME" `
|
||||||
-Body $summaryText -Priority $priority `
|
-Body $summaryText -Priority $priority `
|
||||||
@@ -1304,22 +1341,24 @@ function Start-WindowsUpdater {
|
|||||||
# --- Post-Update Hook ---
|
# --- Post-Update Hook ---
|
||||||
Invoke-UpdateHook -Phase Post | Out-Null
|
Invoke-UpdateHook -Phase Post | Out-Null
|
||||||
|
|
||||||
# --- Reboot Handling ---
|
# --- Reboot Handling (skip in DefenderOnly mode) ---
|
||||||
$script:RebootPending = Test-PendingReboot
|
if (-not $script:DefenderOnlyMode) {
|
||||||
if ($script:RebootPending) {
|
$script:RebootPending = Test-PendingReboot
|
||||||
Write-Log "System reboot is pending"
|
if ($script:RebootPending) {
|
||||||
$rebootHandled = Invoke-ScheduledReboot
|
Write-Log "System reboot is pending"
|
||||||
if (-not $rebootHandled) {
|
$rebootHandled = Invoke-ScheduledReboot
|
||||||
Write-Log "Auto-reboot is disabled, manual reboot required" -Level WARN
|
if (-not $rebootHandled) {
|
||||||
Send-Notification -Subject "Reboot Required - $env:COMPUTERNAME" `
|
Write-Log "Auto-reboot is disabled, manual reboot required" -Level WARN
|
||||||
-Body "Windows updates have been installed on $env:COMPUTERNAME but a reboot is required. Auto-reboot is disabled." `
|
Send-Notification -Subject "Reboot Required - $env:COMPUTERNAME" `
|
||||||
-Priority "high" -EventName "RebootRequired"
|
-Body "Windows updates have been installed on $env:COMPUTERNAME but a reboot is required. Auto-reboot is disabled." `
|
||||||
|
-Priority "high" -EventName "RebootRequired"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Write-Log "No reboot required"
|
||||||
|
# --- Service Watchdog (only when no reboot is pending) ---
|
||||||
|
Invoke-ServiceWatchdog
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else {
|
|
||||||
Write-Log "No reboot required"
|
|
||||||
# --- Service Watchdog (only when no reboot is pending) ---
|
|
||||||
Invoke-ServiceWatchdog
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# --- Finish ---
|
# --- Finish ---
|
||||||
@@ -1328,7 +1367,7 @@ function Start-WindowsUpdater {
|
|||||||
Write-Log "=========================================="
|
Write-Log "=========================================="
|
||||||
|
|
||||||
if ($script:HasErrors) { exit 1 }
|
if ($script:HasErrors) { exit 1 }
|
||||||
if ($script:RebootPending -and -not (Get-ConfigValue -Config $script:Config -Section "Reboot" -Key "Enabled" -Default $false -Type bool)) { exit 2 }
|
if (-not $script:DefenderOnlyMode -and $script:RebootPending -and -not (Get-ConfigValue -Config $script:Config -Section "Reboot" -Key "Enabled" -Default $false -Type bool)) { exit 2 }
|
||||||
exit 0
|
exit 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user