From a580817384e7d4e9762b947cac95667d84d4bc15 Mon Sep 17 00:00:00 2001 From: LuigiLink Date: Wed, 7 Oct 2026 15:07:47 +0200 Subject: [PATCH 1/2] Refresh the local product cache before reading live Patch Status The #59 live Patch Status refresh read SPProductVersions.GetProductVersions, which returns the product/patch status cached in the configuration database. A local binary install does not update that cache until Get-SPProduct -Local runs on the server, so the card kept the pre-install status (e.g. No Action Required) instead of the real post-install one (e.g. Upgrade Blocked). Call Get-SPProduct -Local before reading the patch status in the ProductUpdate action (which runs locally on each patched server), so the read reflects the live state. This also makes the master's later aggregate reads accurate once every server has refreshed its own entry. Refs #71 --- CHANGELOG.md | 1 + src/SPSUpdate.ps1 | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c2f8986..582c70e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - `Set-SPSSideBySideToken` now retries transient SharePoint `SPWebService.Update()` conflicts with a bounded policy (6 attempts, 15 seconds between attempts) instead of failing on the first race with concurrent `PSCONFIG` writes. The retry is conflict-specific: non-conflict errors still fail fast, and a conflict that persists through the budget is surfaced as a failure. ([#79](https://github.com/luigilink/SPSUpdate/issues/79)) +- `-Action ProductUpdate` no longer leaves a **stale pre-install Patch Status** on the Binaries Installation card after installing a cumulative update. The live refresh added in #59 read the product/patch status from the configuration-database cache (`SPProductVersions.GetProductVersions`), which is not updated by a local binary install until `Get-SPProduct -Local` runs on the server. SPSUpdate now refreshes the local server's product cache with `Get-SPProduct -Local` before reading, so the card shows the real post-install status (for example `Upgrade Blocked` / `Installation Required`) instead of the pre-install value (`No Action Required`). ([#71](https://github.com/luigilink/SPSUpdate/issues/71)) ## [5.3.0] - 2026-10-08 diff --git a/src/SPSUpdate.ps1 b/src/SPSUpdate.ps1 index 8ea4484..010d393 100644 --- a/src/SPSUpdate.ps1 +++ b/src/SPSUpdate.ps1 @@ -1445,10 +1445,11 @@ Shutdown Services: $($envCfg.Binaries.ShutdownServices) else { $puInstalledBuild = '' try { $puInstalledBuild = (Get-SPSInstalledProductVersion).FileVersion.ToString() } catch { $puInstalledBuild = '' } - # Refresh this server's live Patch Status only after a real install, so the card - # shows the post-install state (no-op runs keep the last-known-good value). + # Refresh live Patch Status only after a real install. Get-SPSServersPatchStatus + # reads the config-DB product cache, so refresh this server with -Local first. $puPatchStatus = '' if ($installPerformed) { + try { $null = Get-SPProduct -Local } catch { Write-Verbose -Message "Get-SPProduct -Local refresh failed: $($_.Exception.Message)" } try { $puPatchStatus = ConvertTo-SPSPatchStatusLabel -Status "$(Get-SPSServersPatchStatus -Server $thisServer)" } catch { $puPatchStatus = '' } } if ([string]::IsNullOrWhiteSpace($puPatchStatus)) { From 62e1d86c32b5e6fb39ec5a0eacc2c0ed4004a33b Mon Sep 17 00:00:00 2001 From: LuigiLink Date: Wed, 7 Oct 2026 16:17:29 +0200 Subject: [PATCH 2/2] Make the product-cache refresh error terminating Add -ErrorAction Stop to Get-SPProduct -Local so a non-terminating failure is caught by the surrounding try/catch and handled as the intended non-fatal verbose path, instead of leaking an error record before the read continues. Refs #71 --- src/SPSUpdate.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SPSUpdate.ps1 b/src/SPSUpdate.ps1 index 010d393..3f8c0df 100644 --- a/src/SPSUpdate.ps1 +++ b/src/SPSUpdate.ps1 @@ -1449,7 +1449,7 @@ Shutdown Services: $($envCfg.Binaries.ShutdownServices) # reads the config-DB product cache, so refresh this server with -Local first. $puPatchStatus = '' if ($installPerformed) { - try { $null = Get-SPProduct -Local } catch { Write-Verbose -Message "Get-SPProduct -Local refresh failed: $($_.Exception.Message)" } + try { $null = Get-SPProduct -Local -ErrorAction Stop } catch { Write-Verbose -Message "Get-SPProduct -Local refresh failed: $($_.Exception.Message)" } try { $puPatchStatus = ConvertTo-SPSPatchStatusLabel -Status "$(Get-SPSServersPatchStatus -Server $thisServer)" } catch { $puPatchStatus = '' } } if ([string]::IsNullOrWhiteSpace($puPatchStatus)) {