Skip to content

Release v4.2.0 - near real-time patching dashboard#22

Merged
luigilink merged 15 commits into
mainfrom
release/4.2.0
Jun 30, 2026
Merged

Release v4.2.0 - near real-time patching dashboard#22
luigilink merged 15 commits into
mainfrom
release/4.2.0

Conversation

@luigilink

Copy link
Copy Markdown
Owner

Closes #21.

Adds a near real-time HTML dashboard for a farm-wide cumulative-update campaign, fed by a shared status store. Backward compatible with v4.1.0.

Highlights

  • Status store: Set-SPSUpdateStatus / Get-SPSUpdateStatus (atomic per-scope JSON, each writer owns its file) + Get-SPSStatusCampaignPath.
  • Dashboard: Export-SPSUpdateProgressReport renders a self-contained, auto-refreshing HTML page (overall state, per-phase sections, colored badges, per-sequence percentage). Finished scopes collapse by default; real setup.exe / psconfig.exe exit codes are surfaced.
  • Config / actions: StatusStorePath (UNC, local fallback), -Action ResetStatus (creates the empty 'waiting' dashboard).
  • Instrumentation: SPSUpdate.ps1 feeds the store and regenerates the dashboard live across ProductUpdate, the four sequences, the Wizard and side-by-side.
  • Readiness: Test-SPSUpdateReadiness.ps1 probes store write access as the current user and the InstallAccount.
  • Manifest bumped to 4.2.0.

Validation

  • PSScriptAnalyzer clean; 164 Pester tests passing (1 Windows-only skipped).
  • Validated live on a real 3-server Subscription Edition farm with an actual CU end-to-end (binary install, parallel content-database upgrade of 13 DBs, post-setup Configuration Wizard) — all reflected on the dashboard in near real time.

See RELEASE-NOTES.md for the full notes.

luigilink and others added 15 commits June 29, 2026 16:31
Groundwork for the v4.2.0 near-real-time patching dashboard:
- Add an optional StatusStorePath (UNC share) to the environment config; the
  loader defaults it to '' (fall back to the local Results\status folder).
- New Test-SPSUpdateReadiness.ps1 (modelled on Test-SPSWeatherReadiness):
  validates the module import, config keys, the DPAPI secret, elevation, the
  status store reachability + write access (temporary probe file), and the
  per-server WinRM/CredSSP reachability. Read-only.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Core of the v4.2.0 live dashboard data layer:
- Set-SPSUpdateStatus: atomic per-scope JSON upsert (temp file + move, with a
  short retry for transient UNC sharing violations). A scope is Server+Scope,
  so each writer (the 4 sequence tasks, each server's local ProductUpdate, the
  master recording every server's wizard state) owns a distinct file and they
  never write concurrently. Optionally upserts a per-item state (DB / setup
  file) with detail and exit code.
- Get-SPSUpdateStatus: merges every scope file of a campaign, skipping missing,
  empty, half-written or *.tmp.* files so a partial campaign never throws.
- Get-SPSStatusCampaignPath: resolves <root>\<App>-<Env>-<Farm>, falling back
  to a local Results\status folder when StatusStorePath is empty.
- Export the three functions; cross-platform Pester tests for the round-trip,
  item accumulation/update-in-place, scope/server isolation, and resilience.

149 Pester tests passing; PSScriptAnalyzer clean.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Render a self-contained HTML dashboard from the campaign status store:
- Overall roll-up cards (state, servers, done/running/failed) and one section
  per phase (ProductUpdate, Mount/Upgrade sequences, Wizard, SideBySide) with
  colored state badges, per-item tables (state/detail/exit code) and per-scope
  percentage.
- Server-rendered (no fetch), so it works over file:// from the share; carries
  a meta-refresh so an open browser updates itself while patching runs, and
  -Completed turns the refresh off with a final state. Atomic write so a reader
  never sees a half-written page.
- Extend the shared HTML head helper with an optional meta-refresh and state
  badge styles.

Export the function; cross-platform tests cover the running view, the Failed
and Completed states, the empty campaign, and HTML encoding. 161 tests pass.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Instrument the run to feed the shared status store and regenerate the dashboard:
- Resolve the campaign path once (Get-SPSStatusCampaignPath) and add two
  best-effort local helpers, Write-SPSStatus and Write-SPSDashboard, that never
  block the run on a status/report failure.
- New -Action ResetStatus clears the campaign folder so a fresh patching round
  starts clean.
- ProductUpdate: per-server scope with a per-setup-file item (Running -> Done /
  Failed); writes to the shared store so each server's local run is captured.
- Sequence tasks: per-sequence scope with per-database items and a running
  percentage (mount/upgrade), Done/Failed at the end.
- Master full run: regenerates the dashboard on every wait-loop iteration while
  the four sequences progress; records the Configuration Wizard state per server
  (local + remote, Running/Done/Failed/Skipped) and the side-by-side step; and
  renders a final -Completed dashboard (auto-refresh off) at the end.
- Add 'ResetStatus' to the Action set.

Parse OK, PSScriptAnalyzer clean, 161 Pester tests passing.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Bump module manifest to 4.2.0 (test guard updated).
- wiki/Usage.md: 'Near real-time patching dashboard' + 'Pre-flight readiness
  check' sections (StatusStorePath, ResetStatus, campaign workflow).
- wiki/Configuration.md: StatusStorePath reference.
- src/SPSUpdate_README.md (offline guide): dashboard + readiness sections.
- CHANGELOG.md [4.2.0] and RELEASE-NOTES.md (release body).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The example config commented 'Default if omitted: $true' but set the value
to $false. Set it to $true so the template matches the documented default
and the recommended behaviour.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
ResetStatus now creates the campaign folder (when missing) and generates the
empty 'waiting for the first update' dashboard immediately, so the operator
can open _dashboard.html in a browser before launching the ProductUpdate runs
and the master Default run, and watch it fill up live. Previously the folder
and dashboard only appeared on the first status write.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…unt)

