PowerShell essentials
PowerShell pipes objects, not text: the entire design follows from that. Scripts that treat it as bash-with-verbs fight the tool; scripts that filter and shape objects get parsing for free.
Method
- Stay in objects until the last step.
Get-Process | Where-Object CPU -gt 100 | Select-Object Name, Id: no regex, no cut/awk. Convert to text (Format-, Out-) only at final display; Format cmdlets destroy the objects, so never put them mid-pipeline. For interop,ConvertTo-Json/ConvertFrom-Jsonround-trip structures (the text-processing "structured first" rule is the default here). - Make errors terminating where correctness matters.
Non-terminating errors continue by default: set
$ErrorActionPreference = 'Stop'at script top (and know native executables still need$LASTEXITCODEchecks; PSNativeCommandErrorActionPreference in pwsh 7.4+ closes that gap).try/catcharound the operations you can handle;-ErrorAction SilentlyContinueonly with a comment defending the swallow (the bash-robustness|| truerule, translated). - Write functions as advanced functions.
[CmdletBinding()], typedparam()blocks with validation attributes ([ValidateNotNullOrEmpty()],[ValidateSet(...)]),SupportsShouldProcessfor anything destructive so-WhatIfand-Confirmwork fleet-wide (see automation-guardrails): this is the platform's built-in dry-run, use it. Verb-Noun names fromGet-Verb; comment-based help for anything shared. - Structure scripts like the CLI contract they are. Param
block at top (positional for the common case, named for the
rest), objects to the output stream (so callers can pipe),
diagnostics via
Write-Verbose/Write-Warning(neverWrite-Hostfor data), exit codes for CI consumption (see python-cli-tools: same contract, different runtime).Set-StrictMode -Version Latestcatches typo'd variables the wayset -udoes. - Target the right edition per fleet. pwsh (7+) is cross-platform and actively developed: default for new work, including Linux containers and CI. Windows PowerShell 5.1 is what stock Windows guarantees: constrain to compatible syntax (or ship pwsh) when targeting unmanaged Windows boxes; module availability differs, so CI-test on the editions you claim (the shell-portability matrix ethic).
- Lint and test like real code. PSScriptAnalyzer in CI
(see linting-setup), Pester for unit tests of functions
(mock cmdlets, assert on objects: see test-doubles); modules
(
.psm1+ manifest) over dot-sourced script piles once functions are shared (see python-project-structure instincts).
Boundaries
- Remoting, DSC, and AD administration are their own disciplines with security surface (JEA, credential handling: see least-privilege, secrets-management); this skill covers the scripting core beneath them.
- Text-native interop (parsing legacy tool output) loses the object advantage; for heavy text pipelines on Linux, classic tools may serve better (see text-processing): choose per job.
- Performance-sensitive loops over huge collections can crawl in
the pipeline; measure and drop to .NET methods
(
[IO.File]::ReadLines) where profiles demand (see benchmark-design), not preemptively.