Reviews and improvements
This commit is contained in:
@@ -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 `
|
||||||
|
|||||||
Reference in New Issue
Block a user