Orpheus Systems Architecture

6. Idempotent PnP PowerShell provisioning instead of Feature Framework

6. Idempotent PnP PowerShell provisioning instead of Feature Framework

Date: 2026-10-22

Status

Accepted


Problem

The solutions assume three lists with content types and indexes, a RemainingDays column bound to the field customizer, the Orpheus Contract content type bound to the form customizer, a modern page hosting the calendar web part with the lists preselected, a starter holiday set, and application customizer properties carrying list and page URLs. Two mechanisms were checked.


Decision

We will provision with an idempotent PnP PowerShell script, Provision-OrpheusSite.ps1, wrapping a PnP site template (Invoke-PnPSiteTemplate, re-runnable). It runs from PowerShell 7 on the host, needs no Node, and takes -SiteUrl, -Country, -WhatIf. It provisions, in order: content types and the three lists with indexes; RemainingDays with the field customizer bound; the content type bound to the form customizer; ContractCalendar.aspx with the web part and lists preselected; starter holidays for the country; application customizer registration with the Contracts list and calendar page URLs.

Feature Framework (elements.xml inside the .sppkg) is not used for lists.

Provisioning steps of the idempotent script

flowchart TB
  RUN["Provision-<br/>OrpheusSite.ps1<br/>idempotent<br/>PowerShell 7.4 on the host<br/>no Node<br/>-SiteUrl -ClientId -WhatIf"] --> TPL["PnP site template<br/>Invoke-PnPSiteTemplate"]
  TPL --> S1["Lists<br/>content types,<br/>the three lists, indexes,<br/>list validation"]
  S1 --> S5["Starter data, optional<br/>-SeedDemoData -Country:<br/>contracts, entries, holidays"]
  S1 --> INS["-InstallSolutions<br/>OrpheusSolutions.psm1<br/>Invoke-OrpheusSolutionInstall"]
  S5 --> INS
  INS --> A1["1. Four .sppkg into the<br/>site collection App Catalog;<br/>empty manifest: clean<br/>reinstall, own -Confirm gate"]
  A1 --> A2["2. OrpheusChrome<br/>header custom action"]
  A2 --> A3["3. RemainingDays bound to<br/>the field customizer"]
  A3 --> A4["4. Orpheus Contract bound<br/>to the form customizer"]
  A4 --> A5["5. Orpheus Contract Managers<br/>group, Contribute"]
  A5 --> A6["6. SitePages/ContractCalendar.aspx<br/>web part wired to<br/>the three lists"]
  A6 -.->|"re-run: every step<br/>reports Unchanged"| RUN
  FF["Feature Framework<br/>elements.xml in .sppkg"] -.-x|"not used for lists<br/>or the header"| S1

Considered Options

Option Summary Pros Cons
✅ PnP PowerShell site template script Re-runnable script on the host Idempotent; can create page, place web part, bind customizers, write properties; -WhatIf Separate step after .sppkg deployment; needs PnP PowerShell on the host
Feature Framework in .sppkg elements.xml list and content-type definitions Ships with the solution Site-scoped; runs only on first install; cannot re-run to upgrade a schema; cannot create the page, place the web part or bind customizers
Manual setup Click through SharePoint UI No tooling Not repeatable; violates clean-clone goal in spirit
Site designs / site scripts Tenant-level JSON Native Limited actions; cannot bind customizers or place web part with properties as needed

Consequences

Positive:

Negative / Trade-offs:

Risks: | Risk ID | Description | Impact (E/H/M/L) | Mitigation | |---------|-------------|------------------|------------| | R1 | Script run before solutions are in the app catalog fails to bind customizers | M | Documented order in Phase F; -WhatIf | | R2 | Destructive schema changes not handled by re-run | M | Open item OR-09; treat column removals as manual steps |


Amendments

2026-10-23 — The decision stands; it is partly implemented.

Amendment (2026-10-23) — The decision stands; it is now fully implemented (Plan 3). The "Not built yet" bullet above no longer applies.