Before running any command that deletes, stops, or modifies things, add -WhatIf first. It prints exactly what would happen without actually doing it. Remove-Item -Path C:\Logs\* -Recurse -WhatIf shows every file that would be deleted. Once you are confident, remove -WhatIf to execute. This habit prevents catastrophic mistakes in production.
Cmdlets, Syntax, and the Pipeline
Master the building blocks of PowerShell: how cmdlets are structured, how parameters work, why the object pipeline is powerful, and which pipeline utility cmdlets you will use every day.
🧒 Simple Explanation (ELI5)
A cmdlet is like a power tool with a specific job: a drill drills, a saw cuts, a hammer pounds. The pipeline is like a conveyor belt connecting tools: one tool finishes its job and passes the result directly to the next. In PowerShell, instead of making a separate trip back to the workbench between every tool, you connect them with a pipe (|) and the work flows straight through.
🔧 Why Do We Need It?
- Composability: each cmdlet does one thing well; the pipeline lets you compose them into powerful one-liners without writing a full script.
- Object passing: unlike text-based pipes, PowerShell passes typed objects so you never parse output manually.
- Discovery: Verb-Noun naming means you can guess cmdlet names correctly.
Get-Service,Stop-Service,Start-Service,Restart-Serviceare all obvious. - Safety: the
-WhatIfparameter lets you preview any destructive operation before committing.
🌍 Real-world Analogy
An assembly line in a factory: each station takes the car from the previous station, does its specific job, and passes it to the next. No station needs to know the full process. PowerShell's pipeline works the same way. Each cmdlet receives objects, applies its operation, and sends results downstream.
⚙️ Technical Explanation
A cmdlet is a compiled .NET class. Every cmdlet follows Verb-Noun: the verb describes the action (Get, Set, New, Remove, Start, Stop, Invoke, Export, Import, Test, Wait) and the noun describes the target (Service, Process, Item, Content, AzResourceGroup).
Parameters are named arguments prefixed with a hyphen: -Path, -Name, -Recurse, -Force, -ErrorAction. Positional parameters allow omitting the name for common arguments. Switch parameters are boolean on/off flags: -Recurse, -Force, -Verbose, -Debug, -WhatIf, -Confirm.
The pipeline | passes the complete object output from the left cmdlet as input to the right cmdlet. The automatic variable $_ (or $PSItem) refers to the current object being processed in pipeline expressions.
After any pipeline cmdlet, add | Get-Member to see every property and method available. Get-Process | Get-Member -MemberType Property shows all properties you can filter and sort on. This is how you discover that process objects have WorkingSet64, CPU, StartTime, and more—without reading documentation every time.
📊 Visual Representation
⌨️ Commands / Syntax
# Cmdlet with named parameters
Get-Service -Name "wuauserv" -ComputerName localhost
# Pipeline: filter, sort, select
Get-Process |
Where-Object { $_.CPU -gt 5 } |
Sort-Object CPU -Descending |
Select-Object Name, Id, CPU
# Explore object properties
Get-Process | Get-Member -MemberType Property
# Count results
Get-Service | Where-Object Status -eq Running | Measure-Object
# Export to CSV file
Get-Service |
Select-Object Name, Status, StartType |
Export-Csv -Path services.csv -NoTypeInformation
# Preview destructive operations with -WhatIf
Remove-Item -Path C:\Temp\*.log -Recurse -WhatIf
# ForEach-Object to run code per object
Get-Service | Where-Object Status -eq Stopped | ForEach-Object {
Write-Host "Stopped service: $($_.Name)"
}
# Aliases (avoid in scripts, useful at command line)
# gps = Get-Process
# gsv = Get-Service
# ls/dir = Get-ChildItem
💼 Example (Real-world Use Case)
A security engineer needs a list of all Windows services running as LocalSystem on a server: Get-CimInstance Win32_Service | Where-Object StartName -eq 'LocalSystem' | Select-Object Name, StartMode, State | Sort-Object Name | Export-Csv -Path local-system-services.csv -NoTypeInformation. This one pipeline generates an audit-ready report without any custom tooling or text parsing.
🧪 Hands-on
- Count stopped services:
Get-Service | Where-Object Status -eq Stopped | Measure-Object. - Group processes by company:
Get-Process | Group-Object Company | Sort-Object Count -Descending | Select-Object -First 5. - See the last 10 System events:
Get-EventLog -LogName System -Newest 10 | Select-Object TimeGenerated, EntryType, Source. - Explore process properties:
Get-Process | Get-Member -MemberType Property | Select-Object Name. - Preview deleting temp files:
Get-ChildItem -Path $env:TEMP -Filter *.tmp | Remove-Item -WhatIf.
Build a one-liner that finds all services that are stopped AND set to Automatic start (services that should be running but are not): Get-Service | Where-Object { $_.Status -eq 'Stopped' -and $_.StartType -eq 'Automatic' } | Select-Object Name, DisplayName | Sort-Object Name. This is a real health-check pattern that sysadmins run after reboots.
🐛 Debugging Scenario
Problem: you run a pipeline and it silently skips some objects without error—output count is lower than expected.
- Cause 1: the
Where-Objectcondition references a property that is$nullon some objects, causing them to evaluate as false and drop silently. - Cause 2:
-ErrorAction SilentlyContinueis set, suppressing errors that would otherwise surface the skip. - Diagnose: add
-Verboseto cmdlets, check$Errorafter the pipeline, and temporarily remove filters to see raw output count. - Fix: validate that the filtering property exists on all objects with
Get-Memberbefore writing the filter. Add explicit null checks:Where-Object { $_.CPU -ne $null -and $_.CPU -gt 5 }.
🎯 Interview Questions
Beginner
A cmdlet is a compiled .NET class that performs a single, specific operation. It follows the Verb-Noun naming convention (Get-Process, Stop-Service) and can be composed with other cmdlets via the pipeline.
It passes the .NET object output from one cmdlet as input to the next cmdlet. This is different from Bash where the pipe passes text; PowerShell preserves the full object structure, so the receiving cmdlet can access typed properties.
$_ (or $PSItem) is the current object being processed in a pipeline expression. In Where-Object { $_.CPU -gt 5 }, $_ refers to each Process object as it flows through the filter.
-WhatIf is a safety parameter supported by most commands that modify state. It previews what the command would do without executing it. Always use -WhatIf before running Remove-Item, Stop-Service, or any state-changing operation in production.
Pipe to Export-Csv -Path output.csv -NoTypeInformation. The -NoTypeInformation parameter omits the .NET type header row that would otherwise appear at the top of the file.
Intermediate
Bash passes text streams between processes. PowerShell passes .NET objects with typed properties between cmdlets. This means no text parsing—you access properties directly like $_.CPU or $_.Status instead of using cut, awk, or grep.
ForEach-Object works inline in a pipeline and processes objects one at a time as they arrive (streaming). A foreach loop requires the entire collection in memory first. For large datasets piped from cmdlets, ForEach-Object is more memory-efficient.
Select-Object outputs slimmed-down objects you can continue piping. Format-Table outputs formatted display strings for human reading—you cannot pipe Format-Table output to Export-Csv. Always use Select-Object when you need to process results further; use Format-Table only for final display at the console.
Measure-Object with -Count, -Sum, -Average, -Minimum, and -Maximum parameters. Example: Get-Process | Measure-Object CPU -Sum -Average returns total and average CPU across all processes.
Pipeline binding is the automatic passing of pipeline output to a receiving cmdlet's parameter. Parameters accept pipeline input if they are marked ValueFromPipeline or ValueFromPipelineByPropertyName in their definition. You can see this with Get-Help cmdlet -Full, looking at the parameter Accept pipeline input? field.
Scenario-based
Get-Service | Where-Object { $_.Status -eq 'Stopped' -and $_.StartType -eq 'Automatic' } | Select-Object Name, DisplayName | Sort-Object Name. This is a standard health-check script that runs after Windows server reboots to surface services that should have started but did not.
Set $ErrorActionPreference = 'Stop' to make all errors terminating, or add -ErrorAction Stop to each cmdlet. Check $Error[0] after the pipeline. Add -Verbose to see per-item progress. Use a try/catch wrapper around the pipeline to capture and log failures with full context.
Get-Service | Select-Object Name, DisplayName, Status, StartType | Sort-Object Name | Export-Csv -Path services-$(Get-Date -Format yyyyMMdd).csv -NoTypeInformation. Running this on multiple machines adds -ComputerName or uses Invoke-Command for remoting.
🌐 Real-world Usage
Operations engineers build pipeline one-liners for daily health checks: running services, disk space, failed events, stopped critical services. Security teams build audit pipelines for user access, service accounts, and open firewall rules. These pipelines feed Executive dashboards and compliance reports via Export-Csv or ConvertTo-Json.
📝 Summary
Cmdlets follow Verb-Noun, accept named parameters, and support -WhatIf for safe previewing. The pipeline passes typed .NET objects, not text. Core pipeline tools are Where-Object (filter), Sort-Object (sort), Select-Object (project), Measure-Object (aggregate), ForEach-Object (iterate), and Export-Csv (persist). Use Get-Member to discover object properties. Never use Format-* cmdlets inside a pipeline where you need the data downstream.