Skip to main content

psake Cheat Sheet

Quick reference for common psake tasks, patterns, and commands.

Installation & Setup

# Install psake from PowerShell Gallery
Install-Module -Name psake -Scope CurrentUser

# Install globally (requires admin)
Install-Module -Name psake -Scope AllUsers

# Update to latest version
Update-Module -Name psake

# Check installed version
Get-Module psake -ListAvailable | Select-Object Version

# Import module
Import-Module psake

Basic Invocation

# Run default task from psakefile.ps1
Invoke-psake

# Run specific task
Invoke-psake -taskList Build

# Run multiple tasks
Invoke-psake -taskList Clean, Build, Test

# Use custom build file
Invoke-psake -buildFile ./custom-build.ps1

# Pass parameters
Invoke-psake -parameters @{ Configuration = 'Release'; Version = '1.0.0' }

# Show available tasks
Invoke-psake -docs

# Show detailed task information
Invoke-psake -detaileddocs

# Verbose output
Invoke-psake -Verbose

Task Definition Patterns

Basic Task

Task TaskName {
Write-Host "Doing work..."
}

Task with Dependencies

Task Build -depends Clean, Restore {
exec { dotnet build }
}

Task with Description

Task Deploy -description "Deploy application to production" {
exec { ./deploy.ps1 }
}

Conditional Task (Precondition)

Task DeployProd -precondition { $Environment -eq 'Production' } {
exec { ./deploy.ps1 }
}

Task with Validation (Postcondition)

Task Build -postcondition { Test-Path './build/app.dll' } {
exec { dotnet build -o ./build }
}

Task with Required Properties

Task Deploy -requiredVariables 'Environment', 'Version' {
exec { ./deploy.ps1 -Env $Environment -Ver $Version }
}

Continuous Task (Watches for Changes)

Task Watch -continueOnError {
while ($true) {
exec { dotnet watch run }
Start-Sleep -Seconds 1
}
}

Property Patterns

Basic Properties

Properties {
$Configuration = 'Release'
$OutputPath = './build'
$Version = '1.0.0'
}

Properties from Script Location

Properties {
$BuildScriptDir = Split-Path $psake.build_script_file
$ProjectRoot = Split-Path $BuildScriptDir
$SrcDir = Join-Path $ProjectRoot 'src'
}

Properties with Environment Variables

Properties {
$ApiKey = $env:API_KEY
$BuildNumber = $env:BUILD_NUMBER ?? '0'
$Environment = $env:DEPLOY_ENV ?? 'Development'
}

Properties with Fallback Values

Properties {
# Use environment variable or default
$Configuration = if ($env:BUILD_CONFIG) { $env:BUILD_CONFIG } else { 'Debug' }

# PowerShell 7+ null-coalescing
$Version = $env:VERSION ?? '1.0.0'
}

Computed Properties

Properties {
$Configuration = 'Release'
}

Properties {
# Computed from other properties
$OutputPath = "./build/$Configuration"
$PackageName = "myapp-$Version.zip"
}

Common Task Patterns

Clean Task

Task Clean {
if (Test-Path $OutputPath) {
Remove-Item $OutputPath -Recurse -Force
}
New-Item -ItemType Directory -Path $OutputPath -Force | Out-Null
}

Restore/Install Dependencies

# .NET
Task Restore {
exec { dotnet restore }
}

# Node.js
Task Install {
exec { npm install }
}

# Python
Task InstallDeps {
exec { pip install -r requirements.txt }
}

Build Tasks

# .NET Build
Task Build -depends Restore {
exec { dotnet build --configuration $Configuration --no-restore }
}

# Node.js Build
Task Build -depends Install {
exec { npm run build }
}

# Go Build
Task Build {
exec { go build -o ./build/app }
}

Test Tasks

# .NET Tests
Task Test -depends Build {
exec { dotnet test --no-build --no-restore }
}

# With Coverage
Task TestCoverage -depends Build {
exec { dotnet test --collect:"XPlat Code Coverage" }
}

# Node.js Tests
Task Test {
exec { npm test }
}

Package/Publish Tasks

# Create NuGet Package
Task Pack -depends Build {
exec { dotnet pack --no-build --output $OutputPath }
}

# Create Zip Archive
Task Package -depends Build {
Compress-Archive -Path "$OutputPath/*" -DestinationPath "./dist/app.zip"
}

# Publish to Folder
Task Publish -depends Build {
exec { dotnet publish --configuration $Configuration --output ./publish }
}

Exec Function Patterns

Basic Execution

Task Build {
exec { dotnet build }
}

Custom Error Message

Task Build {
exec { dotnet build } -errorMessage "Build failed with error"
}

Continue on Error

Task Test {
exec { dotnet test } -continueOnError
}

With Retries

Task Deploy {
exec { ./deploy.ps1 } -maxRetries 3
}

