ArchitektApx/PowershellModuleTemplate

Starting point for new PowerShell modules, with build, test and publish wired up from the first commit

โ˜… 0Forks 0PowerShellGitHub โ†—Compare
modulebuilderpesterpowershellpowershell-gallerypowershell-modulepsscriptanalyzerpwshpwsh-moduletemplatetemplate-repository

README

๐Ÿงฐ PowerShell Module Template

A batteries-included starting point for PowerShell modules.

Build with ModuleBuilder, test with Pester, lint with PSScriptAnalyzer, and ship to the PowerShell Gallery from a git tag.

CI License PowerShell Platforms


โœจ Features

๐Ÿ—๏ธ ModuleBuilder Builds your module from source into a clean, versioned Dist output
๐Ÿงช Pester 5 Test runner wired to Tests/: behaviour tests run against the source tree (failures name a source file and line), artifact checks against the built module
๐Ÿ” PSScriptAnalyzer Style and correctness pass, plus a compatibility pass against your target hosts
๐Ÿ“Š Code coverage Per-file coverage report over the source tree with an optional minimum-percentage gate
๐ŸŽฏ Task runner One entry point (tasks.ps1) for every tool
๐Ÿค– GitHub Actions CI matrix across your target hosts, plus a tag-driven Gallery release
๐Ÿงฉ Platform presets PowerShell5.1, PowerShell7, or both. One key sets the manifest, the lint targets, and the CI matrix
๐Ÿช„ prepare task Renames and stamps the whole template from a single module.psd1
๐Ÿ” Hardening guide The GitHub rulesets and settings that make publishing to the Gallery safe
๐Ÿ“ Structured source Source/ layout with Enum, Classes, Private, and Public. Classes and enums have rules of their own: see Docs/CLASSES_AND_ENUMS.md

๐Ÿš€ Quick Start

1๏ธโƒฃ Create your repository

Use as a template (recommended)

Use this template > Create a new repository

Use_Template

Or clone it
git clone https://github.com/ArchitektApx/PowershellModuleTemplate
cd PowershellModuleTemplate

2๏ธโƒฃ Configure your module

Edit module.psd1 at the repository root:

Property Description
ModuleName Your module name (no spaces, must start with a letter)
ModuleDescription Short description of the module
ModuleTargetPlatform Which PowerShell hosts you target. See Target platforms
ModuleAuthor Your name or team
ModuleCompanyName Company or vendor. Also becomes the LICENSE copyright holder
ModuleProjectUri Your repository URL. Becomes ProjectUri/LicenseUri and the changelog compare links
ModuleRequiredModules Extra development-time dependencies, on top of the fixed base set
ModuleRequiredPowershellVersion Optional. Pin a higher minimum than the platform default; empty uses the default

Note

CompatiblePSEditions and PowerShellVersion are derived from ModuleTargetPlatform, so there is no separate key for them.

3๏ธโƒฃ Run the prepare task

./tasks.ps1 prepare
./tasks.ps1 prepare -Platform PowerShell7   # override the platform for this run

This renames Source/ModuleTemplate.ps[dm]1, generates a fresh GUID, stamps the manifest, build.psd1 and LICENSE, generates Tools/PSScriptAnalyzer.psd1 and the CI matrix for your target platform, renders a README.md and CHANGELOG.md for your module, deletes the template-only scaffolding (res/, Tools/templates/, Tools/platforms/), and installs the dev requirements.

Warning

prepare overwrites README.md, CHANGELOG.md, Tools/PSScriptAnalyzer.psd1 and .github/workflows/ci.yml. Run it on a fresh clone, before you have written anything of your own.

4๏ธโƒฃ Write and verify

Add one .ps1 per function under Source/Public (exported) or Source/Private (internal), then:

./tasks.ps1 build
./tasks.ps1 test
./tasks.ps1 lint

The built module lands in Dist/<ModuleName>/<ModuleVersion>. ๐ŸŽ‰


๐Ÿ–ฅ Target platforms

ModuleTargetPlatform selects one of the presets in Tools/platforms/:

Preset Manifest Lint targets CI hosts
๐ŸชŸ PowerShell5.1 Desktop, 5.1.0 5.1 win / WinPS 5.1
๐ŸŒ PowerShell7 Core, 7.0.0 7.0 win, linux, macos / pwsh 7
๐Ÿ”€ PowerShell5.1And7 Core, Desktop, 5.1.0 5.1 + 7.0 all four (default)

The lint step enforces the preset. A ternary or ?? in Source/ passes under PowerShell7 and fails under PowerShell5.1And7.

Tip

Retargeting after prepare means editing Tools/PSScriptAnalyzer.psd1 and the ci.yml matrix by hand, since Tools/platforms/ is gone by then.


๐Ÿ“‚ Project Structure

See ModuleBuilder for how the source tree is assembled into the built module.

