PowerShell

PowerShell is Microsoft's automation shell and scripting language — and its defining idea separates it from every Unix shell: the pipeline passes objects, not text. Where Bash pipes byte streams that you slice with awk and regex, PowerShell pipes .NET objects with typed properties you filter and sort directly. No parsing ls -l columns; Get-ChildItem gives you objects with a .Length you can compare as a number.

It's the native language of Windows administration (Active Directory, Exchange, IIS, Intune are managed through it), a first-class citizen in Azure automation, and — since PowerShell 7 went cross-platform on .NET — a legitimate scripting option on Linux and macOS too.

TL;DR

Quick Example

Real administration tasks, the PowerShell way:

Note what's absent: no grep/awk/sed, no JSON parsing library, no SSH loop — the object pipeline, Invoke-RestMethod, and remoting cover it.

Core Concepts

The Object Pipeline

The mental shift from Unix shells:

Three cmdlets do most pipeline work: Where-Object (filter), Select-Object (project columns / first N), Sort-Object. ForEach-Object maps; Measure-Object aggregates; Group-Object is GROUP BY. When you don't know what an object offers:

Get-Member + Get-Help + Get-Command form the self-discovery loop that makes PowerShell learnable from inside the shell.

Language Essentials

Gotchas worth tattooing: comparison is -eq, not ==; the escape character is the backtick ` `; and comparisons are case-insensitive by default (-ceq` for case-sensitive).

Error Handling — the Big Trap

PowerShell distinguishes terminating errors (stop execution, catchable) from non-terminating ones (print red text, continue, skip your catch):

Robust scripts open with $ErrorActionPreference = 'Stop' and use try/catch/finally deliberately. For native executables, check $LASTEXITCODE — .NET exceptions and process exit codes are separate worlds.

Remoting and Fleet Management

Results returned from remote machines are deserialized property bags — data survives, live methods don't. For configuration-as-code at fleet scale, that role has largely moved to Ansible (which drives Windows via PowerShell/WinRM under the hood) and DSC's successors.

The Ecosystem

Yes, testing: infrastructure scripts with Pester tests and ScriptAnalyzer gates in CI are the line between "automation" and "pile of .ps1 files everyone fears."

PowerShell vs Bash (and Python)

Pragmatic rules: administering Windows/Azure/M365 → PowerShell, no contest. Gluing Unix tools, containers, and CI steps → Bash. Past ~100 lines of logic in either → consider Python. Cross-platform teams increasingly write ops tooling in pwsh precisely because the same script runs on the Windows jump box and the Linux runner.

Common Mistakes

Parsing Text You Already Have as Objects

Piping Get-Process to Out-String and regexing it recreates Bash's problems voluntarily. Stay in objects until the final output step; format (Format-Table) only at the end — formatted output is for eyes and destroys the objects for any later pipeline stage.

Trusting catch Without -ErrorAction Stop

The single most common silently-broken pattern in production scripts (see above). Audit any script whose try/catch has never visibly fired.

Write-Host for Data

Write-Host paints the console and returns nothing — data "output" that way can't be piped, captured, or tested. Emit objects; use Write-Verbose/Write-Warning for commentary.

Ignoring 5.1 vs 7 Differences

Legacy Windows PowerShell 5.1 (.NET Framework) and PowerShell 7 (pwsh, modern .NET) differ in defaults (encoding! 5.1's UTF-16/ANSI vs 7's UTF-8), available operators (&&, ||, ternary are 7-only), and module compatibility. Target 7, declare it (#Requires -Version 7), and test on what production actually runs.

Backslash-Escaping Like It's Bash

The escape character is the backtick; backslashes are just path separators. Regex escapes, line continuations, and special characters all follow backtick rules — muscle memory from Bash produces subtly broken strings.

FAQ

Is PowerShell only for Windows?

Not since 2018 — PowerShell 7 runs on Linux and macOS, remotes over SSH, and is preinstalled in Azure Cloud Shell and many CI images. Its center of gravity remains Windows/Azure administration, but "pwsh as a portable ops language" is real, especially in mixed shops.

Should a Linux person bother learning it?

If you touch Windows Server, Active Directory, Azure, or Microsoft 365 at all — yes, it's the management surface; clicking the portal doesn't scale. Otherwise it's optional, though the object pipeline is a genuinely mind-expanding contrast to text pipes.

PowerShell or Python for automation scripts?

PowerShell when the task is system administration on Microsoft surfaces (everything has a cmdlet; identity/remoting are built in). Python when logic dominates — data structures, algorithms, third-party APIs beyond REST. The crossover point is lower than PowerShell fans admit and higher than Python fans assume.

What's the deal with execution policy?

A safety yes-I-meant-to seatbelt, not a security boundary (trivially bypassed by design). Set-ExecutionPolicy RemoteSigned per machine or -Scope CurrentUser is the standard developer setup; signed scripts matter in locked-down enterprises.

How do I make scripts trustworthy?

Same as any code: parameters over hardcoding, [CmdletBinding()] with -WhatIf support (SupportsShouldProcess) for destructive actions, Pester tests, PSScriptAnalyzer in CI, and secrets from a vault (SecretManagement module) — never inline. See Secrets Management.

Related Topics

References