diff --git a/config.example.ini b/config.example.ini index 70a22f0..2063dd2 100644 --- a/config.example.ini +++ b/config.example.ini @@ -78,6 +78,8 @@ NotifyOnHookFailure = true NotifyOnNoUpdates = true # Dry-run mode report with list of available updates NotifyOnDryRun = true +# Update scan failed (e.g. network error, service unavailable) +NotifyOnScanError = true # Summary after update installation (success and errors) NotifyOnUpdateComplete = true # System reboot triggered (immediate, delayed, or scheduled) diff --git a/windows-updater.ps1 b/windows-updater.ps1 index d8b211d..da62148 100644 --- a/windows-updater.ps1 +++ b/windows-updater.ps1 @@ -9,9 +9,9 @@ Author: Patrick Asmus Web: https://www.cleveradmin.de Repository: https://git.techniverse.net/scriptos/windows-updater.git License: MIT -Version: 2.3.0 +Version: 2.4.0 Datum: 06.07.2026 -Modifiaktione: Reviews and improvements +Modifiaktione: Reviews and improvements, part 2 ##################################################### #> @@ -817,8 +817,8 @@ function Install-PendingUpdates { $resultArray = @($results) # PSWindowsUpdate returns multiple objects per update (one per phase: search, download, install). - # Group by KB and keep only the last (final) result per update. - $grouped = $resultArray | Group-Object -Property KB + # Group by KB (or Title as fallback for driver/feature updates without KB) and keep only the last (final) result per update. + $grouped = $resultArray | Group-Object -Property { if ($_.KB) { $_.KB } else { $_.Title } } $deduplicated = foreach ($group in $grouped) { $group.Group | Select-Object -Last 1 } @@ -853,7 +853,8 @@ function Install-PendingUpdates { Write-Log " Retry $i/$retryCount for $kbId - $($f.Title)" -Level WARN try { $retryResult = Install-WindowsUpdate -KBArticleID $kbId -AcceptAll -ErrorAction Stop - if ($retryResult -and $retryResult.Result -and $retryResult.Result.ToString() -match "^Installed|^Succeeded|^Success") { + $finalRetryResult = if ($retryResult) { @($retryResult) | Select-Object -Last 1 } else { $null } + if ($finalRetryResult -and $finalRetryResult.Result -and $finalRetryResult.Result.ToString() -match "^Installed|^Succeeded|^Success") { Write-Log " Retry successful for $kbId" -Level INFO $retrySuccess = $true @@ -1009,8 +1010,19 @@ function Invoke-ScheduledReboot { } "scheduled" { $scheduledTime = Get-ConfigValue -Config $script:Config -Section "Reboot" -Key "ScheduledTime" -Default "03:00" + if ($scheduledTime -notmatch '^\d{1,2}:\d{2}$') { + Write-Log "Invalid ScheduledTime format '$scheduledTime' (expected HH:mm), falling back to 03:00" -Level WARN + $scheduledTime = "03:00" + } + $parts = $scheduledTime.Split(':') + $hour = [int]$parts[0] + $minute = [int]$parts[1] + if ($hour -lt 0 -or $hour -gt 23 -or $minute -lt 0 -or $minute -gt 59) { + Write-Log "ScheduledTime '$scheduledTime' out of range, falling back to 03:00" -Level WARN + $hour = 3; $minute = 0 + } $now = Get-Date - $target = Get-Date -Hour ([int]($scheduledTime.Split(':')[0])) -Minute ([int]($scheduledTime.Split(':')[1])) -Second 0 + $target = Get-Date -Hour $hour -Minute $minute -Second 0 if ($target -le $now) { $target = $target.AddDays(1) @@ -1180,7 +1192,7 @@ function Start-WindowsUpdater { Write-Log "Update scan failed, aborting" -Level ERROR Send-Notification -Subject "Scan Error - $env:COMPUTERNAME" ` -Body "Windows update scan failed on $env:COMPUTERNAME. Check logs for details." ` - -Priority "high" -EventName "UpdateComplete" + -Priority "high" -EventName "ScanError" Invoke-UpdateHook -Phase Post | Out-Null Write-Log "Windows Updater finished (scan error)" exit 1 @@ -1196,6 +1208,10 @@ function Start-WindowsUpdater { Export-UpdateHistory } Invoke-UpdateHook -Phase Post | Out-Null + if ($script:HasErrors) { + Write-Log "Windows Updater finished (no updates, but errors occurred)" -Level WARN + exit 1 + } Write-Log "Windows Updater finished (no updates)" exit 0 }