.
โ”œโ”€โ”€ ๐Ÿ“„ module.psd1          # Module metadata consumed by the tooling
โ”œโ”€โ”€ ๐Ÿ“„ build.psd1           # ModuleBuilder build config (manifest path, output, SemVer)
โ”œโ”€โ”€ ๐ŸŽฏ tasks.ps1            # Task runner
โ”œโ”€โ”€ ๐Ÿ“ Source/
โ”‚   โ”œโ”€โ”€ ModuleTemplate.psd1   # Becomes <YourModuleName>.psd1 after prepare
โ”‚   โ”œโ”€โ”€ ModuleTemplate.psm1   # Becomes <YourModuleName>.psm1 after prepare
โ”‚   โ”œโ”€โ”€ Enum/
โ”‚   โ”œโ”€โ”€ Classes/
โ”‚   โ”œโ”€โ”€ Private/
โ”‚   โ””โ”€โ”€ Public/
โ”œโ”€โ”€ ๐Ÿงช Tests/
โ”‚   โ”œโ”€โ”€ _TestHelpers.ps1      # Target selection and import helpers; no module name hardcoded
โ”‚   โ”œโ”€โ”€ Harness.Tests.ps1     # Tests for the test harness itself
โ”‚   โ””โ”€โ”€ Module.Tests.ps1      # Artifact checks against the BUILT module, green on a fresh clone
โ”œโ”€โ”€ ๐Ÿ”ง Tools/
โ”‚   โ”œโ”€โ”€ platforms/          # Target-platform presets (removed by prepare)
โ”‚   โ”œโ”€โ”€ templates/          # Skeletons rendered by prepare (removed by prepare)
โ”‚   โ””โ”€โ”€ ...                 # See Tools/README.md
โ”œโ”€โ”€ ๐Ÿ” Docs/HARDENING.md    # GitHub settings to set before publishing (survives prepare)
โ”œโ”€โ”€ ๐Ÿ“š Docs/CLASSES_AND_ENUMS.md  # Class/enum rules and limits (removed by prepare)
โ”œโ”€โ”€ ๐Ÿค– .github/workflows/   # ci.yml (matrix from the platform) and release.yml (tag -> PSGallery)
โ””โ”€โ”€ ๐Ÿ“ฆ Dist/                # Build output (gitignored, created by build)

๐ŸŽ› Tasks Reference

./tasks.ps1 <TaskName>
Task Description
๐Ÿช„ prepare One-time template setup. Run once, after editing module.psd1. -Platform <name> overrides ModuleTargetPlatform.
๐Ÿงน cleanup One-time teardown. Deletes prepare.ps1 and itself and strips both tasks out of tasks.ps1. Run once the repo is prepared and hardened.
๐Ÿ“ฅ install_dev_requirements Installs ModuleBuilder, Configuration, Pester 5+, PSScriptAnalyzer, plus your extras. Once per host per PowerShell edition.
๐Ÿ—๏ธ build Clears Dist/, builds with ModuleBuilder into Dist/<ModuleName>/<ModuleVersion>.
๐Ÿงช test Builds, then runs the Pester suite. -Target Source (default) or Dist picks the tree the behaviour tests import; -Path <file-or-dir> picks which tests execute. Fails on an empty run.
๐Ÿ” lint PSScriptAnalyzer over Source/: style and correctness, then compatibility against your target platform.
๐Ÿ“Š coverage Per-file coverage report over the source tree. -MinimumPercent 90 to gate.
๐Ÿšข prepare_release ./tasks.ps1 prepare_release 1.1.0. Gates, promotes the changelog, stamps the version, rebuilds, verifies.

๐Ÿ“– Full tool reference: Tools/README.md


๐Ÿค– CI and Releases

ci.yml runs lint, build, and test on every push and pull request. Which hosts it runs on comes from your ModuleTargetPlatform; the template's own default is all four (Windows PowerShell 5.1, and pwsh 7 on Windows, Linux, and macOS).

release.yml fires on a vX.Y.Z tag. It requires the tagged commit to be on the default branch, requires the tag to match the built manifest version, runs the full CI matrix, then publishes to the PowerShell Gallery and creates a GitHub release whose body is that version's CHANGELOG.md section.

The release flow after prepare_release has stamped the version and the PR is merged:

git checkout master
git pull origin master
git tag v1.1.0
git push origin v1.1.0

The generated module README documents this in full. Pull first: the tag has to land on the merged commit, not on whatever you had locally.

Important

Publishing needs a repository secret PSGALLERY_API_KEY. The workflow also refuses to release while module.psd1 still names the module ModuleTemplate.


๐Ÿ” Hardening the repository

Caution

The release workflow publishes to the PowerShell Gallery, where a version number can never be reused and consumers install without reviewing what they get. That makes push access to your default branch equivalent to push access to everyone's machines.

Docs/HARDENING.md covers how to close that path:

๐Ÿ”‘ the publishing secret ย โ€ขย  ๐Ÿ›ก๏ธ a default-branch ruleset with an empty bypass list ย โ€ขย  ๐Ÿท๏ธ immutable release tags ย โ€ขย  ๐Ÿค– read-only Actions permissions ย โ€ขย  ๐Ÿ”Ž secret scanning with push protection ย โ€ขย  โœ๏ธ commit signing

Each step says what it does and why it matters, and most come with a gh command.

None of it is on by default, and none of it survives "Use this template". It is GitHub configuration rather than repository content, so you have to redo it per repo. Do it before the first tag.


๐Ÿ“ฆ Build Output

  • ๐Ÿ”ข Version is controlled in build.psd1 via the SemVer key, which prepare_release stamps for you.
  • ๐Ÿ“ค Copy Dist/<ModuleName>/<ModuleVersion> to a module path, or let the release workflow publish it.

๐Ÿ“œ License

MIT. See the LICENSE file in this repository.

Contributors

ArchitektApx

Issues