CI/CD with PowerShell
Prerequisites: Building production modules
PowerShell runs on every CI system — and is the default shell on Windows runners. Use it both to build your PowerShell code and to script pipelines for anything else.
Testing a module in GitHub Actions
# .github/workflows/ci.yml
name: CI
on: [push, pull_request]
jobs:
test:
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- name: Lint
shell: pwsh
run: |
Install-Module PSScriptAnalyzer -Force
$issues = Invoke-ScriptAnalyzer -Path . -Recurse -Severity Warning, Error
$issues | Format-Table
if ($issues) { exit 1 }
- name: Test
shell: pwsh
run: |
Install-Module Pester -Force -SkipPublisherCheck
$config = New-PesterConfiguration
$config.Run.Exit = $true
$config.CodeCoverage.Enabled = $true
$config.TestResult.Enabled = $true
Invoke-Pester -Configuration $config
- The matrix runs the same tests on all three operating systems.
shell: pwshruns the step with PowerShell 7.$config.Run.Exit = $truemakes Pester exit with a non-zero code on failure, failing the build.- Coverage and test-result files can be uploaded as artifacts or published to the pull request.
Making scripts CI-friendly
Set-Content build.ps1 @'
[CmdletBinding()]
param([ValidateSet('Test', 'Package')][string]$Task = 'Test')
$ErrorActionPreference = 'Stop'
switch ($Task) {
'Test' { 'Running tests...'; $failed = 0 }
'Package' { 'Packaging...'; $failed = 0 }
}
if ($env:CI) { "::notice::$Task finished" }
exit $failed
'@
./build.ps1 -Task Test
"exit: $LASTEXITCODE"
$env:CI = 'true'
./build.ps1 -Task Package
Running tests...
exit: 0
Packaging...
::notice::Package finished
- One
build.ps1entry point runs the same way locally and in CI. - Exit codes tell the CI system whether the step passed.
- Most CI systems set
CI=true; GitHub Actions turns::notice::,::warning::, and::error::lines into annotations. - Build tools like Invoke-Build and psake organize larger builds into tasks with dependencies.
Azure DevOps
Azure DevOps pipelines run PowerShell with the pwsh: | step (or the
PowerShell@2 task with pwsh: true), and the AzurePowerShell task
signs in to Azure for you using a service connection.