PowerShell Engineering
Use this skill for idiomatic, testable PowerShell that behaves deliberately on
its supported platforms. Use script-engineering
when deciding whether the behavior belongs in a script or whether PowerShell is
preferable to shell, Python, Ruby, or JavaScript.
Workflow
- Inspect repository instructions,
.ps1, .psm1, .psd1, module manifests,
required modules, Pester/PSScriptAnalyzer configuration, task runners, CI,
documentation, and supported host matrix.
- Establish the minimum PowerShell edition and version. Distinguish PowerShell
7+ (
pwsh) from Windows PowerShell (powershell.exe); support both only when
repository evidence requires both.
- Identify platform-specific commands, providers, paths, encodings, native
executables, permissions, and modules before claiming cross-platform support.
- Define parameters, pipeline behavior, output objects and streams, errors,
native exit codes, side effects, idempotence, and
-WhatIf behavior.
- Implement small advanced functions or modules with explicit boundaries and
direct command invocation.
- Run parser/static checks and focused Pester tests, then validate on every
claimed operating-system and PowerShell-version target where practical.
- Report the tested matrix, skipped targets, dependencies, commands, and
remaining platform risk.
Edition And Platform Contract
- Prefer current PowerShell 7 for new cross-platform scripts. Do not claim that
a script is cross-platform merely because
pwsh runs on Windows, macOS, and
Linux; every cmdlet, module, provider, native executable, path, and data format
must also exist or have an intentional adapter.
- Treat Windows PowerShell 5.1 compatibility as a separate target with older
language, .NET, module, encoding, and native-command behavior. Avoid dual
support unless consumers require it and the repository tests it.
- Use
$PSVersionTable and the repository's declared matrix as evidence. Keep
platform branches small and test each branch; prefer capability detection when
it expresses the real requirement better than an operating-system name.
- Consult current official PowerShell, Pester, PSScriptAnalyzer, or module
documentation after determining pinned versions.
Idiomatic PowerShell
- Write reusable commands as advanced functions with approved
Verb-Noun
names, [CmdletBinding()], explicit parameters, validation attributes, help,
and pipeline semantics where pipeline input is a real part of the contract.
- Emit objects from reusable functions. Leave formatting to callers or dedicated
format views; do not replace structured output with preformatted strings.
- Use full cmdlet and parameter names in durable scripts. Interactive aliases,
positional ambiguity, and implicit command discovery reduce readability and
portability.
- Use splatting and argument arrays for readable calls. Invoke native programs
with
& and separate arguments; never construct executable command text for
Invoke-Expression.
- Keep success output distinct from error, warning, verbose, debug, information,
and progress streams. Do not use
Write-Host as a machine-readable result.
- Use
Join-Path, Split-Path, Resolve-Path, Test-Path, literal-path
parameters, and .NET path APIs deliberately. Do not hard-code path separators,
drive letters, case-insensitive matching, or a caller working directory.
- Choose file encodings and newline expectations explicitly at boundaries.
Account for different defaults across PowerShell editions and external tools.
- Keep module import and script load behavior cheap and deterministic. Avoid
profile dependencies and mutation of caller-global preference variables.
Errors And Native Commands
- Distinguish non-terminating PowerShell errors, terminating errors, and native
process failures. Use
-ErrorAction Stop at boundaries that must enter
try/catch; do not assume every cmdlet failure terminates automatically.
- Catch only errors the current boundary can handle. Preserve useful error
records and context, use
finally for cleanup, and fail with a nonzero process
result when the script contract fails.
- Inspect
$LASTEXITCODE after native commands when their success matters. A
native nonzero exit is not universally equivalent to a catchable PowerShell
exception across supported versions.
- Avoid broad
SilentlyContinue, empty catches, output suppression that hides
failure, and global $ErrorActionPreference changes that leak to callers.
Safe Side Effects
- For functions that change state, use
[CmdletBinding(SupportsShouldProcess)] and call
$PSCmdlet.ShouldProcess() close to each mutation so -WhatIf and -Confirm
participate in the normal PowerShell contract.
- Do not implement a separate
-WhatIf switch or rely on Read-Host as the
only safety gate. Validate the exact target even in preview mode.
- Keep destructive, privileged, credentialed, and remote operations explicit,
idempotent where possible, and bounded by repository and agent authority.
- Never embed credentials or print secure values. Use established secret stores
and credential types without converting secrets to ordinary strings merely for
convenience.
Cross-Platform Checklist
Verify, rather than assume:
- command and module availability on Windows, macOS, and Linux targets;
- filesystem roots, separators, case behavior, permissions, symlinks, and
executable discovery;
- environment-variable names and platform-provided values;
- text encoding, BOM, newline, locale, culture, and date/number formatting;
- native argument passing, stdout/stderr treatment, signal and exit behavior;
- registry, services, event logs, COM, WMI/CIM, and other Windows-only providers;
and
- container, CI, remoting, authentication, and module-install assumptions.
Isolate unavoidable platform behavior behind small functions or adapters instead
of spreading $IsWindows, $IsMacOS, or $IsLinux branches throughout the
script.
Test PowerShell
Use the repository's Pester version and conventions. Do not mix Pester major
syntax or configuration without checking the installed version.
- Unit: test pure functions, parameter validation, object shapes,
transformations, error contracts, and
ShouldProcess decisions.
- Integration: exercise real modules, filesystems, native processes,
environment boundaries, and platform adapters with isolated temporary state.
- End to end: invoke the script or module entry point when parameters,
streams, host interaction, side effects, or process exit status are the
behavior under test.
- Use Pester's isolated filesystem facilities or repository-owned temporary
directories. Mock external boundaries selectively; do not mock the function or
behavior being specified.
- Run tests on every claimed OS and PowerShell edition/version. Static
compatibility analysis helps find gaps but does not replace target execution.
Use the repository's parser or import smoke check and configured
PSScriptAnalyzer settings. PSScriptAnalyzer can check scripts, modules, manifests,
style, defects, and configured compatibility profiles; review intentional
suppressions rather than disabling broad rule sets.
Typical commands to derive from repository evidence include:
Invoke-ScriptAnalyzer -Path ./scripts -Recurse
Invoke-Pester -Path ./tests
pwsh -NoProfile -File ./scripts/example.ps1
Use test-driven-development for behavior
changes and regressions, and systematic-debugging
for active unexplained PowerShell failures.
Anti-Patterns
Avoid:
- aliases and unexplained positional parameters in checked-in scripts;
- text parsing when cmdlets or APIs already return objects;
Invoke-Expression, interpolated command strings, and unvalidated paths;
Write-Host as data output or output formatting inside reusable functions;
- broad preference-variable changes, global state, profile dependence, and
import-time side effects;
- Windows-only commands, backslash paths, CRLF, case-insensitivity, or drive
assumptions in code labeled cross-platform;
- treating native stderr as automatic failure or forgetting
$LASTEXITCODE;
- ad hoc confirmations instead of
SupportsShouldProcess; and
- claiming cross-platform support after testing only one host.
Security And Completion
Load security-review for untrusted parameters,
paths, command execution, remoting, credentials, privileged operations, or
destructive targets. Use
security-review-evidence when command
output, transcripts, test artifacts, or reports may contain sensitive values.
Load dependency-supply-chain-review
for modules, galleries, installers, signatures, checksums, or provenance.
Before handoff, report supported and actually tested hosts, PowerShell versions,
Pester and analyzer evidence, side-effect and -WhatIf coverage, skipped matrix
entries, and residual compatibility or security risk.
1---2name: powershell-engineering3description: Cross-platform PowerShell engineering guidance. Use when creating, changing, reviewing, or testing `.ps1`, `.psm1`, or `.psd1` files, PowerShell functions/modules, Pester tests, PSScriptAnalyzer configuration, native-command orchestration, or Windows/macOS/Linux PowerShell automation. Do not use merely to run an existing PowerShell command or for generic script-language selection with no PowerShell implementation.4---56# PowerShell Engineering78Use this skill for idiomatic, testable PowerShell that behaves deliberately on9its supported platforms. Use [`script-engineering`](../script-engineering/SKILL.md)10when deciding whether the behavior belongs in a script or whether PowerShell is11preferable to shell, Python, Ruby, or JavaScript.1213## Workflow14151. Inspect repository instructions, `.ps1`, `.psm1`, `.psd1`, module manifests,16 required modules, Pester/PSScriptAnalyzer configuration, task runners, CI,17 documentation, and supported host matrix.182. Establish the minimum PowerShell edition and version. Distinguish PowerShell19 7+ (`pwsh`) from Windows PowerShell (`powershell.exe`); support both only when20 repository evidence requires both.213. Identify platform-specific commands, providers, paths, encodings, native22 executables, permissions, and modules before claiming cross-platform support.234. Define parameters, pipeline behavior, output objects and streams, errors,24 native exit codes, side effects, idempotence, and `-WhatIf` behavior.255. Implement small advanced functions or modules with explicit boundaries and26 direct command invocation.276. Run parser/static checks and focused Pester tests, then validate on every28 claimed operating-system and PowerShell-version target where practical.297. Report the tested matrix, skipped targets, dependencies, commands, and30 remaining platform risk.3132## Edition And Platform Contract3334- Prefer current PowerShell 7 for new cross-platform scripts. Do not claim that35 a script is cross-platform merely because `pwsh` runs on Windows, macOS, and36 Linux; every cmdlet, module, provider, native executable, path, and data format37 must also exist or have an intentional adapter.38- Treat Windows PowerShell 5.1 compatibility as a separate target with older39 language, .NET, module, encoding, and native-command behavior. Avoid dual40 support unless consumers require it and the repository tests it.41- Use `$PSVersionTable` and the repository's declared matrix as evidence. Keep42 platform branches small and test each branch; prefer capability detection when43 it expresses the real requirement better than an operating-system name.44- Consult current official PowerShell, Pester, PSScriptAnalyzer, or module45 documentation after determining pinned versions.4647## Idiomatic PowerShell4849- Write reusable commands as advanced functions with approved `Verb-Noun`50 names, `[CmdletBinding()]`, explicit parameters, validation attributes, help,51 and pipeline semantics where pipeline input is a real part of the contract.52- Emit objects from reusable functions. Leave formatting to callers or dedicated53 format views; do not replace structured output with preformatted strings.54- Use full cmdlet and parameter names in durable scripts. Interactive aliases,55 positional ambiguity, and implicit command discovery reduce readability and56 portability.57- Use splatting and argument arrays for readable calls. Invoke native programs58 with `&` and separate arguments; never construct executable command text for59 `Invoke-Expression`.60- Keep success output distinct from error, warning, verbose, debug, information,61 and progress streams. Do not use `Write-Host` as a machine-readable result.62- Use `Join-Path`, `Split-Path`, `Resolve-Path`, `Test-Path`, literal-path63 parameters, and .NET path APIs deliberately. Do not hard-code path separators,64 drive letters, case-insensitive matching, or a caller working directory.65- Choose file encodings and newline expectations explicitly at boundaries.66 Account for different defaults across PowerShell editions and external tools.67- Keep module import and script load behavior cheap and deterministic. Avoid68 profile dependencies and mutation of caller-global preference variables.6970## Errors And Native Commands7172- Distinguish non-terminating PowerShell errors, terminating errors, and native73 process failures. Use `-ErrorAction Stop` at boundaries that must enter74 `try`/`catch`; do not assume every cmdlet failure terminates automatically.75- Catch only errors the current boundary can handle. Preserve useful error76 records and context, use `finally` for cleanup, and fail with a nonzero process77 result when the script contract fails.78- Inspect `$LASTEXITCODE` after native commands when their success matters. A79 native nonzero exit is not universally equivalent to a catchable PowerShell80 exception across supported versions.81- Avoid broad `SilentlyContinue`, empty catches, output suppression that hides82 failure, and global `$ErrorActionPreference` changes that leak to callers.8384## Safe Side Effects8586- For functions that change state, use87 `[CmdletBinding(SupportsShouldProcess)]` and call88 `$PSCmdlet.ShouldProcess()` close to each mutation so `-WhatIf` and `-Confirm`89 participate in the normal PowerShell contract.90- Do not implement a separate `-WhatIf` switch or rely on `Read-Host` as the91 only safety gate. Validate the exact target even in preview mode.92- Keep destructive, privileged, credentialed, and remote operations explicit,93 idempotent where possible, and bounded by repository and agent authority.94- Never embed credentials or print secure values. Use established secret stores95 and credential types without converting secrets to ordinary strings merely for96 convenience.9798## Cross-Platform Checklist99100Verify, rather than assume:101102- command and module availability on Windows, macOS, and Linux targets;103- filesystem roots, separators, case behavior, permissions, symlinks, and104 executable discovery;105- environment-variable names and platform-provided values;106- text encoding, BOM, newline, locale, culture, and date/number formatting;107- native argument passing, stdout/stderr treatment, signal and exit behavior;108- registry, services, event logs, COM, WMI/CIM, and other Windows-only providers;109 and110- container, CI, remoting, authentication, and module-install assumptions.111112Isolate unavoidable platform behavior behind small functions or adapters instead113of spreading `$IsWindows`, `$IsMacOS`, or `$IsLinux` branches throughout the114script.115116## Test PowerShell117118Use the repository's Pester version and conventions. Do not mix Pester major119syntax or configuration without checking the installed version.120121- **Unit:** test pure functions, parameter validation, object shapes,122 transformations, error contracts, and `ShouldProcess` decisions.123- **Integration:** exercise real modules, filesystems, native processes,124 environment boundaries, and platform adapters with isolated temporary state.125- **End to end:** invoke the script or module entry point when parameters,126 streams, host interaction, side effects, or process exit status are the127 behavior under test.128- Use Pester's isolated filesystem facilities or repository-owned temporary129 directories. Mock external boundaries selectively; do not mock the function or130 behavior being specified.131- Run tests on every claimed OS and PowerShell edition/version. Static132 compatibility analysis helps find gaps but does not replace target execution.133134Use the repository's parser or import smoke check and configured135PSScriptAnalyzer settings. PSScriptAnalyzer can check scripts, modules, manifests,136style, defects, and configured compatibility profiles; review intentional137suppressions rather than disabling broad rule sets.138139Typical commands to derive from repository evidence include:140141```powershell142Invoke-ScriptAnalyzer -Path ./scripts -Recurse143Invoke-Pester -Path ./tests144pwsh -NoProfile -File ./scripts/example.ps1145```146147Use [`test-driven-development`](../test-driven-development/SKILL.md) for behavior148changes and regressions, and [`systematic-debugging`](../systematic-debugging/SKILL.md)149for active unexplained PowerShell failures.150151## Anti-Patterns152153Avoid:154155- aliases and unexplained positional parameters in checked-in scripts;156- text parsing when cmdlets or APIs already return objects;157- `Invoke-Expression`, interpolated command strings, and unvalidated paths;158- `Write-Host` as data output or output formatting inside reusable functions;159- broad preference-variable changes, global state, profile dependence, and160 import-time side effects;161- Windows-only commands, backslash paths, CRLF, case-insensitivity, or drive162 assumptions in code labeled cross-platform;163- treating native stderr as automatic failure or forgetting `$LASTEXITCODE`;164- ad hoc confirmations instead of `SupportsShouldProcess`; and165- claiming cross-platform support after testing only one host.166167## Security And Completion168169Load [`security-review`](../security-review/SKILL.md) for untrusted parameters,170paths, command execution, remoting, credentials, privileged operations, or171destructive targets. Use172[`security-review-evidence`](../security-review-evidence/SKILL.md) when command173output, transcripts, test artifacts, or reports may contain sensitive values.174Load [`dependency-supply-chain-review`](../dependency-supply-chain-review/SKILL.md)175for modules, galleries, installers, signatures, checksums, or provenance.176177Before handoff, report supported and actually tested hosts, PowerShell versions,178Pester and analyzer evidence, side-effect and `-WhatIf` coverage, skipped matrix179entries, and residual compatibility or security risk.