Getting Started with ModuleForge
ModuleForge scaffolds a production-ready PowerShell module structure - including CI/CD pipelines, Pester test stubs, and semantic versioning - so you can focus on writing functions rather than plumbing.
Prerequisites
Local tools
| Requirement | Minimum Version |
|---|---|
| PowerShell | 7.0+ |
| Git | Any recent version |
| Pester | 5.6+ |
| PSScriptAnalyzer | 1.24+ |
| Microsoft.PowerShell.PSResourceGet | 1.1.1+ |
A terminal and any text editor will do, but VSCode is assumed in the step-by-step tutorials.
Platform account (pick one)
| Platform | What you need |
|---|---|
| GitHub | A GitHub account and a repository (public or private) |
| Azure DevOps | An Azure DevOps organisation and project, with an Artifacts feed and build service permissions configured - see Azure DevOps Prerequisites |
Runner / agent setup
GitHub Actions - the scaffolded workflows use
ubuntu-latest(GitHub-hosted runners). No setup is required for most cases. If you need Windows-specific behaviour or a private network, configure a self-hosted runner and update theruns-on:value in the generated workflow files.Azure DevOps - the scaffolded pipelines also target
ubuntu-latest. If your organisation does not have Microsoft-hosted agents enabled, or you need a specific environment, you will need to provision a self-hosted agent and update thepool:in the generated pipeline YAML.
Install ModuleForge
# Bootstrap PSResourceGet first if you don't have it
Install-Module Microsoft.PowerShell.PSResourceGet -Force
# Install everything you need
Install-PSResource -Name Pester, PSScriptAnalyzer, ModuleForge
Scaffold a New Module
From your (already-initialised) git repository root:
# Create the folder structure and build config
New-MFProject -ModuleName 'MyModule' -Description 'What my module does'
Then add CI/CD workflows for your platform:
GitHub Actions
Add-MFGithubScaffold
Azure DevOps
Add-MFAzureDevOpsScaffold
Commit the scaffolding back to main before doing anything else - the CI workflows need to exist on the default branch before they can run.
chore: initialise ModuleForge project with scaffolding and workflows
What Gets Created
MyModule/
├── source/
│ ├── functions/ ← exported functions (.ps1 + .Tests.ps1)
│ ├── private/ ← internal helpers (not exported)
│ ├── classes/ ← PowerShell classes
│ ├── enums/ ← enum definitions
│ ├── validationClasses/ ← custom [ValidateScript] types
│ └── resource/ ← non-code files bundled with the module
├── moduleForgeConfig.xml ← build and publish configuration
└── .github/workflows/ ← CI/CD pipelines (GitHub) or azure-pipelines.yml
Each function gets its own file. Each function file gets its own FunctionName.Tests.ps1 alongside it - Pester picks these up automatically.
Development Workflow
flowchart TD
A([Start a new module]) --> B[Write your functions]
B --> C[Run tests locally\nInvoke-Pester]
C --> D[Commit and open a PR]
subgraph CI
D --> E[Pester + PSScriptAnalyzer\nrun automatically]
E --> F[Review and merge]
end
subgraph CD
F --> G[Trigger Build and Release\nvia GitHub Actions]
G --> H[Module published\nto Artifacts feed]
end
H --> B
The CI pipelines run automatically on every PR. The Build and Release step is triggered manually - you decide when a version is ready to ship and what kind of version bump it gets.
PSScriptAnalyzer runs on every PR as a soft gate - it reports findings but does not block a merge. The results are posted as a PR comment, grouped by severity: Error, Warning, and Information. Errors represent genuine problems (undefined variables, syntax issues); Warnings are style and best-practice concerns worth addressing; Information is advisory. The weighting is intentional - not everything flagged demands action, but the highest-severity items are surfaced first so you can prioritise. Treat the report as a code review prompt rather than a hard stop.
Writing Your First Function
Add a .ps1 file to source/functions/ and a matching .Tests.ps1 alongside it:
# source/functions/Get-Greeting.ps1
function Get-Greeting {
<#
.SYNOPSIS
Returns a greeting string.
.EXAMPLE
Get-Greeting -Name 'World'
#>
[CmdletBinding()]
param(
[string]$Name = 'World'
)
process {
"Hello, $Name!"
}
}
# source/functions/Get-Greeting.Tests.ps1
BeforeAll {
. $PSCommandPath.Replace('.Tests.ps1', '.ps1')
}
Describe 'Get-Greeting' {
It 'returns the default greeting' {
Get-Greeting | Should -Be 'Hello, World!'
}
It 'uses the provided name' {
Get-Greeting -Name 'PowerShell' | Should -Be 'Hello, PowerShell!'
}
}
Always run tests locally before committing. Two options:
Run a specific test file directly with Pester:
Invoke-Pester .\source\functions\Get-Greeting.Tests.ps1
Or run the full test suite with code coverage - mirrors exactly what the CI pipeline does:
.\scripts\Invoke-MFPester.ps1
Invoke-MFPester.ps1 is a standalone script (added to your project by Add-MFProjectScripts) that discovers all *.Tests.ps1 files under source/functions/ and source/private/ (plus a root tests/ folder if you have one), runs them with code coverage enabled, and reports the results. Use this before opening a PR to confirm you’re in the same state the automated pipeline will see. On Constrained Language Mode systems, add -SkipCodeCoverage - coverage instrumentation cannot run there.
Looking for a house style for the code inside your functions? The PowerShell Style Recommendations page collects a set of optional, non-binding conventions (quoting, collections, function shape, cross-platform habits) you can adopt as a starting point.
Commit with the right prefix so your changelog generates correctly:
feat: add Get-Greeting function
test: add Pester tests for Get-Greeting
Next Steps
| Goal | Where to go |
|---|---|
| Full walkthrough - GitHub setup | GitHub Getting Started Tutorial |
| Advanced features - enums, classes, private functions | AstroCmdlets: An Advanced Tutorial |
| Azure DevOps setup | Azure DevOps Tutorial |
| Choosing version and release options | Build and Release Guide |
| All commit prefixes and their effects | Commit Prefixes |
| Optional code style conventions | PowerShell Style Recommendations |
| Full function reference | Functions |