diff --git a/CHANGELOG.md b/CHANGELOG.md index fe1b147..006fb23 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,15 +5,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [4.4.0] - 2026-10-07 + ### Changed - The one-shot `SPSUpdate-RebootConfirm` boot task (automatic reboot) now runs as **`NT AUTHORITY\SYSTEM`** instead of the farm InstallAccount decrypted from `secrets.psd1`. This removes the stored-credential dependency that could abort the reboot at task registration (`0x8007052E` when the secret did not match the account password) and decouples the reboot from password rotation. `Add-SPSScheduledTask` gains a `-RunAsSystem` switch (mutually exclusive with `-ExecuteAsCredential`). Because a `SYSTEM` task reaches a UNC status store as the computer account, the share must grant the farm computer accounts (e.g. `Domain Computers`) Modify — add them to `New-SPSDashboardSite.ps1 -WriteAccounts`; `-Action ConfirmReboot` logs a clear, actionable warning (and retries on the next boot) when the grant is missing. ([#64](https://github.com/luigilink/SPSUpdate/issues/64)) ### Added -- **SharePoint 2016 / 2019 compatibility restored on the `4.x` line.** This maintenance line keeps the full feature set of the `5.x` (Subscription Edition) line but re-introduces support for SharePoint Server 2016 and 2019: the SharePoint cmdlets are loaded through the legacy `Microsoft.SharePoint.PowerShell` snap-in on 2016/2019 (and the `SharePointServer` module on Subscription Edition), both locally and in remote sessions, and `Get-SPSLocalVersionInfo` is version-aware again (`2016`/`2019`/`SE`) so the correct product-name string is matched in the registry. SharePoint 2016, 2019 and Subscription Edition all share the same `16.0` hive and `OSearch16` search service, so no other paths change. Version detection is automatic at runtime (no configuration change). As an added safety check on this multi-version line, `Start-SPSProductUpdate` now compares the update package's product line (2016/2019/SE, derived from the CU build) with the installed SharePoint line and fails fast — before stopping any service or launching the installer — when they do not match. ([#38](https://github.com/luigilink/SPSUpdate/issues/38)) - A deferred automatic reboot that was already performed **out-of-band** (a manual restart or a Windows Update reboot) is now reconciled instead of restarting the server again: when a `.pending` reboot request predates the server's last boot time, `Invoke-SPSAutomaticReboot` marks the Reboot phase `Done` ("Reboot already completed out-of-band"), clears the pending marker and skips the restart. A fresh reboot required by an install performed in the same run is unaffected. ([#68](https://github.com/luigilink/SPSUpdate/issues/68)) - `-Action ProductUpdate` now refreshes the installed server's live **Patch Status** on the Binaries Installation card after the cumulative update is installed, so the column reflects the real post-install state (typically `Upgrade Required` until the Configuration Wizard runs) instead of keeping the pre-install value (`No Action Required`) until the next `Default` run. It reuses the same master-side patch-status read as the `Default` baseline (no remoting) and is status-only — the Content Databases and Configuration Wizard cards still evolve only during a `Default` run. A failed read preserves the last-known-good value. ([#59](https://github.com/luigilink/SPSUpdate/issues/59)) + +## [4.3.0] - 2026-10-06 + +### Added + +- **SharePoint 2016 / 2019 compatibility restored on the `4.x` line.** This maintenance line keeps the full feature set of the `5.x` (Subscription Edition) line but re-introduces support for SharePoint Server 2016 and 2019: the SharePoint cmdlets are loaded through the legacy `Microsoft.SharePoint.PowerShell` snap-in on 2016/2019 (and the `SharePointServer` module on Subscription Edition), both locally and in remote sessions, and `Get-SPSLocalVersionInfo` is version-aware again (`2016`/`2019`/`SE`) so the correct product-name string is matched in the registry. SharePoint 2016, 2019 and Subscription Edition all share the same `16.0` hive and `OSearch16` search service, so no other paths change. Version detection is automatic at runtime (no configuration change). As an added safety check on this multi-version line, `Start-SPSProductUpdate` now compares the update package's product line (2016/2019/SE, derived from the CU build) with the installed SharePoint line and fails fast — before stopping any service or launching the installer — when they do not match. ([#38](https://github.com/luigilink/SPSUpdate/issues/38)) + - The near real-time dashboard now refreshes the live farm status at key transitions (the pre-patch baseline, after the Configuration Wizard completes, and the final render) instead of only at the pre-patch baseline. The master re-reads each server's patch status and re-publishes the content-database inventory with fresh upgrade status, so a successful campaign no longer shows a stale `Upgrade Required` Patch Status or a lingering pre-patch inconsistency banner once the farm is back to `No Action Required`. The Content Databases card also shows a database that SPSUpdate has already upgraded (State `Done`) as `No update pending` even before the next refresh. Server patch status and role are shown with short readable labels (for example `Upgrade Required`, `Application with Search`) via the new public helpers `ConvertTo-SPSPatchStatusLabel` and `ConvertTo-SPSRoleLabel`, instead of the raw enum names. ([#51](https://github.com/luigilink/SPSUpdate/issues/51)) - A standalone, idempotent helper `New-SPSDashboardSite.ps1` (in `src/`) that provisions the dashboard hosting target on an IIS/pull server — folder, SMB share (Modify for the write accounts), NTFS permissions, a static-file `web.config`, and either a dedicated IIS site or a sub-application under an existing site. It has no dependency on the `SPSUpdate.Common` module, so it can be copied on its own to the IIS server. A new wiki page, *Hosting the dashboard on IIS*, documents the validated setup (and the manual equivalent). ([#49](https://github.com/luigilink/SPSUpdate/issues/49)) - `Test-SPSUpdateReadiness.ps1` now checks the optional `Dashboard.OutputPath`: it verifies the folder exists and is writable, and warns when it is a master-local (drive-letter) path rather than a shared UNC path that worker runs can also reach. ([#49](https://github.com/luigilink/SPSUpdate/issues/49)) diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index 9065c96..4ddd889 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -1,12 +1,24 @@ # SPSUpdate - Release Notes -## [5.0.1] - 2026-09-21 +## [4.4.0] - 2026-10-07 -This patch fixes the near real-time patching dashboard on farms that have no content -databases (for example a dedicated search farm). +SPSUpdate 4.4.0 backports to the **`4.x` maintenance line** (SharePoint Server 2016 / 2019 and +Subscription Edition) three fixes already shipped on the `5.x` line in **v5.2.0**. The only +difference from the `5.x` line remains how the SharePoint cmdlets are loaded (the legacy +`Microsoft.SharePoint.PowerShell` snap-in on 2016/2019, the `SharePointServer` module on Subscription +Edition); the ported logic is otherwise identical. -### Fixed +> ⚠️ SharePoint Server 2016 and 2019 reached **Microsoft end of support on 14 July 2026** and no +> longer receive security updates. This line restores SPSUpdate **tooling compatibility** only; it +> does **not** restore vendor support. Plan a migration to Subscription Edition (and the `5.x` line). -- On a farm without content databases, the mount/upgrade sequences were shown as **Failed** with "Cannot bind argument to parameter 'Name' because it is an empty string." The inventory generator (`Initialize-SPSContentDbJsonFile`) now always writes a valid file with four (possibly empty) sequences; the master run skips the mount/upgrade sequences entirely when there is no content database (so the dashboard shows only ProductUpdate, the Configuration Wizard and side-by-side); and each sequence sub-run defensively ignores empty entries. +### Changed + +- **Reboot-confirm boot task runs as `NT AUTHORITY\SYSTEM`.** The one-shot `SPSUpdate-RebootConfirm` task (automatic reboot) no longer depends on the farm InstallAccount decrypted from `secrets.psd1`, removing a stored-credential failure at task registration (`0x8007052E`) and decoupling the reboot from password rotation. `Add-SPSScheduledTask` gains a `-RunAsSystem` switch. Because a `SYSTEM` task reaches a UNC status store as the computer account, the share must grant the farm computer accounts (e.g. `Domain Computers`) Modify; `-Action ConfirmReboot` logs a clear, actionable warning and retries on the next boot when the grant is missing. ([#64](https://github.com/luigilink/SPSUpdate/issues/64)) + +### Added + +- **Live Patch Status after a CU install.** `-Action ProductUpdate` now refreshes the installed server's Patch Status on the Binaries Installation card after a real install, so the column reflects the post-install state (typically `Upgrade Required` until the Configuration Wizard runs) instead of keeping the pre-install value until the next `Default` run. Status-only, no remoting, and a failed read preserves the last-known-good value. ([#59](https://github.com/luigilink/SPSUpdate/issues/59)) +- **Out-of-band reboot reconciliation.** A deferred automatic reboot already performed out-of-band (a manual restart or a Windows Update reboot) is reconciled instead of restarting the server again: when a `.pending` reboot request predates the server's last boot time, `Invoke-SPSAutomaticReboot` marks the Reboot phase `Done`, clears the pending marker and skips the restart. A fresh reboot required by an install performed in the same run is unaffected. ([#68](https://github.com/luigilink/SPSUpdate/issues/68)) A full list of changes in each version can be found in the [change log](CHANGELOG.md) diff --git a/src/Modules/SPSUpdate.Common/SPSUpdate.Common.psd1 b/src/Modules/SPSUpdate.Common/SPSUpdate.Common.psd1 index 1ecd50f..b2c660f 100644 --- a/src/Modules/SPSUpdate.Common/SPSUpdate.Common.psd1 +++ b/src/Modules/SPSUpdate.Common/SPSUpdate.Common.psd1 @@ -1,6 +1,6 @@ @{ RootModule = 'SPSUpdate.Common.psm1' - ModuleVersion = '5.0.1' + ModuleVersion = '4.4.0' GUID = 'd6f4e2b7-3a1c-4d8e-9f2a-6c5b7e0a1d34' Author = 'Jean-Cyril DROUHIN' CompanyName = 'luigilink' diff --git a/tests/SPSUpdate.Common.Tests.ps1 b/tests/SPSUpdate.Common.Tests.ps1 index 982b582..593204b 100644 --- a/tests/SPSUpdate.Common.Tests.ps1 +++ b/tests/SPSUpdate.Common.Tests.ps1 @@ -36,8 +36,8 @@ Describe 'SPSUpdate.Common module' { { Test-ModuleManifest -Path $modulePath -ErrorAction Stop } | Should -Not -Throw } - It 'manifest version is 5.0.0 or higher' { - (Test-ModuleManifest -Path $modulePath).Version | Should -BeGreaterOrEqual ([version]'5.0.0') + It 'manifest version is 4.0.0 or higher' { + (Test-ModuleManifest -Path $modulePath).Version | Should -BeGreaterOrEqual ([version]'4.0.0') } It 'exports exactly the expected public functions' {