With Retry Pattern Matching

Task Deploy {
exec { ./deploy.ps1 } `
-maxRetries 3 `
-retryTriggerErrorPattern "timeout|connection"
}

Working Directory

Task BuildSubproject {
Push-Location ./subproject
try {
exec { dotnet build }
}
finally {
Pop-Location
}
}

Assert Patterns

File Existence

Task Validate {
Assert (Test-Path './src') "Source directory not found"
Assert (Test-Path './config.json') "Configuration file missing"
}

Variable Validation

Task Deploy {
Assert (![string]::IsNullOrEmpty($ApiKey)) "API_KEY is required"
Assert ($Version -match '^\d+\.\d+\.\d+$') "Invalid version format"
}

Condition Checks

Task DeployProd {
Assert ($Environment -eq 'Production') "This task only runs in Production"
exec { ./deploy.ps1 }
}

Include Patterns

Include Shared Tasks

# In psakefile.ps1
Include ./build/shared-tasks.ps1
Include ./build/deploy-tasks.ps1

Task Default -depends SharedClean, Build, Deploy

Relative Includes

Properties {
$BuildDir = Split-Path $psake.build_script_file
}

Include (Join-Path $BuildDir 'tasks/common.ps1')

Format Task Name Patterns

Simple Format

FormatTaskName {
param($taskName)
Write-Host "===== $taskName =====" -ForegroundColor Cyan
}

Detailed Format

FormatTaskName {
param($taskName)
$line = "-" * 70
Write-Host $line -ForegroundColor Blue
Write-Host " Task: $taskName" -ForegroundColor Yellow
Write-Host $line -ForegroundColor Blue
}

Timestamp Format

FormatTaskName {
param($taskName)
$timestamp = Get-Date -Format "HH:mm:ss"
Write-Host "[$timestamp] Starting task: $taskName" -ForegroundColor Green
}

Parameter Passing

Via Parameters Hashtable

Invoke-psake -parameters @{
Configuration = 'Release'
Version = '2.0.0'
Environment = 'Production'
}

Via Properties (Alternative)

Invoke-psake -properties @{
Configuration = 'Release'
Version = '2.0.0'
}

Via Environment Variables

# Set in shell
$env:BUILD_CONFIG = 'Release'
$env:VERSION = '2.0.0'

# Read in build script
Properties {
$Configuration = $env:BUILD_CONFIG ?? 'Debug'
$Version = $env:VERSION ?? '1.0.0'
}

Invoke-psake

Nested Build Patterns

Sequential Subproject Builds

Task BuildAll {
Invoke-psake ./projects/ProjectA/psakefile.ps1 -taskList Build
Invoke-psake ./projects/ProjectB/psakefile.ps1 -taskList Build
}

Parallel Subproject Builds

Task BuildAll {
$jobs = @(
Start-Job { Invoke-psake ./projects/ProjectA/psakefile.ps1 -taskList Build }
Start-Job { Invoke-psake ./projects/ProjectB/psakefile.ps1 -taskList Build }
)

$jobs | Wait-Job | Receive-Job
$jobs | Remove-Job
}

Pass Parameters to Nested Build

Task BuildSubproject {
Invoke-psake ./subproject/psakefile.ps1 `
-taskList Build `
-parameters @{ Configuration = $Configuration }
}

Common Build Workflows

Standard .NET Workflow

Properties {
$Configuration = 'Release'
$OutputPath = './build'
}

Task Default -depends Test

Task Clean {
Remove-Item $OutputPath -Recurse -Force -ErrorAction SilentlyContinue
}

Task Restore {
exec { dotnet restore }
}

Task Build -depends Clean, Restore {
exec { dotnet build --configuration $Configuration --no-restore }
}

Task Test -depends Build {
exec { dotnet test --configuration $Configuration --no-build }
}

Task Pack -depends Test {
exec { dotnet pack --configuration $Configuration --no-build --output $OutputPath }
}

Standard Node.js Workflow

Properties {
$ProjectRoot = $PSScriptRoot
$DistDir = Join-Path $ProjectRoot 'dist'
}

Task Default -depends Test

Task Clean {
Remove-Item $DistDir -Recurse -Force -ErrorAction SilentlyContinue
}

Task Install {
exec { npm install }
}

Task Lint -depends Install {
exec { npm run lint }
}

Task Build -depends Install, Lint {
exec { npm run build }
}

Task Test -depends Build {
exec { npm test }
}

Task Package -depends Test {
exec { npm pack --pack-destination $DistDir }
}

CI/CD Workflow

Properties {
$Configuration = 'Release'
$Version = $env:VERSION ?? '1.0.0'
$BuildNumber = $env:BUILD_NUMBER ?? '0'
}

Task CI -depends Clean, Restore, Build, Test, Pack, Publish

Task Clean {
Remove-Item ./build -Recurse -Force -ErrorAction SilentlyContinue
}

