-
Notifications
You must be signed in to change notification settings - Fork 0
Configuration
Important
The kit's first-class deliverable is the SharePoint farm configuration in
scripts/sps/. The PDC, PULL and SQL .psd1 files documented here are
reference samples only — in production you should describe those tiers
in your own tooling and only keep Secrets.psd1 + CfgAppSps.psd1 from
this kit.
SPSConfigKit is driven by two PowerShell data files:
-
scripts/Secrets.psd1— centralised credential material. -
scripts/sps/CfgAppSps.psd1— the SharePoint farmConfigurationDataconsumed byCfgAppSps.ps1.
The configuration scripts (CfgAppSps.ps1, plus the example PDC / PULL / SQL
scripts) import both files and compile one MOF per node defined in
AllNodes.
scripts/
├── Secrets.psd1 ← credentials, shared by every config script
├── init/
│ ├── Initialize-DscNode.psd1 ← prerequisite manifest (modules, certs)
│ ├── Initialize-DscNode.ps1
│ ├── Initialize-DscEncryption.ps1
│ ├── Initialize-SoftwarePackages.psd1 ← package manifest (binaries, LPs, CUs)
│ └── Initialize-SoftwarePackages.ps1
├── sps/
│ ├── CfgAppSps.psd1 ← SharePoint farm ConfigurationData
│ └── CfgAppSps.ps1
├── pdc/ ← REFERENCE EXAMPLE — not for production
├── pull/ ← REFERENCE EXAMPLE — not for production
└── sql/ ← REFERENCE EXAMPLE — not for production
A single serviceAccounts array. Each entry has a stable Name (used as
the variable name when the loader materialises a PSCredential) and an
optional IsAdAccount flag:
| Key | Required | Description |
|---|---|---|
Name |
yes | Stable identifier, used as the PSCredential variable name in the configuration scripts. |
DisplayName |
yes | Human-readable label, surfaced in logs. |
Description |
yes | Free-form description. |
Username |
yes |
DOMAIN\sam for AD accounts; any non-empty value for non-AD containers (the loader requires it). |
Password |
yes | Plain-text password. Replace with your own secret-management hook before checking the file in. |
IsAdAccount |
no | Default $True. Set to $False for containers that aren't real AD accounts (PFX passwords, DSRM, passphrase). |
Example:
@{
serviceAccounts = @(
@{ # AD account — IsAdAccount omitted, defaults to $True
Name = 'SETUP'
DisplayName = 'SharePoint SETUP'
Description = 'SharePoint Setup Account'
Username = 'CONTOSO\svcspssetup'
Password = '************'
}
@{ # Non-AD container — used as a PFX password
Name = 'SharePointCert'
DisplayName = 'SharePoint cert PFX'
Description = 'PFX password for the SharePoint web-app certificate'
Username = 'SharePointCert'
IsAdAccount = $False
Password = '************'
}
)
}Caution
Never commit real passwords to source control. The bundled Secrets.psd1
uses '******************' placeholders. Replace it with a wrapper that
pulls from your secret store (Azure Key Vault, CyberArk, etc.) before the
file is read in any environment.
-
IsAdAccount -ne $falseis the filter that selects AD accounts. Because$null -ne $falseis$true, omitting the key includes the entry, which is the desired default for AD accounts. - Containers (
ADSETUP,ADSAFEMODE,DscPullCert,SharePointCert,OfficeOnlineCert,SQLServerCert, ...) must setIsAdAccount = $Falseto be excluded from$spManagedAccountsand similar AD-only collections.
Certificate Name values in CfgAppSps.psd1 are intentionally chosen to
match Secrets entries 1:1 (with a Cert suffix to make the intent
obvious). This lets the configuration scripts resolve each cert's PFX
password directly:
CertificatePassword = (Get-Variable -Name $spCertificate.Name -ValueOnly)A missing or misnamed Secrets entry fails fast at compile time.
The file is a single hashtable with two top-level keys: AllNodes and
NonNodeData.
Per-node settings. The first entry must be the wildcard '*' node that
declares the DSC plain-text/domain-user policy:
AllNodes = @(
@{
NodeName = '*'
PSDscAllowPlainTextPassword = $True
PSDscAllowDomainUser = $True
}
@{
NodeName = 'APP1'
IsMaster = $true
IsSPSServer = $True
SPVersion = 'SE'
SPServerRole = 'Application' # ApplicationWithSearch | DistributedCache | Search | …
LocalAdmins = @('CONTOSO\svcspssetup', 'CONTOSO\svcspsfarm')
}
# WFE1, SCH1, OOS1 …
)Node-selector flags:
| Flag | Used by |
|---|---|
IsSPSServer |
All SharePoint Node blocks. |
IsMaster |
Designates the farm-creator (vs. join-farm) node. |
IsOOSServer, IsOOSSingle
|
Office Online Server Node block. |
SPServerRole |
SPFarm.ServerRole (MinRole). |
IsSrcAdmin, IsSrcCrawl, IsCntProc, IsSrcAnalyt, IsSrcQuery, IsIndexPart
|
Search topology component placement. |
LocalAdmins |
Group BUILTIN\Administrators membership. |
Note
The non-master node selector uses -not $_.IsMaster, so you can omit
IsMaster on non-master nodes — missing key and explicit $false
both work.
Flat container of farm-wide settings:
NonNodeData = @{
# The share host is defined ONCE here. Host it on a MEMBER server (e.g. the
# pull server), never on a domain controller (a node already holds a machine
# session to the DC, and Windows refuses a second identity to the same server,
# so a share on the DC fails at apply with "Access is denied").
SourcePath = '\\PULL\Softwarepackages' # binaries + .cer/.pfx share
DomainName = 'contoso.com'
Drives = @{ Data = 'F:'; Logs = 'G:' }
ADC = @{
# Each entry carries only the .cer / .pfx FILE NAME; the full path is
# derived by the Cfg*.ps1 script as SourcePath + CerFileName / PfxFileName.
# (An explicit CertPath / PfxPath is still honoured for backward compat.)
certificates = @(
@{ Name = 'SharePointCert' ; FriendlyName = 'SharePoint' ; CerFileName = 'SharePoint.cer' ; PfxFileName = 'SharePoint.pfx' }
@{ Name = 'OfficeOnlineCert' ; FriendlyName = 'OOSCertSSL' ; CerFileName = 'OfficeOnline.cer'; PfxFileName = 'OfficeOnline.pfx' }
@{ Name = 'SQLServerCert' ; FriendlyName = 'SQLCertSSL' ; CerFileName = 'SQLServer.cer' ; PfxFileName = 'SQLServer.pfx' }
@{ Name = 'DscPullCert' ; FriendlyName = 'DSCPull' ; CerFileName = 'DscPull.cer' ; PfxFileName = 'DscPull.pfx' }
)
}
SQLAlias = @(
@{ Name = 'ADMIN' ; ServerAlias = 'SINGLE-ADM-SPSSQL' ; ServerName = 'SQL1' ; InstanceName = 'MSSQLSERVER' ; Port = 1433 }
@{ Name = 'SEARCH' ; … }
@{ Name = 'SERVICES'; … }
@{ Name = 'CONTENT' ; … }
)
IIS = @{ LogFolder = 'LOGS\IIS' ; httpErrFolder = 'LOGS\HTTPERR' }
OOS = @{ AllServers = @('OOS') ; URL = 'oosweb.contoso.com' ; … }
SharePoint = @{
ProductKey = '…'
FarmConfigDatabaseName = 'DSPS_Admin_Config'
CentralAdministrationPort = '5555'
ServiceAppPoolName = 'SharePointServiceApplications'
WebApplications = @(
@{
Name = 'SharePoint'
Url = 'https://sharepoint.contoso.com'
HostHeader = 'sharepoint.contoso.com'
Port = 443
CertName = 'SharePointCert' # ← indirection into ADC.certificates / Secrets
ContentDBName = 'DSPS_CONTENT_MySite'
ApplicationPool = 'PAMYSITE'
Path = 'C:\InetPub\…\SharePoint'
ManagedPath = @( @{ Name = 'Search' ; Explicit = $true ; RelativeUrl = 'search' } )
Sites = @(
@{ Name = 'MySiteHost' ; Url = 'https://sharepoint.contoso.com' ; Template = 'SPSMSITEHOST#0' ; Language = 1033 ; ContentDatabase = 'DSPS_CONTENT_MySite' }
@{ Name = 'SearchCenter' ; Url = 'https://sharepoint.contoso.com/search' ; Template = 'SRCHCEN#0' ; Language = 1033 ; ContentDatabase = 'DSPS_CONTENT_SearchCenter' }
@{ Name = 'AppsCatalog' ; Url = 'https://sharepoint.contoso.com/sites/apps' ; Template = 'APPCATALOG#0' ; Language = 1033 ; ContentDatabase = 'DSPS_CONTENT_AppsCatalog' }
)
}
)
Services = @{
SearchService = @{ Name = 'SVCSearchService' ; DatabaseName = 'DSPS_SCH_SearchService' ; SearchCenterUrl = 'https://sharepoint.contoso.com/search' ; ContentSources = @{ … } }
UserProfile = @{ … }
ManagedMetadataService = @{ … }
AppManagementService = @{ … }
SubscriptionSettingsService = @{ … }
StateService = @{ … }
SessionState = @{ … }
UsageService = @{ … }
}
}
}The full reference file lives at
scripts/sps/CfgAppSps.psd1
and is the canonical schema — treat the snippet above as a map.
-
Cert names end in
Certand match Secrets entries 1:1. -
WebApplications carry a
CertNamefield so a friendlyNamecan be used in MOF identifiers while the cert lookup still resolves through Secrets. -
SourcePathis the SMB share that hosts every binary, language pack, CU,.cer, and.pfxreferenced by the kit. Configure share-level read for the setup account on every node. -
Drives.Data/Drives.Logsprefix every per-node path so each farm role can point at different physical drives without editing the script. -
Per-product path overrides (optional) — if your customer's file hierarchy doesn't match the kit's default (
<SourcePath>\SPS→<Drives.Data>\SoftwarePackages\SPS\{BIN,LP,CU}and the same shape for OOS), override any subset of the following keys inNonNodeData.SharePointand / orNonNodeData.OOS— defaults preserve the original layout:Key Purpose Default SourcePathNetwork share holding the product's setup files. <NonNodeData.SourcePath>\<SPS|OOS>DestinationPathLocal folder the setup files are copied to. <Drives.Data>\SoftwarePackages\<SPS|OOS>Subfolders.BinariesSub-folder of DestinationPathholding the main installer (setup.exe/ prereq).BINSubfolders.LanguagePackSub-folder of DestinationPathholding per-locale Language Pack installers.LPSubfolders.CumulativeUpdateSub-folder of DestinationPathholding the CU package(s).CUExisting absolute values for
SharePoint.UberCumulativeUpdateandOOS.CUFileNamekeep working unchanged: any rooted path is used as-is, otherwise the value is resolved under<DestinationPath>\<Subfolders.CumulativeUpdate>. -
SharePoint.LanguagePacks(optional) — array of SharePoint locale codes (e.g.@('fr-fr','es-es')). Each entry must match a sub-folder under<DestinationPath>\<Subfolders.LanguagePack>\that contains the correspondingsetup.exe. Omit or leave as@()to skip Language Pack installation entirely. -
SharePoint.ManagedAccounts(optional) — allowlist ofSecrets.psd1account names that SharePoint should register asSPManagedAccountresources on the farm master. Defaults to@('FARM', 'IISAPP', 'SEARCH'). Extend the list when you introduce a new dedicated SharePoint service account; leave it untouched to keep the historical set. Anything not in the allowlist (PULLSETUP,IISPULLAPP, SQL / OOS / monitoring accounts …) is intentionally ignored and stays out of the SPS MOF. -
SQL Server path overrides (optional) —
scripts/sql/CfgAppSql.psd1exposes the sameSourcePath/DestinationPathkeys underNonNodeData.SQL. Defaults are<NonNodeData.SourcePath>\SQLand<Drives.Data>\SoftwarePackages\SQL. SQL doesn't useSubfolders—setup.exeis expected at the root ofDestinationPath.
scripts/pdc/CfgAppPdc.psd1, scripts/pull/CfgAppPull.psd1, and
scripts/sql/CfgAppSql.psd1 follow the same AllNodes + NonNodeData
pattern and are meant to be read alongside their .ps1 counterparts.
Use them to see how the loader, the certificate import pattern, and the manifest-driven approach work end-to-end — then replace them with your organisation's production AD, pull-server, and SQL provisioning.
The manifest read by Initialize-SoftwarePackages.ps1. It describes the
local file-share repository that backs \\PULL\SoftwarePackages (or
whichever SMB share every node downloads binaries from), and the list of
packages the script should fetch into it.
@{
# Root folder of the local SoftwarePackages repository. Every entry
# below uses a Path RELATIVE to this root.
Repository = 'F:\SoftwarePackages'
SoftwarePackages = @(
@{
Name = 'SharePointServerSE'
Description = 'SharePoint Server Subscription Edition'
FileName = 'OfficeServer.iso'
Url = 'https://download.microsoft.com/.../OfficeServer.iso'
Extract = $true # mount and copy ISO contents
Path = 'SPS\BIN' # relative to Repository
}
@{
Name = 'KB5002773'
Description = 'SharePoint Server SE Cumulative Update'
FileName = 'uber-subscription-kb5002773-fullfile-x64-glb.exe'
Url = 'https://download.microsoft.com/.../uber-subscription-kb5002773-fullfile-x64-glb.exe'
Extract = $false # direct download to Path
Path = 'SPS\CU'
}
# …SQL Server 2022 + CU, Language Packs, .NET 4.8, VC++ 2015-2019,
# Office Online Server CU…
)
}| Key | Required | Description |
|---|---|---|
Repository |
yes | Local root folder backing the SMB share. Every package Path is resolved against it. |
Name |
yes | Identifier used in log output. |
Description |
yes | Human-readable label, surfaced in the per-package banner. |
FileName |
yes | Source file name; for Extract = $true packages, this is the temp-file name in %TEMP% before mounting. |
Url |
yes | Download URL (HTTPS). |
Extract |
yes |
$true to mount the ISO with Mount-DiskImage and copy its contents into Path; $false to download the file directly to Path. |
Path |
yes | Folder under Repository where the file or extracted contents land (e.g. SPS\BIN, SQL\CU, SPS\LP\FR-fr). |
Marker |
no | Sentinel file used to short-circuit re-extraction. Defaults to setup.exe, which is correct for the SQL Server, SharePoint, and LP ISOs. |
- For
Extract = $trueentries, the script skips the package when<Path>\<Marker>already exists. - For
Extract = $falseentries, the script skips the package when<Path>\<FileName>already exists. - Downloaded ISOs cached in
%TEMP%are reused when a previous run was interrupted between download and extraction.
ISO expansion uses Windows' built-in Mount-DiskImage /
Copy-Item -Recurse / Dismount-DiskImage pipeline, so the file-share
host has zero external dependencies beyond Windows itself. Required
capability: Windows 8 / Server 2012 or newer (for Mount-DiskImage).
Note
The script also detects outbound internet access once at startup and skips the download phase entirely on offline hosts. Per-package failures are caught and logged so one bad URL does not abort the whole run.
Before compiling any MOF, run the bundled Pester v5 pre-flight suite
against your .psd1 files. It catches the mistakes that would otherwise
surface mid-run on the customer site — placeholder product key,
duplicate NodeName, missing Secrets.psd1 entry for a managed account,
mismatched CertName ↔ ADC.certificates, malformed Language Pack
locale, unreachable \\share\setup.exe, missing .pfx …
# Full validation (workstation must have the install share mounted).
.\scripts\test\Invoke-ConfigDataTest.ps1 -ConfigPath .\scripts\sps\CfgAppSps.psd1
# Structure-only (skip every Test-Path on the install share / certificates).
.\scripts\test\Invoke-ConfigDataTest.ps1 -ConfigPath .\scripts\sql\CfgAppSql.psd1 -SkipFilesystemThe driver exits with code 1 on the first failure, so it can be wired
into CI or used as a hard gate before Start-DscConfiguration:
.\scripts\test\Invoke-ConfigDataTest.ps1 -ConfigPath .\scripts\sps\CfgAppSps.psd1
if ($LASTEXITCODE -ne 0) { throw 'Config invalid — refusing to compile MOFs.' }Every check is read-only. Sections that don't apply to the file under test
are skipped automatically (e.g. SharePoint checks are not run against
CfgAppSql.psd1).
-
File integrity —
Import-PowerShellDataFilesucceeds;AllNodes/NonNodeDatapresent; the wildcard*node declaresPSDscAllowPlainTextPassword/PSDscAllowDomainUser. -
AllNodes —
NodeNamevalues unique; exactly oneIsMasterper role family (SPS, OOS); at least oneIsSPSServer/IsOOSServer/IsSQLServerwhen the matchingNonNodeDatasection is present. -
Drives &
SourcePath—Drives.Data/Drives.Logsmatch^[A-Z]:$;NonNodeData.SourcePathis a UNC or rooted local path. -
Product key —
SharePoint.ProductKeymatchesXXXXX-XXXXX-XXXXX-XXXXX-XXXXXand is not the placeholderXXXXX-.... -
Ports —
CentralAdministrationPortandSQLAlias[*].Portare valid integers in1..65535. -
Certificates —
ADC.certificates[*].Nameunique; every cert has a matchingSecrets.psd1entry (drives the PFX password);WebApplications[*].CertNameresolves to a known certName;OOS.CertFriendlyNameresolves to a knownFriendlyName. -
Managed accounts —
SharePoint.ManagedAccounts(or the default@('FARM','IISAPP','SEARCH')when omitted) is a subset ofSecrets.psd1. -
Language packs — each
SharePoint.LanguagePacksentry matchesxx-xx. -
Filesystem reachability (skipped by
-SkipFilesystem) —SourcePath,BIN\setup.exe,LP\<locale>\setup.exe, and the CU package for both SPS and OOS; SQLSourcePath\setup.exe; everyADC.certificates[*].CertPath/PfxPath.
Filesystem checks reuse the exact same Resolve-ProductPaths resolution
as CfgAppSps.ps1, so the per-product SourcePath / Subfolders
overrides documented above are validated against the same paths the MOF
will compile with.
Pester v5 prefixes each It line with a status glyph, and the wrapper
adds a one-line summary that also surfaces the number of skipped tests
so a clean run never looks misleadingly green:
| Glyph | Meaning |
|---|---|
[+] |
Passed |
[-] |
Failed — fix before compiling MOFs |
[!] |
Skipped — the section / context didn't apply (e.g. SPS checks against CfgAppSql.psd1, or filesystem checks under -SkipFilesystem). Not a failure.
|
Tests Passed: 59, Failed: 0, Skipped: 3, Inconclusive: 0, NotRun: 0
59 of 62 test(s) passed; 3 skipped (sections not present in this config
or filesystem checks disabled). ConfigurationData looks healthy.
A run with Failed: 0 is healthy regardless of the skipped count;
sections gate themselves off when the relevant NonNodeData sub-tree is
missing. The wrapper still returns exit code 0 so CI / pre-MOF gates
work the same way.
The suite requires Pester 5.0.0 or later on the authoring host (Windows PowerShell 5.1 ships with Pester 3.x by default):
Install-Module Pester -MinimumVersion 5.0.0 -Force -SkipPublisherCheckNo other module is needed; the test files use only built-in cmdlets and Pester v5.
Continue with the Usage page.