Reviews and improvements

This commit is contained in:
Patrick Asmus
2026-07-06 09:46:33 +02:00
parent b6894df214
commit 89f38e13f9

View File

@@ -9,9 +9,9 @@ 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.2.0 Version: 2.3.0
Datum: 06.07.2026 Datum: 06.07.2026
Modifiaktione: Service-Watchdog: Dienste nach Updates ohne Reboot neu starten und überwachen. Modifiaktione: Reviews and improvements
##################################################### #####################################################
#> #>
@@ -355,21 +355,29 @@ function New-HtmlUpdateReport {
$rows = "" $rows = ""
foreach ($r in $Results) { foreach ($r in $Results) {
$color = if ($r.Result -match "Install|Success") { "#2d7d2d" } else { "#c0392b" } $color = if ($r.Result -match "^Installed|^Succeeded|^Success") { "#2d7d2d" } else { "#c0392b" }
$bgColor = if ($r.Result -match "Install|Success") { "#e8f5e9" } else { "#fde8e8" } $bgColor = if ($r.Result -match "^Installed|^Succeeded|^Success") { "#e8f5e9" } else { "#fde8e8" }
$eDate = [System.Net.WebUtility]::HtmlEncode($r.Date)
$eKB = [System.Net.WebUtility]::HtmlEncode($r.KB)
$eTitle = [System.Net.WebUtility]::HtmlEncode($r.Title)
$eSize = [System.Net.WebUtility]::HtmlEncode($r.Size)
$eResult = [System.Net.WebUtility]::HtmlEncode($r.Result)
$rows += @" $rows += @"
<tr style="background-color: $bgColor;"> <tr style="background-color: $bgColor;">
<td style="padding: 8px; border: 1px solid #ddd;">$($r.Date)</td> <td style="padding: 8px; border: 1px solid #ddd;">$eDate</td>
<td style="padding: 8px; border: 1px solid #ddd;">$($r.KB)</td> <td style="padding: 8px; border: 1px solid #ddd;">$eKB</td>
<td style="padding: 8px; border: 1px solid #ddd;">$($r.Title)</td> <td style="padding: 8px; border: 1px solid #ddd;">$eTitle</td>
<td style="padding: 8px; border: 1px solid #ddd;">$($r.Size)</td> <td style="padding: 8px; border: 1px solid #ddd;">$eSize</td>
<td style="padding: 8px; border: 1px solid #ddd; color: $color; font-weight: bold;">$($r.Result)</td> <td style="padding: 8px; border: 1px solid #ddd; color: $color; font-weight: bold;">$eResult</td>
</tr> </tr>
"@ "@
} }
$successCount = ($Results | Where-Object { $_.Result -match "Install|Success" } | Measure-Object).Count $successCount = ($Results | Where-Object { $_.Result -match "^Installed|^Succeeded|^Success" } | Measure-Object).Count
$failCount = ($Results | Where-Object { $_.Result -notmatch "Install|Success" } | Measure-Object).Count $failCount = ($Results | Where-Object { $_.Result -notmatch "^Installed|^Succeeded|^Success" } | Measure-Object).Count
$eHostname = [System.Net.WebUtility]::HtmlEncode($Hostname)
$eOSCaption = [System.Net.WebUtility]::HtmlEncode($OSCaption)
$html = @" $html = @"
<!DOCTYPE html> <!DOCTYPE html>
@@ -377,8 +385,8 @@ function New-HtmlUpdateReport {
<head><meta charset="utf-8"></head> <head><meta charset="utf-8"></head>
<body style="font-family: Segoe UI, Arial, sans-serif; color: #333; max-width: 900px; margin: 0 auto; padding: 20px;"> <body style="font-family: Segoe UI, Arial, sans-serif; color: #333; max-width: 900px; margin: 0 auto; padding: 20px;">
<h2 style="color: #1a5276;">Windows Update Report</h2> <h2 style="color: #1a5276;">Windows Update Report</h2>
<p><strong>Host:</strong> $Hostname<br> <p><strong>Host:</strong> $eHostname<br>
<strong>OS:</strong> $OSCaption<br> <strong>OS:</strong> $eOSCaption<br>
<strong>Date:</strong> $(Get-Date -Format "dd.MM.yyyy HH:mm:ss")<br> <strong>Date:</strong> $(Get-Date -Format "dd.MM.yyyy HH:mm:ss")<br>
<strong>Successful:</strong> $successCount | <strong>Failed:</strong> $failCount</p> <strong>Successful:</strong> $successCount | <strong>Failed:</strong> $failCount</p>
<table style="border-collapse: collapse; width: 100%; font-size: 14px;"> <table style="border-collapse: collapse; width: 100%; font-size: 14px;">
@@ -510,7 +518,7 @@ function Send-TeamsNotification {
} }
foreach ($r in $UpdateResults) { foreach ($r in $UpdateResults) {
$icon = if ($r.Result -match "Install|Success") { "" } else { "" } $icon = if ($r.Result -match "^Installed|^Succeeded|^Success") { "" } else { "" }
$bodyItems += @{ $bodyItems += @{
type = "TextBlock" type = "TextBlock"
text = "$icon **$($r.KB)** — $($r.Title) ($($r.Size)) — $($r.Result)" text = "$icon **$($r.KB)** — $($r.Title) ($($r.Size)) — $($r.Result)"
@@ -761,7 +769,8 @@ function Get-AvailableUpdates {
} }
catch { catch {
Write-Log "Error scanning for updates: $($_.Exception.Message)" -Level ERROR Write-Log "Error scanning for updates: $($_.Exception.Message)" -Level ERROR
return @() $script:HasErrors = $true
return $null
} }
} }
@@ -833,7 +842,7 @@ function Install-PendingUpdates {
Add-UpdateHistoryEntry -KB $kbStr -Title $r.Title -Size $sizeStr -Result $resultStr Add-UpdateHistoryEntry -KB $kbStr -Title $r.Title -Size $sizeStr -Result $resultStr
} }
$failed = $deduplicated | Where-Object { $_.Result -and $_.Result.ToString() -notmatch "Install|Success" } $failed = $deduplicated | Where-Object { $_.Result -and $_.Result.ToString() -notmatch "^Installed|^Succeeded|^Success" }
if ($failed -and $retryCount -gt 0) { if ($failed -and $retryCount -gt 0) {
Write-Log "Retrying $(@($failed).Count) failed update(s)..." -Level WARN Write-Log "Retrying $(@($failed).Count) failed update(s)..." -Level WARN
foreach ($f in $failed) { foreach ($f in $failed) {
@@ -844,7 +853,7 @@ function Install-PendingUpdates {
Write-Log " Retry $i/$retryCount for $kbId - $($f.Title)" -Level WARN Write-Log " Retry $i/$retryCount for $kbId - $($f.Title)" -Level WARN
try { try {
$retryResult = Install-WindowsUpdate -KBArticleID $kbId -AcceptAll -ErrorAction Stop $retryResult = Install-WindowsUpdate -KBArticleID $kbId -AcceptAll -ErrorAction Stop
if ($retryResult -and $retryResult.Result -and $retryResult.Result.ToString() -match "Install|Success") { if ($retryResult -and $retryResult.Result -and $retryResult.Result.ToString() -match "^Installed|^Succeeded|^Success") {
Write-Log " Retry successful for $kbId" -Level INFO Write-Log " Retry successful for $kbId" -Level INFO
$retrySuccess = $true $retrySuccess = $true
@@ -925,12 +934,14 @@ function Invoke-WingetUpgrade {
} }
else { else {
Write-Log "Winget upgrade finished with exit code: $exitCode" -Level WARN Write-Log "Winget upgrade finished with exit code: $exitCode" -Level WARN
$script:HasErrors = $true
} }
Add-UpdateHistoryEntry -KB "winget" -Title "Winget Upgrade --all" -Size "N/A" -Result "ExitCode: $exitCode" -UpdateType "Winget" Add-UpdateHistoryEntry -KB "winget" -Title "Winget Upgrade --all" -Size "N/A" -Result "ExitCode: $exitCode" -UpdateType "Winget"
} }
catch { catch {
Write-Log "Winget upgrade error: $($_.Exception.Message)" -Level WARN Write-Log "Winget upgrade error: $($_.Exception.Message)" -Level ERROR
$script:HasErrors = $true
} }
} }
@@ -1165,12 +1176,25 @@ function Start-WindowsUpdater {
$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
$availableUpdates = Get-AvailableUpdates $availableUpdates = Get-AvailableUpdates
if (-not $availableUpdates -or @($availableUpdates).Count -eq 0) { if ($null -eq $availableUpdates) {
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"
Invoke-UpdateHook -Phase Post | Out-Null
Write-Log "Windows Updater finished (scan error)"
exit 1
}
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 Updates - $env:COMPUTERNAME" `
-Body "No Windows updates are currently available for $env:COMPUTERNAME." ` -Body "No Windows updates are currently available for $env:COMPUTERNAME." `
-EventName "NoUpdates" -EventName "NoUpdates"
Invoke-WingetUpgrade if (-not $dryRun) {
Invoke-WingetUpgrade
Export-UpdateHistory
}
Invoke-UpdateHook -Phase Post | Out-Null Invoke-UpdateHook -Phase Post | Out-Null
Write-Log "Windows Updater finished (no updates)" Write-Log "Windows Updater finished (no updates)"
exit 0 exit 0
@@ -1198,7 +1222,6 @@ function Start-WindowsUpdater {
Send-Notification -Subject "Dry-Run Report - $env:COMPUTERNAME" -Body $reportBody -EventName "DryRun" Send-Notification -Subject "Dry-Run Report - $env:COMPUTERNAME" -Body $reportBody -EventName "DryRun"
Invoke-WingetUpgrade
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)"
exit 0 exit 0
@@ -1214,8 +1237,8 @@ function Start-WindowsUpdater {
Export-UpdateHistory Export-UpdateHistory
# --- Send Summary Notification --- # --- Send Summary Notification ---
$successCount = ($script:UpdateResults | Where-Object { $_.Result -match "Install|Success" } | Measure-Object).Count $successCount = ($script:UpdateResults | Where-Object { $_.Result -match "^Installed|^Succeeded|^Success" } | Measure-Object).Count
$failCount = ($script:UpdateResults | Where-Object { $_.Result -notmatch "Install|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" "Windows Update Summary for $env:COMPUTERNAME"
@@ -1232,8 +1255,9 @@ function Start-WindowsUpdater {
} }
$summaryText = $summaryBody -join "`n" $summaryText = $summaryBody -join "`n"
$priority = if ($failCount -gt 0) { "high" } else { "default" } $hasFailures = $failCount -gt 0 -or $script:HasErrors
$subjectPrefix = if ($failCount -gt 0) { "Updates (with errors)" } else { "Updates OK" } $priority = if ($hasFailures) { "high" } else { "default" }
$subjectPrefix = if ($hasFailures) { "Updates (with errors)" } else { "Updates OK" }
Send-Notification -Subject "$subjectPrefix - $env:COMPUTERNAME" ` Send-Notification -Subject "$subjectPrefix - $env:COMPUTERNAME" `
-Body $summaryText -Priority $priority ` -Body $summaryText -Priority $priority `