Authoring and validating templates

modusops-templates ships starter templates for the common cases, and the modusOps tooling vendors, pins, updates, and tests them for you - see The library model. But the starters are a starting point, not a ceiling: authoring your own templates is encouraged wherever your environment needs something the library doesn’t cover. This page is how you write one well.

Templates you contribute back to the library are released as versioned assets, and two automated gates keep the library trustworthy.

Keep templates thin

A template orchestrates; it does not contain business logic. Real logic belongs in versioned PowerShell modules pulled at runtime (for example ModusOps.Toolkit), not in the YAML. A good template is a small, declarative wrapper around a module call.

PR validation

Every pull request lints all distributed templates. Gating findings block the merge; advisory ones are posted as a comment:

Class Gate? Examples
Structure yes must parse as YAML, no tab indentation
Parameters (azd) yes default not in values, undeclared parameters.X, missing/duplicate name
GH action (gh) yes literal $ in a manifest, runs.using not composite, undeclared inputs.X
Secrets yes a known credential format (token prefix, JWT, PEM, AccountKey=) - format-based, so GUIDs are not flagged
Hints / style / spacing no (advisory) unused params, missing types, duplicate display names, trailing whitespace

The $ gate exists because GitHub evaluates that expression everywhere in an action manifest - including description: and run: text - so a literal reference breaks the action before any step runs. Tokens must arrive via an input the caller passes secrets.* into.

Release

On merge, the next rolling-integer tag (v1, v2, v3, …) is cut and the full asset set is attached to the release (azd.<name>.yml and gh.<name>.zip for pipeline templates, the repoScaffold workflow/markdown assets, plus manifest.json and a checksums.txt). Release notes list only what actually changed since the previous tag, so an unchanged template never looks modified even though the library version advanced. The same vN tag is the immutable ref GitHub consumers pin (...@vN) - one tag scheme, not a separate SemVer line. (modusOps modules stay SemVer’d; the template library is a curated set, so one monotonic coordinate is simpler.)

The manifest is authoritative

The tooling resolves a template name to its asset through manifest.json, never by parsing filenames. Each entry lists the template’s platforms and per-platform asset names - which is how one name can map to both an azd.<name>.yml and a gh.<name>.zip - plus a category (pipeline or repoScaffold), the per-platform kind, and (for repoScaffold) a fixed dest. A top-level sets block declares the archetypes that Add-MORepoScaffold applies.