Task Restore {
exec { dotnet restore }
}

Task Build -depends Restore {
exec { dotnet build --configuration $Configuration --no-restore }
}

Task Test -depends Build {
exec { dotnet test --configuration $Configuration --no-build --logger trx }
}

Task Pack -depends Test {
exec { dotnet pack --configuration $Configuration --no-build -p:Version=$Version }
}

Task Publish -depends Pack {
exec { dotnet nuget push "./bin/Release/*.nupkg" --api-key $env:NUGET_API_KEY }
}

Error Handling Patterns

Try-Catch in Tasks

Task Deploy {
try {
exec { ./deploy.ps1 }
Write-Host "Deployment successful" -ForegroundColor Green
}
catch {
Write-Host "Deployment failed: $_" -ForegroundColor Red
throw
}
}

Graceful Failure

Task OptionalTask {
try {
exec { ./optional-operation.ps1 }
}
catch {
Write-Warning "Optional operation failed: $_"
# Continue build
}
}

Cleanup on Failure

Task Deploy {
try {
exec { ./deploy.ps1 }
}
catch {
Write-Host "Deployment failed, rolling back..." -ForegroundColor Yellow
exec { ./rollback.ps1 }
throw
}
}

Useful One-Liners

# Run psake from any directory with specific build file
Invoke-psake -buildFile C:\projects\myapp\psakefile.ps1

# Run with .NET Framework 4.8
Invoke-psake -framework '4.8'

# List all tasks without running
Invoke-psake -docs

# Run and show task execution time
Invoke-psake -Verbose

# Run specific task in specific file
Invoke-psake -buildFile ./psakefile.ps1 -taskList Deploy -parameters @{Environment='Prod'}

# Check if psake is installed
Get-Module psake -ListAvailable

# Get psake commands
Get-Command -Module psake

# View psake help
Get-Help Invoke-psake -Full

# Run psake and capture output
$result = Invoke-psake -taskList Build 2>&1

# Run psake with specific error action
Invoke-psake -ErrorAction Stop

Environment-Specific Patterns

Multi-Environment Configuration

Properties {
$Environment = $env:DEPLOY_ENV ?? 'Development'

$Config = @{
Development = @{
ApiUrl = 'http://localhost:5000'
DbConnection = 'Server=localhost;Database=DevDB'
}
Staging = @{
ApiUrl = 'https://staging.example.com'
DbConnection = 'Server=staging-db;Database=StagingDB'
}
Production = @{
ApiUrl = 'https://api.example.com'
DbConnection = 'Server=prod-db;Database=ProdDB'
}
}

$CurrentConfig = $Config[$Environment]
}

Task Deploy {
Write-Host "Deploying to $Environment" -ForegroundColor Cyan
Write-Host " API URL: $($CurrentConfig.ApiUrl)" -ForegroundColor Gray

exec { ./deploy.ps1 -Environment $Environment -ApiUrl $CurrentConfig.ApiUrl }
}

Environment-Specific Tasks

Task DeployDev -precondition { $Environment -eq 'Development' } {
exec { ./deploy-dev.ps1 }
}

Task DeployStaging -precondition { $Environment -eq 'Staging' } {
exec { ./deploy-staging.ps1 }
}

Task DeployProd -precondition { $Environment -eq 'Production' } {
Assert ($Version -ne 'dev') "Cannot deploy dev version to production"
exec { ./deploy-production.ps1 }
}

Task Deploy -depends DeployDev, DeployStaging, DeployProd {
# At least one will run based on preconditions
}

Docker Build Patterns

Build Docker Image

Properties {
$ImageName = 'myapp'
$ImageTag = $env:VERSION ?? 'latest'
}

Task DockerBuild {
$fullTag = "${ImageName}:${ImageTag}"
exec { docker build -t $fullTag . }
}

Task DockerPush -depends DockerBuild {
$fullTag = "${ImageName}:${ImageTag}"
exec { docker push $fullTag }
}

Multi-Stage Docker Build

Task DockerBuildDev {
exec { docker build --target development -t myapp:dev . }
}

Task DockerBuildProd {
exec { docker build --target production -t myapp:prod . }
}

Debugging Tips

# Add breakpoints
Set-PSBreakpoint -Script .\psakefile.ps1 -Line 42

# Run with verbose output
Invoke-psake -Verbose

# Add debug output in tasks
Task Build {
Write-Host "Configuration: $Configuration" -ForegroundColor Yellow
Write-Host "OutputPath: $OutputPath" -ForegroundColor Yellow
exec { dotnet build }
}

# Check psake context
Task Debug {
Write-Host "Build script: $($psake.build_script_file)"
Write-Host "Version: $($psake.version)"
Write-Host "Context: $($psake.context | Out-String)"
}

# Test task execution order without running tasks
Invoke-psake -docs # Shows task dependencies

See Also