Repository navigation
Configuration
SPSUpdate is driven by two PowerShell data files in the Config\ folder:
- a per-farm environment config (
*.psd1) — hand-edited, gitignored; - a secret store (
secrets.psd1) — DPAPI-encrypted, gitignored, never committed.
Only the *.example.psd1 templates are tracked in source control.
Copy Config\CONTOSO-PROD.example.psd1 to a real file (one per farm, for example
CONTOSO-PROD-CONTENT.psd1) and edit the values:
@{
ConfigurationName = 'PROD'
ApplicationName = 'contoso'
FarmName = 'CONTENT'
Domain = 'contoso.com'
CredentialKey = 'PROD-ADM'
Binaries = @{
ProductUpdate = $true
SetupFullPath = 'D:\SoftwarePackages\SPS\cumulativeupdates'
SetupFileName = @('uber-subscription-kb5002651-fullfile-x64-glb.exe')
ShutdownServices = $true
}
MountContentDatabase = $false
UpgradeContentDatabase = $true
SideBySideToken = @{
Enable = $false
BuildVersion = ''
}
}| Key | Description |
|---|---|
ConfigurationName |
Environment identifier (e.g. PROD, PPRD, DEV). Used in log/result file names. |
ApplicationName |
Application/customer code. Used in log/result file names. |
FarmName |
Logical farm name. Used in logs and in the ContentDB inventory file name. |
Domain |
DNS suffix appended to each farm server short name for CredSSP remoting. |
CredentialKey |
Name of the entry in secrets.psd1 that holds the InstallAccount. |
If an optional key is omitted, SPSUpdate applies a safe default:
| Key | Possible values | Default if omitted |
|---|---|---|
Binaries.ProductUpdate |
$true / $false
|
$true |
Binaries.ShutdownServices |
$true / $false
|
$true |
UpgradeContentDatabase |
$true / $false
|
$true |
MountContentDatabase |
$true / $false
|
$false |
SideBySideToken.Enable |
$true / $false
|
$false |
SideBySideToken.BuildVersion |
'' or a build, e.g. '16.0.17928.20238'
|
'' (skip) |
Remoting.AllowFallback |
$true / $false
|
$false |
Binaries.SetupFullPath and Binaries.SetupFileName are required as soon as
ProductUpdate is $true.
Boolean switches. Every Boolean configuration switch —
Binaries.ProductUpdate,Binaries.ShutdownServices,UpgradeContentDatabase,MountContentDatabase,SideBySideToken.Enable,Reboot.Enable,Reboot.Force,Remoting.AllowFallbackandExecution.InteractiveSequences— accepts$true/$falseor the integers1/0(1=$true,0=$false). Any other value (an out-of-range number, or a string such as'yes'or'false') is rejected with a clear error naming the property.
The previous JSON
StoredCredentialkey has been renamed toCredentialKey, and the configuration format moved from JSON to psd1. Runtime/output files (the ContentDB inventory and logs) stay JSON by design.
The InstallAccount credential is stored as a DPAPI-encrypted SecureString. Copy
Config\secrets.example.psd1 to Config\secrets.psd1:
@{
'PROD-ADM' = @{
Username = 'CONTOSO\svc_spsupdate'
PasswordSecure = 'PASTE-ConvertFrom-SecureString-OUTPUT-HERE'
}
}Each key (e.g. PROD-ADM) matches the CredentialKey of an environment config. The
recommended way to populate it is to run -Action Install -InstallAccount (Get-Credential)
as the service account, which writes the entry for you. To generate a value manually,
on the target server signed in as that account:
Read-Host -AsSecureString -Prompt 'Password' | ConvertFrom-SecureStringImportant
The encrypted value can only be decrypted by the same user account on the same
machine. secrets.psd1 is gitignored and must never be committed.
ConfigurationName, ApplicationName and FarmName populate the Environment,
Application and FarmName PowerShell variables used throughout the run and in the
generated file names.
Use ProductUpdate, SetupFullPath, SetupFileName and ShutdownServices to configure
the binary installation step. SetupFileName is an array, so you can list a single uber
package or the STS + WSSLOC (language) pair, installed in order.
Recommendation — keep the update binaries on a local drive. Point
SetupFullPathat a local path on each server (for exampleD:\SoftwarePackages\SPS\cumulativeupdates), not a UNC share. Running the cumulative-update.exedirectly from a UNC path (\\server\share\...) makes Windows apply its network-zone check and show an interactive "Open File - Security Warning" prompt, whichUnblock-Filecannot suppress (it only clears the file'sZone.Identifier, not the network-location zone). That prompt blocks an unattended or scheduled-task run. Copy the binaries to a local folder on each farm server first, then setSetupFullPathto that local folder.
An optional window restricting when the binary install may run:
Binaries = @{
# ...
Schedule = @{
Days = @('sat', 'sun') # optional: allowed days (mon..sun)
Time = '2:00 AM to 5:00 AM' # optional: same-day window
}
}Days and Time are both optional and independent. Omit Schedule (or its keys) to
allow the install at any time. The time window is same-day only (a window crossing
midnight is rejected). Both Binaries.Schedule and Reboot.Schedule share this shape.
Reboot is an optional, opt-in block. When enabled, a server that reports "reboot
required" after the ProductUpdate step (installer exit code 17022) is rebooted
automatically, once, and the live dashboard shows the reboot lifecycle.
Reboot = @{
Enable = $false # opt-in; default $false
Force = $false # reboot even on exit 0; default $false
Schedule = @{ # optional reboot window (same shape as above)
Days = @('sat', 'sun')
Time = '3:00 AM to 4:00 AM'
}
}| Key | Description | Default |
|---|---|---|
Enable |
Allow SPSUpdate to reboot a server automatically after a CU install. | $false |
Force |
Reboot even when the installer did not request one (exit code 0). Leave off to reboot strictly on exit code 17022. |
$false |
Schedule |
Optional { Days, Time } window restricting when the reboot may happen. Outside the window the dashboard shows the reboot as pending. |
any time |
How it works. The reboot is triggered only by the installer exit code 17022
(never by Windows pending-reboot registry markers, which stay stuck on production farms),
so it happens at most once per patching campaign. Just before rebooting, SPSUpdate marks
the Reboot phase as Running ("Automatic Reboot launched, check the server in a few
minutes"), writes a Windows Event Log entry (source Restart-SPSServer, ID 3010) and
registers a one-shot boot task that, when the server is back, stamps the reboot as Done
("Server back online after automatic reboot") and removes itself.
Warning
Reboots are per-server. On a farm you remain responsible for not rebooting the sole
Distributed Cache host or the last available WFE at the same time. Run ProductUpdate
(and its reboot) one server at a time, and keep the farm quorum in mind.
UpgradeContentDatabase runs Upgrade-SPContentDatabase in parallel (4 sequences) for
every content database that needs an upgrade.
MountContentDatabase attaches content databases to the target farm before the upgrade
step. It is typically used in farm migration scenarios (for example SharePoint Server
2019 → Subscription Edition) where content databases restored on the target SQL Server
need to be mounted on the new farm. The databases are read from the ContentDatabase
inventory JSON file (<ApplicationName>-<ConfigurationName>-<FarmName>-ContentDBs.json,
generated with the InitContentDB action).
Use Enable to turn on the side-by-side feature and BuildVersion to set the build used
by the side-by-side token. Zero downtime patching is a method of patching and upgrade
developed in SharePoint in Microsoft 365. For more details see
SharePoint Server zero downtime patching steps.
StatusStorePath is an OPTIONAL UNC share where every farm server writes its patching
progress so the master can assemble the near-real-time HTML dashboard. It must be writable
by the InstallAccount from every server.
StatusStorePath = '\\fileserver\spsupdate-status'Leave it empty (or omit it) to fall back to a local Results\status folder; in that case
ProductUpdate runs launched on the other servers are not captured centrally. The status
files of one campaign live under <StatusStorePath>\<App>-<Env>-<Farm>\, with the
dashboard written there as _dashboard.html. See the
Usage page for the campaign workflow and the ResetStatus action.
Grant the InstallAccount (the account that runs the scheduled tasks) Modify rights
on the share — both the SMB share permission and the NTFS permission. The four
upgrade/mount sequence tasks run as that account, so if it cannot write to the share the
upgrade phase never appears on the dashboard (the ProductUpdate and Wizard sections, written
by your interactive/master run under your own account, still show — which can hide the
problem). Run Test-SPSUpdateReadiness.ps1 to verify both your account and the InstallAccount
can write to the store before patching.
The near real-time dashboard is organised as three cards:
-
Binaries Installation — one row per farm server: status, SharePoint patch status
(
No Action Requiredbefore patching), installed build and completion time. - SharePoint Configuration Wizard — the same, plus a detail column.
-
Content Databases — one row per content database: web application, SQL instance, size,
sequence, upgrade status (
No update pendingwhen up to date) and processing state.
At the start of the master Default run the farm is enumerated with Get-SPServer and a
baseline row is pre-filled per server, so the whole farm is visible up front. The live farm
status (each server's patch status and the content-database upgrade status) is then refreshed
at key transitions — the pre-patch baseline, after the Configuration Wizard completes, and the
final render — so a successful campaign ends with the servers back to No Action Required and no
lingering warning. A server or database that is not reporting a healthy state is highlighted
as an informational farm inconsistency banner (a quick health check); it clears automatically
once the farm is healthy again. Patch status and server role are shown with short, readable
labels (for example Upgrade Required, Application with Search) derived from the Central
Administration values.
Dashboard = @{
OutputPath = 'E:\inetpub\spsupdate' # existing IIS folder; empty = campaign folder
}| Key | Meaning | Default |
|---|---|---|
Dashboard.OutputPath |
An existing folder (for example an IIS site folder) where the dashboard HTML is written so it can be served over HTTP. SPSUpdate does not create the IIS site — create it once (like the SPSConfigKit pull-server dashboard) and point OutputPath at its folder. |
'' (campaign folder) |
Only the campaign master (the server running the orchestrating Default run) writes to a
master-local OutputPath (a drive-letter path); worker runs then render to the campaign folder
and the master republishes the hosted copy on its next render. If you point OutputPath at a
shared UNC path (\\server\share\...) reachable by every farm server, worker runs publish to it
too — so a deferred ConfirmReboot that finishes after the master run exits still updates the
hosted dashboard. For a distributed farm where you want the hosted dashboard to update in near real
time, prefer a shared UNC OutputPath over a path that only exists locally on each server.
To serve this folder over HTTP (fileshare + IIS binding, like the SPSConfigKit pull-server
dashboard), see Hosting the dashboard on IIS and the standalone
New-SPSDashboardSite.ps1 helper.
The dashboard file name is derived per farm — <App>-<Env>-<Farm>-dashboard.html — so several
farms (INT / Preprod / PROD) can share a single IIS folder without colliding. At the start of a
new campaign (-Action ResetStatus) the previous dashboard is archived to
history\<name>_<timestamp>.html before a fresh one is created.
SPSUpdate runs the Configuration Wizard and side-by-side copy on other farm servers over PowerShell remoting. By default it uses CredSSP, which is required because the remote SharePoint cmdlets perform a second hop (to the configuration database on SQL, or to a binaries file share) and CredSSP is the mechanism that delegates the credential for that hop.
The optional Remoting block controls what happens when the CredSSP session cannot be opened
(for example on a farm where CredSSP was never configured):
Remoting = @{
AllowFallback = $false # $true to fall back to Negotiate when CredSSP fails
}| Key | Meaning | Default |
|---|---|---|
Remoting.AllowFallback |
When $true, fall back to Negotiate if the CredSSP session cannot be opened. CredSSP is always tried first. |
$false |
Security / behaviour note.
Negotiate(Kerberos with an NTLM fallback) cannot delegate the credential, so steps that need a second hop may fail unless Kerberos constrained delegation (KCD/RBCD) is configured for the service account. The fallback is a best-effort aid for farms with a broken CredSSP configuration, not a replacement for it — a warning is always logged when the fallback is used. Leave it off in secure/strict environments.
Test-SPSUpdateReadiness.ps1 tests this for you: it opens a real short-lived CredSSP session to
each server and reports PASS (CredSSP works), FAIL (CredSSP failed and
AllowFallback is off), or WARN (CredSSP failed but the Negotiate fallback works — mind the
double-hop caveat).
By default, the four parallel content-database sequences run as hidden scheduled tasks. In an attended patching campaign you can instead run them in visible Windows PowerShell windows so you can watch each sequence's progress live:
Execution = @{
InteractiveSequences = $true # visible windows in an attended run; tasks otherwise
}| Key | Meaning | Default |
|---|---|---|
Execution.InteractiveSequences |
When $true and the run is attended (interactive session, not launched by a scheduled task), launch the sequences in visible PowerShell windows instead of scheduled tasks. |
$false |
Notes:
- The windows run Windows PowerShell (
powershell.exe), because the SharePointServer module (Subscription Edition) targets the full .NET Framework and runs on Windows PowerShell 5.1, not PowerShell 7. - They run as the current user, who is already a farm administrator when running SPSUpdate
interactively. The content-database sequences run under that identity, so no InstallAccount is
needed for interactive mode — unlike the scheduled-task path, which must be told which account
to run as (
-ExecuteAsCredential). Make sure the signed-in operator has the usual farm-admin and content-database rights. -
Unattended / scheduled runs always use scheduled tasks, regardless of this setting, because
interactive windows die when the operator closes the session. Keep runs you launch from a
scheduled task (or that re-enter with
-Sequence) on the task path.
For the next steps, go to the Usage page.