Notice: Due to size constraints and loading performance considerations, scripts referenced in blog posts are not attached directly. To request access, please complete the following form: Script Request Form Note: A Google account is required to access the form.
Disclaimer: I do not accept responsibility for any issues arising from scripts being run without adequate understanding. It is the user's responsibility to review and assess any code before execution. More information

ADDS Server Patching: A Journey Through KB Numbers and Updates Types

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:

  1. A Server 2016 machine showing a servicing stack update (KB5054006) wrongly labeled as a cumulative update
  2. Another Server 2016 machine showing an incorrect KB number entirely (KB5050109)
  3. 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:

  1. Connects to any specified server
  2. Retrieves the most recent installed hotfixes
  3. Looks up detailed descriptions for each KB
  4. Categorizes updates by type
  5. 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:

  1. Never change what's actually installed on the server - enhance the information instead
  2. Windows updates come in various types that need specific handling
  3. Web-based verification provides more accurate and future-proof results than hardcoding
  4. Error handling is crucial when dealing with network operations
  5. 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.

Previous Post Next Post

نموذج الاتصال