Part of the DBA-Tools Project.
Config-Driven Multi-Server Patching, and a One-Command Status Check
Invoke-SqlPatch.ps1 reads a server list and CU definitions from a config file, compares each instance’s installed version against the target, downloads the installer if needed, and applies it, locally or remotely over WinRM. patch-summary.ps1 is the fast companion check: every SQL Server instance and SSMS install on a machine, its build mapped to a human-readable CU label, and recent patch activity from the log history, in one command.
A real gap turned up testing this pair: patch-summary.ps1‘s build-to-CU lookup table had no entry for SQL Server 2025 at all, so a fully up-to-date SQL 2025 instance reported as “Unknown build.” Fixed below.
Why a Config-Driven Patcher and a Status Check Matter
- Patch status should be answerable in one command, not a per-server manual check.
patch-summary.ps1gives version, CU label, SSMS version, and recent activity for a machine in one pass. Invoke-SqlPatch.ps1never downloads or installs anything by accident.-WhatIfreports status only,-DownloadOnlystages installers without applying them, and even a real run prompts per-server before installing unless-Forceis explicitly passed.- The version comparison is genuinely useful in both directions. Behind means patch needed; ahead of the configured target is flagged too, worth knowing when someone patched a server manually and the config file wasn’t updated to match.
The Bug: SQL Server 2025 Missing From the CU Lookup Table
patch-summary.ps1 maps a raw build number like 17.0.4045.5 to a readable label like “SQL 2025 CU5” using a hardcoded lookup table. Running it against this lab instance, genuinely running SQL Server 2025, returned:
MSSQLSERVER 17.0.4045.5 Unknown build Enterprise Developer Edition (64-bit)
The table simply had no SQL Server 2025 (major version 17) entries at all, only 2016 through 2022. powershell\patching\sql\patch-config.psd1, the config file Invoke-SqlPatch.ps1 reads, already had the correct mapping (17.0.4045.5 → SQL2025 CU5 / KB5084896), the two files had simply drifted apart. Fixed by adding the missing entry:
'17.0.4045.5' = 'SQL 2025 CU5'
Re-running against the same instance:
MSSQLSERVER 17.0.4045.5 SQL 2025 CU5 Enterprise Developer Edition (64-bit)
Invoke-SqlPatch.ps1
<#
.SYNOPSIS
Download and apply SQL Server Cumulative Updates to local or remote servers.
.DESCRIPTION
Reads server list and patch definitions from patch-config.psd1. For each server and
each installed SQL instance: detects installed version, compares against target,
downloads the CU installer if needed, applies it locally or remotely via WinRM.
.PARAMETER WhatIf
Report version status and planned actions without downloading or installing anything.
.PARAMETER DownloadOnly
Download CU installers to PatchRoot but do not install.
.PARAMETER Force
Skip the per-server install confirmation prompt.
#>
(The full script is in the repo, link below.)
Real Output — WhatIf Against This Lab Instance
No elevation needed to preview:
[11:08:35] SQL Server Autopatch - 20260730-110835
[11:08:35] Config : powershell\patching\sql\patch-config.psd1
[11:08:35] Servers : localhost
[11:08:35] PatchRoot : D:\SQLPatches
[11:08:35] [WhatIf - no changes will be made]
[11:08:36] --- localhost ---
[11:08:36] Instance : MSSQLSERVER (MSSQL17.MSSQLSERVER)
[11:08:36] Installed : 17.0.4045.5
[11:08:36] Target : 17.0.4045.5 (SQL2025 CU5 / KB5084896)
[11:08:36] Status : UP TO DATE - no action needed.
Server Instance Installed Target Status
------ -------- --------- ------ ------
localhost MSSQLSERVER 17.0.4045.5 17.0.4045.5 UpToDate
Correctly detects the local instance’s real version, compares it against the configured target for SQL 2025, and reports the accurate result: this instance is already current, no patch needed. Genuine output against a real, currently-patched instance, not a manufactured example.
patch-summary.ps1
<#
.SYNOPSIS
Show patch status for all SQL Server instances and SSMS on this machine.
.DESCRIPTION
Detects all installed SQL Server instances, queries their version, maps the build
number to a known CU level, and displays SSMS version alongside patch log history.
#>
(The full script is in the repo, link below.)
Real Output — Run Against This Lab Machine (After the Fix)
SQL Server Patch Summary - 2026-07-30 11:10
----------------------------------------------------------------------
SQL Server Instances
MSSQLSERVER 17.0.4045.5 SQL 2025 CU5 Enterprise Developer Edition (64-bit)
SSMS
SQL Server Management Studio 22 v22.7.0
Recent patch activity (last 5 logs):
2026-07-30 11:08 ssms-uninstall-20260730-110856.log Success
2026-07-30 11:08 ssms-install-20260730-110847.log Unknown
2026-07-30 11:08 sql-autopatch-20260730-110835.log Unknown
2026-06-15 17:34 ssms-uninstall-20260615-173412.log Success
2026-06-15 17:34 ssms-uninstall-20260615-173406.log Success
Every field correct after the fix: instance version, the now-resolving CU label, SSMS version, and real log history pulled straight from output-files\patches\, including the same-session -WhatIf runs from testing this exact pillar, logged and showing up honestly as Unknown (a status check, not an applied patch) rather than misreported as a success.
How To Run From The Repo
git clone https://github.com/peterwhyte-lgtm/dba-tools
cd dba-tools
.\Initialize-Environment.ps1
# Check status without changing anything
.\powershell\patching\sql\Invoke-SqlPatch.ps1 -WhatIf
# One-command overview of everything on this machine
.\powershell\patching\patch-summary.ps1
# Download only, install later
.\powershell\patching\sql\Invoke-SqlPatch.ps1 -DownloadOnly
# Patch specific servers, skip confirmation
.\powershell\patching\sql\Invoke-SqlPatch.ps1 -Server SQL01,SQL02 -Force
These scripts live in the repo at:
Understanding the Results
- “Unknown build” in
patch-summary.ps1means the CU lookup table hasn’t been updated for that SQL Server version yet, not that something is wrong with the instance. Add the mapping, same as the SQL 2025 fix here, rather than assuming it’s a script defect. - “AHEAD” status in
Invoke-SqlPatch.ps1means the installed build is newer than the configured target. Worth updatingpatch-config.psd1rather than treating it as an error, someone likely patched manually and the config drifted behind reality. Unknownin the recent-activity log is expected for status-only and-WhatIfruns. Only a completed, successful install writes success language to the log.
Best Practices
- Keep
patch-config.psd1‘s CU download URLs and target versions current manually, Microsoft doesn’t expose a queryable API for this, the note at the bottom of both scripts’ output says so plainly. - Run
Invoke-SqlPatch.ps1 -WhatIfacross your whole server list before any real patch window, know what’s behind, current, and ahead before deciding what to actually apply. - Use
-DownloadOnlyahead of a maintenance window to stage installers, then run without it during the window itself to apply with less time pressure. - Run
patch-summary.ps1as a routine check independent of any patch cycle, it’s cheap, read-only, and catches CU-map gaps (like the SQL 2025 one here) the moment a new major version shows up as “Unknown.”
Related Scripts
You may also find these scripts useful:
- SQL Server Installation and Patching (hub)
- Install and Update SSMS via PowerShell
- Pre-Install and Post-Install Checks
- Get Version Upgrade Readiness
Frequently Asked Questions
My instance shows “Unknown build” even though I know the patch level, what’s wrong?
The build-to-CU lookup table in patch-summary.ps1 needs a manual entry added for that specific build. This isn’t unique to any one version, the table simply needs updating every time a new CU releases, exactly the gap the SQL 2025 fix in this post closes for that version.
What happens if Invoke-SqlPatch.ps1 finds an instance running a newer build than the configured target?
It reports Ahead and takes no action, it never downgrades. Update patch-config.psd1‘s target version to match reality if the newer build is intentional.
Summary
One script patches, config-driven and safe by default (-WhatIf, -DownloadOnly, per-server confirmation); the other reports status in one command across every SQL Server and SSMS install on a machine. A real gap in the status script’s CU lookup table, no SQL Server 2025 entry at all, was found and fixed by testing both against this lab’s genuinely-current SQL 2025 instance, confirmed correct with real before-and-after output.
Leave a Reply