Accurate update reporting is crucial for my security posture and compliance requirements. Recently, I encountered several challenges with my domain controller update reporting script that produced inaccurate KB information. This blog post outlines how I transformed a problematic script into a reliable solution, and eventually developed a specialized hotfix reporting tool.
The Initial Problem: Incorrect KB Numbers
My original OS Update Report script generated misleading information about installed updates on several servers. While it correctly reported most updates, three specific servers had incorrect KB numbers or descriptions:
- A Server 2016 machine showing a servicing stack update (KB5054006) wrongly labeled as a cumulative update
- Another Server 2016 machine showing an incorrect KB number entirely (KB5050109)
- A Server 2012 R2 machine reporting an outdated KB number (KB5017220)
Here's how this looked in the log:
grizzly.bear.local
OS Version : Microsoft Windows Server 2016 Standard
OS Build : 10.0.14393 (Build 14393)
Last Update : 2025-03-15
Update Info : 2025-03 Cumulative Update for Windows Server 2016 for x64-based Systems
(KB5054006)
KB Number : KB5054006
The problem: KB5054006 is actually a Servicing Stack Update, not a Cumulative Update!
Understanding the Root Cause
The source of the issue was that my script wasn't intelligent enough to identify different types of updates. Windows updates come in various flavors:
- Cumulative Updates (monthly OS updates)
- Servicing Stack Updates (update infrastructure)
- Security Updates (specific vulnerability fixes)
- Quality Rollups (for older Server versions)
- .NET Framework Updates
- License Updates
My script was naively assuming any recent KB was a cumulative update, which led to misreporting.
The Fix: Update Type Identification
My first attempt at fixing the issue involved hardcoding known KB numbers, but that would require constant maintenance. Instead, I created a function to identify update types:
function Test-IsServiceStackUpdate {
param(
[string]$KBNumber,
[string]$Description
)
# Check specific known Servicing Stack KB numbers first
if ($KBNumber -eq "KB5054007" -or $KBNumber -eq "KB5054006") {
return $true
}
# Check description
if ($Description -like "*Servicing Stack*" -or
$Description -like "*.NET Framework*" -or
$Description -like "*License*" -or
$Description -like "*Preview*") {
return $true
}
return $false
}
This function checks both the KB number and description to determine if an update is a Servicing Stack Update. I then modified the script to preserve these updates without trying to "correct" them.
Enhanced Solution: Web-Based KB Verification
To make the solution more robust, I implemented web-based KB verification that looks up Microsoft's support site for accurate update descriptions:
function Get-KBDescription {
param(
[string]$KBNumber,
[string]$OSVersion,
[string]$ProxyServer
)
try {
# Configure web client with proxy
$webClient = New-Object System.Net.WebClient
if ($ProxyServer) {
$webProxy = New-Object System.Net.WebProxy($ProxyServer)
$webClient.Proxy = $webProxy
}
# Search Microsoft's support site
$encodedQuery = [System.Web.HttpUtility]::UrlEncode("$KBNumber $OSVersion")
$url = "https://support.microsoft.com/en-us/search/results?query=$encodedQuery"
$html = $webClient.DownloadString($url)
# Process results and extract descriptions
# (additional code omitted for brevity)
}
catch {
Write-Log "Error looking up KB information: $($_.Exception.Message)"
return "Security Update for Windows ($KBNumber)"
}
}
This approach allowed my script to dynamically fetch accurate KB information without hardcoding values.
Key Learning: Never Change What's Installed
An important lesson I learned: never replace information about what's actually installed on the server. My improved script preserves the actual installed KB numbers and only enhances the descriptions.
When I mistakenly developed a version that "corrected" installed KBs, it created more problems by reporting KBs that weren't actually installed! The fixed script respects what's installed while providing better context.
From Fix to Feature: Creating a Dedicated Hotfix Reporting Tool
After solving the core reporting issue, I realized the KB lookup capability could be developed into a standalone tool. I created a new PowerShell script that:
- Connects to any specified server
- Retrieves the most recent installed hotfixes
- Looks up detailed descriptions for each KB
- Categorizes updates by type
- Provides a comprehensive report
Here's a snippet of the final script:
# Main function to get detailed hotfix information
param(
[Parameter(Mandatory=$true)]
[string]$Server,
[string]$ProxyServer = "peoxy.bear.local:3129",
[int]$HotfixCount = 15,
[string]$OutputFile = ""
)
try {
# Get OS info
$OS = Get-WmiObject -Class Win32_OperatingSystem -ComputerName $Server
-ErrorAction Stop
$OSVersion = $OS.Caption
$OSBuild = $OS.Version + " (Build " + $OS.BuildNumber + ")"
# Get hotfixes
$hotfixes = Get-HotFix -ComputerName $Server -ErrorAction Stop |
Sort-Object -Property InstalledOn -Descending |
Select-Object -First $HotfixCount
# Process each hotfix
foreach ($hotfix in $hotfixes) {
# Get enhanced description
$enhancedDescription = Get-KBDescription -KBNumber $hotfix.HotFixID `
-OSVersion $OSVersion -ProxyServer $ProxyServer
# Determine update type
$updateType = Get-UpdateType -KBNumber $hotfix.HotFixID -Description
$hotfix.Description
# Add to results collection
# (additional code omitted for brevity)
}
}
catch {
Write-Host "Error: $($_.Exception.Message)" -ForegroundColor Red
}
This script generates output like:
======= Hotfix Report for bearclaws.bear.local =======
OS Version: Microsoft Windows Server 2019 Datacenter
OS Build: 10.0.17763 (Build 17763)
Total Hotfixes: 15
=======================================
KBNumber InstalledOn UpdateType EnhancedDescription
-------- ----------- ---------- -------------------
KB5054007 3/14/2025 12:00:00 AM Servicing Stack Servicing Stack Update for Windows Server 2019 (KB5054007)
KB5050110 1/27/2025 12:00:00 AM Security Update 2025-01 Security Update for Windows Server 2019 (KB5050110)
KB5052000 2/15/2025 12:00:00 AM Cumulative Update 2025-02 Cumulative Update for Windows Server 2019 (KB5052000)
...
======= Update Type Summary =======
Servicing Stack Update: 1
Cumulative Update: 5
Security Update: 9
================================
Handling Different Update Types
A crucial part of the solution was correctly identifying different update types. The final script uses this function:
function Get-UpdateType {
param(
[string]$KBNumber,
[string]$Description,
[string]$EnhancedDescription
)
# Service Stack Updates
if ($KBNumber -eq "KB5054007" -or $KBNumber -eq "KB5054006" -or
$Description -like "*Servicing Stack*" -or $EnhancedDescription -like "*Servicing Stack*") {
return "Servicing Stack Update"
}
# Cumulative Updates
if ($EnhancedDescription -like "*Cumulative Update*" -or
$Description -like "*Cumulative Update*" -or
($KBNumber -like "*5055*" -and $KBNumber -notlike "*5054*")) {
return "Cumulative Update"
}
# Quality Rollups (for older Server versions)
if ($EnhancedDescription -like "*Quality Rollup*" -or
$Description -like "*Monthly Quality*") {
return "Monthly Rollup"
}
# Default case
return "Security Update"
}
Conclusion: From Bug to Feature
What started as troubleshooting a bug in my update reporting script evolved into a comprehensive hotfix reporting tool. The journey taught me several valuable lessons:
- Never change what's actually installed on the server - enhance the information instead
- Windows updates come in various types that need specific handling
- Web-based verification provides more accurate and future-proof results than hardcoding
- Error handling is crucial when dealing with network operations
- A good reporting tool should categorize information in a meaningful way
The final scripts now provide accurate information about installed updates, properly categorize
them by type, and present the information in a format that's useful for both quick inspection and compliance reporting.
Next time you're facing issues with update reporting in your environment, consider whether
your scripts are correctly identifying the different types of updates and preserving the actual installed information.