Tags: powershell, scripting, automation, windows, cli, devops, sysadmin
Level: intermediate
TL;DR: PowerShell is an object-oriented shell and scripting language built on .NET. It passes structured objects through pipelines rather than raw text streams.
Everything in the pipeline is a .NET object, preserving properties and methods across commands.
Cmdlet naming follows a strict Verb-Noun pattern such as Get-Process or Set-Item.
Modules bundle cmdlets, providers, functions, and variables for modular reuse.
Providers expose diverse data stores, such as the registry or certs, like standard filesystem drives.
The pipeline uses streaming execution: downstream commands process objects as upstream yields them.
Execution policies are safety controls to prevent accidental execution, not security boundaries.
Common parameters like -ErrorAction, -WhatIf, and -Confirm are built into advanced functions.
Output streams are numbered: 1 is Success, 2 Error, 3 Warning, 4 Verbose, 5 Debug, 6 Information.
PowerShell 7+ is cross-platform and runs on .NET Core across Windows, Linux, and macOS.
Remoting uses WinRM on Windows and OpenSSH cross-platform via Invoke-Command and Enter-PSSession.
Q: How does the PowerShell pipeline differ from a traditional Unix pipeline?R: Unix pipelines pass streams of untyped byte/text data that require parsing with tools like awk or sed. PowerShell passes typed .NET objects, allowing direct access to strongly-typed properties and methods downstream.
Q: What is the difference between $_ and $PSItem?R: They are identical; $PSItem is simply the explicit, readable alias for $_, representing the current object in the pipeline.
Q: What is the difference between terminating and non-terminating errors?R: Non-terminating errors write to stream 2 and continue execution, while terminating errors halt execution. I control non-terminating error behavior using -ErrorAction Stop to convert them into catchable terminating exceptions.
Q: Why should you avoid Write-Host in reusable automation scripts?R: Write-Host writes directly to the console host rather than the pipeline output stream (prior to PS 5.0, where it was tied to stream 6). I use Write-Output for returning pipeline data or Write-Verbose for operational logging.
Q: How do you execute commands concurrently across remote machines?R: I use Invoke-Command -ComputerName ... -ScriptBlock { ... }, which runs the block across targets in parallel over WinRM, or background jobs with Start-ThreadJob for local concurrency.
Array appending with += creates a brand new array copy every iteration, causing O(N^2) performance degradation on large collections.
FilterScript inside Where-Object inspects every single object; filter as far left as possible using cmdlet-native parameters like -Filter.
return in PowerShell does not discard uncaptured pipeline output produced earlier in the function.
Backtick (```) line continuation is fragile because trailing whitespace after it breaks parsing.
String comparison with -eq on an array acts as a filter returning matches rather than returning a single boolean.
Not using [CmdletBinding()] prevents scripts from inheriting common parameters like -Verbose or standard error controls.
# Discover commands, parameters, and documentation
Get-Command -Verb Get -Noun *Service*
Get-Help Get-Process -Examples
Get-Process | Get-Member -MemberType Property
# Efficient filtering and projection
Get-Service | Where-Object { $_.Status -eq 'Running' } | Select-Object -Property Name, DisplayName, StartType
# Pipeline-safe collection processing (avoiding += overhead)
$result = [System.Collections.Generic.List[string]]::new()
Get-ChildItem -Path ./src -Filter *.cs -Recurse | ForEach-Object {
$result.Add($_.FullName)
}
# Advanced function template with CmdletBinding
function Invoke-SafeDeploy {
[CmdletBinding(SupportsShouldProcess = $true)]
param (
[Parameter(Mandatory = $true, ValueFromPipeline = $true)]
[ValidateNotNullOrEmpty()]
[string]$Environment,
[ValidateSet('Rolling', 'BlueGreen')]
[string]$Strategy = 'Rolling'
)
process {
if ($PSCmdlet.ShouldProcess($Environment, "Deploying with $Strategy strategy")) {
Write-Verbose "Starting deployment to $Environment..."
# Core logic
}
}
}
# Error handling with terminating conversion
try {
Stop-Service -Name "NonExistentService" -ErrorAction Stop
} catch [System.ServiceProcess.ServiceCommandException] {
Write-Warning "Service operation failed: $($_.Exception.Message)"
} catch {
Write-Error "Unexpected failure: $($_.Exception.Message)"
} finally {
Write-Verbose "Cleanup executed."
}
# Remote execution across multiple nodes
Invoke-Command -ComputerName 'srv-web-01', 'srv-web-02' -ScriptBlock {
Get-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full' -Name Release
}
[ ] Implement custom parameter completers with Register-ArgumentCompleter
[ ] Measure performance differences between ForEach-Object and the language statement foreach ($x in $coll)
[ ] Validate custom module manifests (.psd1) using Test-ModuleManifest
[x] Manage terminating vs non-terminating exceptions with $ErrorActionPreference
[x] Extract object properties using Select-Object -ExpandProperty
[x] Configure remote sessions using OpenSSH transport in PowerShell 7
Microsoft Learn PowerShell Documentation: [learn.microsoft.com/powershell](https://learn.microsoft.com/powershell)
PowerShell GitHub Repository: [github.com/PowerShell/PowerShell](https://github.com/PowerShell/PowerShell)