PowerShell is a cross-platform automation shell built on .NET with an object-based pipeline, a rich standard library of cmdlets, and first-class scripting. If you’ve ever juggled Bash, Python, and C# to glue systems together, PowerShell lets you do most of that from one console—Windows, macOS, or Linux.
Who’s this for? Admins, SREs, devs, and curious tinkerers who want a single, pragmatic guide to the PowerShell universe—from “what is a cmdlet?” to remoting, packaging, testing, and performance.
Why PowerShell
- Objects, not text: Pipe rich .NET objects instead of parsing text. Less regex, fewer bugs.
- Cross-platform: PowerShell 7+ runs on Windows/macOS/Linux. Same language, platform-specific modules when needed.
- Deep OS access: Filesystem, registry, certificates, services, processes, WMI/CIM, event logs, and more via providers/cmdlets.
- Interoperability: Call native tools, .NET APIs, REST, SSH, and use JSON/CSV/XML like a local citizen.
A 10-minute Tour
Discoverability
Get-Help Get-Process -Online # open docs in browser
Get-Command -Noun Process # list all *-Process cmdlets
Get-Command -ParameterName ComputerName
Get-Member -InputObject (Get-Process | Select-Object -First 1) # inspect members
The object pipeline
Get-Service | Where-Object Status -eq Running | Sort-Object Name | Select-Object Name,Status
Get-ChildItem C:\Logs -File | Where-Object Length -gt 10MB | Remove-Item -WhatIf
Providers & PSDrives
Get-PSDrive # FileSystem, Registry (HKLM:\), Cert:\, Env:\, Function:\ ...
Set-Location HKLM:\SOFTWARE
Get-ChildItem Cert:\CurrentUser\My | Format-Table Subject, NotAfter
Formatting vs. data shaping
Get-Process | Select-Object Name,CPU | Sort-Object CPU -Descending |
Format-Table -AutoSize # Format-* is for display only; don't pipe it into more logic
Shell Superpowers
Consistency: Verb-Noun
Cmdlets use approved verbs (Get, Set, New, Remove, Invoke…) and singular nouns. Autocomplete helps you guess new commands you’ve never seen.
Common parameters
Most cmdlets support -Verbose, -ErrorAction, -WhatIf, -Confirm. Try them!
Aliases (use sparingly)
ls -> Get-ChildItem; cat -> Get-Content; ps -> Get-Process
# Great at the prompt; avoid aliases in scripts for readability.
Type accelerators & .NET interop
[datetime]::UtcNow
[regex]::Match('abc123', '\d+').Value
$wc = New-Object System.Net.Http.HttpClient
Scripting Essentials
Parameters, validation & pipeline input
function Get-TopProcess {
[CmdletBinding()]
param(
[int]$Count = 5,
[Parameter(ValueFromPipeline=$true)]
[object[]]$Process
)
process {
if(-not $Process){ $Process = Get-Process }
$Process | Sort-Object CPU -Descending | Select-Object -First $Count
}
}
Advanced functions & ShouldProcess
function Remove-OldLog {
[CmdletBinding(SupportsShouldProcess)]
param([string]$Path, [int]$Days = 30)
$cut = (Get-Date).AddDays(-$Days)
Get-ChildItem $Path -File | Where-Object LastWriteTime -lt $cut |
ForEach-Object {
if ($PSCmdlet.ShouldProcess($_.FullName, "Delete")) { Remove-Item $_.FullName -Force }
}
}
Variables & scope
$script:Var = 42; $global:Log = @(); $env:Path; $PSBoundParameters
Classes (when you need structure)
class Server {
[string]$Name
[string]$Role
Server([string]$n,[string]$r){ $this.Name=$n; $this.Role=$r }
}
Errors & Debugging
Terminating vs non-terminating
$ErrorActionPreference = 'Stop' # promote non-terminating errors to terminating
try {
1/0
} catch {
Write-Warning "Oops: $($_.Exception.Message)"
} finally {
Write-Verbose "Always runs" -Verbose
}
Debugging & transcripts
Set-PSDebug -Trace 1 # lightweight tracing (turn off after!)
Start-Transcript .\session.log # capture console I/O
Working with Data (JSON / CSV / XML)
# JSON
Invoke-RestMethod https://api.example.com/users | Where-Object active |
ConvertTo-Json -Depth 5 | Out-File users.json
# CSV
Import-Csv .\servers.csv | Where-Object Role -eq 'web' |
ForEach-Object { Test-Connection $_.Name -Count 1 }
# XML
[xml]$xml = Get-Content .\config.xml
$xml.configuration.appSettings.add | Select-Object key, value
Remoting & Parallelism
Windows Remoting (WinRM)
Enter-PSSession -ComputerName server01
Invoke-Command -ComputerName web01,web02 -ScriptBlock { Get-Service w3svc }
SSH-based remoting (cross-platform)
Enter-PSSession -HostName linux01 -User demo
Invoke-Command -HostName mac01 -User admin -ScriptBlock { uname -a }
Background & parallel
# Background jobs
1..10 | ForEach-Object { Start-Job { param($i) Start-Sleep 1; "Job $i" } -ArgumentList $_ } | Receive-Job -Wait
# PowerShell 7+: foreach -Parallel
$servers | ForEach-Object -Parallel { Test-Connection $_ -Count 1 } -ThrottleLimit 10
Security, Signing & Execution Policy
- Execution Policy: Controls script launch behaviour (
Get-ExecutionPolicy,Set-ExecutionPolicy). It’s not a security boundary, but a guardrail. - Script signing: Use a code-signing certificate; set policy to
AllSignedin locked-down environments. - AMSI & Defender: PowerShell integrates with antimalware; keep AV and OS current.
- Constrained Language Mode (CLM): Reduces capabilities under certain enterprise controls.
Modules, Packaging & PowerShell Gallery
# Find, install, update (PowerShellGet)
Find-Module Pester | Install-Module -Scope CurrentUser
Update-Module Pester
# Create a module
New-ModuleManifest -Path .\AwesomeTools\AwesomeTools.psd1 -RootModule AwesomeTools.psm1
# Export functions in the .psm1; publish when ready.
Prefer modules over one-off scripts for reuse/versioning. Include a manifest, semantic version, dependency list, and help.
Tooling: Profiles, PSReadLine, Style
- Profile: Customize your shell in
$PROFILE(aliases, prompt, module imports). - PSReadLine: Syntax highlighting, history search (Ctrl+r), predictive IntelliSense.
- Style: Use approved verbs, singular nouns, PascalCase for functions,
Verb-Nounnaming, and comment-based help. - Help: Add examples and parameter docs with
<# .SYNOPSIS ... #>blocks; surface viaGet-Help.
Windows, Linux & Cloud Automation
Windows & Server
Get-Service, Get-EventLog / Get-WinEvent, Get-ScheduledTask, Get-NetAdapter
Enable-WindowsOptionalFeature -Online -FeatureName containers
Linux/macOS
# Use native tools + PowerShell objects
df -h | Out-String
Get-ChildItem /var/log | Where-Object Length -gt 1MB
Cloud & REST
# REST first
Invoke-RestMethod -Uri 'https://api.example.com/items' -Headers @{Authorization="Bearer $token"}
# Cloud modules (examples)
# Install-Module Az; Install-Module AWSPowerShell.NetCore; Install-Module GoogleCloud
Testing, Linting & CI
Pester (unit & integration tests)
Describe "Get-TopProcess" {
It "returns N items" {
(Get-TopProcess -Count 3).Count | Should -Be 3
}
}
PSScriptAnalyzer (linting)
Install-Module PSScriptAnalyzer
Invoke-ScriptAnalyzer -Path .\ -Recurse -Fix
CI
Run Pester & PSScriptAnalyzer in GitHub Actions/Azure DevOps; publish modules to an internal feed or the PowerShell Gallery as part of release.
Performance Tips
- Measure:
Measure-CommandandMeasure-Objectbefore “optimizing”. - Avoid unnecessary formatting in the middle of pipelines; shape with
Select-Objectuntil the end. - Prefer cmdlets over external tools when you need objects; prefer native tools when you only need raw text quickly.
- Use
ForEach-Object -Parallel(7+) or background jobs for I/O-bound fan-out. - Batch remote calls: Use
Invoke-Commandwith multiple targets instead of serial loops.
Cheat Sheet
# Discover
Get-Help cmdlet -Online
Get-Command *service*
Get-Member
# Files
Get-ChildItem -Recurse | Where Length -gt 1MB
Get-Content .\log.txt -Tail 50
# JSON
Invoke-RestMethod ... | ConvertTo-Json -Depth 5
# Errors
$ErrorActionPreference = 'Stop'
try { ... } catch { $_ | Out-String }
# Remoting
Invoke-Command -ComputerName srv -ScriptBlock { hostname }
# Parallel (7+)
$items | ForEach-Object -Parallel { ... } -ThrottleLimit 10
Check your runtime with $PSVersionTable. Put your favourite tweaks in $PROFILE. Keep modules up to date.