The Done/Running/Failed cards summed both the scope-level state and each of
its item states, so two servers each running one ProductUpdate binary showed
'4 Done' instead of '2'. Roll up leaf units only: count the items when a scope
has any (each binary, each database), otherwise the scope state itself (e.g.
the Wizard, which has a per-server state but no items). Add tests locking the
no-double-count behaviour for item-bearing and item-less scopes.

163 Pester tests passing; PSScriptAnalyzer clean.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The master started the four sequence tasks one by one with a single 60-90s
Start-Sleep between each (to avoid OWSTimer conflicts) and only regenerated
the dashboard once it reached the wait loop. During that staggered start
(~4-6 min) a running sequence wrote its status but the dashboard did not
refresh, so the upgrade section appeared late. Now regenerate the dashboard
right after each task start and every ~10s during the pause, so per-database
progress shows up immediately.

163 Pester tests passing; PSScriptAnalyzer clean.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…s store

The upgrade/mount sequence tasks run as the InstallAccount (the scheduled-task
service account), not the interactive user. When that account lacks write
access to the UNC status store, the sequences silently fail to publish their
status and the dashboard never shows the upgrade phase (only ProductUpdate and
Wizard, written by the interactive/master run, appear) -- exactly the symptom
observed on a real farm.

- Test-SPSUpdateReadiness.ps1 now probes write access twice: as the current
  user, and as the InstallAccount (launches a short process under its
  credential and verifies the file lands on the share). A missing service-
  account write is a FAIL with remediation guidance.
- Document the required share permission (InstallAccount needs Modify on the
  SMB share + NTFS) in Getting-Started, Configuration, Usage and the offline
  guide. CHANGELOG updated (also records the ResetStatus/empty-dashboard,
  staggered-start live refresh, and the roll-up double-count fix).

PSScriptAnalyzer clean; 163 Pester tests passing.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
After the no-double-count fix, the overall state was derived only from leaf
item states, so a scope still Running whose recorded items all happened to be
Done (e.g. Sequence1 during the staggered start, before sequences 2-4 begin
and before the Wizard) flipped the overall badge to 'Done' prematurely.

Separate the two concerns: card counts stay leaf-unit based (no double count),
while the overall state now considers both scope states and item states, so it
stays 'Running' while any scope is Running/Pending and only reads 'Done' when
everything is truly finished (or the final -Completed render). Add a test
locking this (scope Running + items Done -> overall Running, Done count 2).

164 Pester tests passing; PSScriptAnalyzer clean.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
On a completed dashboard the footer said 'refreshes itself while patching is
in progress', contradicting the 'Campaign completed (auto-refresh off)' note
in the header. Make the footer conditional: 'reflects the completed campaign'
when done, the live wording only while running.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Operators had to scroll past every completed block to find what still needs
attention. Render each scope as a native <details>/<summary>: finished scopes
(Done/Skipped) are collapsed by default, active ones (Running/Pending/Failed/
Warning) stay expanded. The always-visible summary line keeps the badge, the
percentage and a compact 'N/M done' count, and a CSS chevron rotates on expand.
No JavaScript - works offline over file:// and is keyboard/screen-reader
accessible. On the live (auto-refreshing) dashboard a scope re-collapses once
it turns Done, so the view stays focused on in-flight work.

164 Pester tests passing; PSScriptAnalyzer clean.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The Exit column and the wizard status were never populated because the
instrumentation discarded the process exit codes. Wire them through:
- Start-SPSProductUpdate now returns the setup.exe exit code (or $null when no
  install was needed) and no longer leaks the iisreset -start Process object
  into its output. SPSUpdate.ps1 records it on the ProductUpdate item with a
  friendly detail (0 -> installed, 17022 -> reboot required, 17025 -> already
  installed) and the numeric code in the Exit column.
- The Configuration Wizard captures the psconfig.exe exit code (local via
  Start-SPSConfigExe, remote via Start-SPSConfigExeRemote) -- robustly filtering
  the integer return from the functions' Write-Output lines -- and records it in
  the scope detail, e.g. 'PSConfig completed (exit 0)'.

Content-database upgrade items have no process exit code (cmdlet) so their Exit
column stays blank by design. PSScriptAnalyzer clean; 164 Pester tests passing.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Set the [4.2.0] date to 2026-06-30 and expand RELEASE-NOTES with the
improvements that came out of the real-farm live test: the readiness
service-account write probe, the collapsible finished scopes, the surfaced
process exit codes, the required share permission, and a note that the
release was validated end-to-end on a real Subscription Edition farm with an
actual cumulative update.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions

Copy link
Copy Markdown

Pester Test Results

  1 files   19 suites   2s ⏱️
 72 tests  72 ✅ 0 💤 0 ❌
165 runs  165 ✅ 0 💤 0 ❌

Results for commit 11fcbc2.

@luigilink luigilink merged commit d88f215 into main Jun 30, 2026
3 checks passed
@luigilink luigilink deleted the release/4.2.0 branch June 30, 2026 15:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Near real-time patching dashboard (v4.2.0)

1 participant