“Can you script?”
That question separates junior admins from senior ones. And in the Windows world, the answer needs to be PowerShell. It’s not optional anymore – Server Core has no GUI, Azure is PowerShell-native, and anything at scale requires automation.
The good news? PowerShell is learnable. If you’ve used any command line, you’re halfway there. This guide gets you from zero to productive.
What you’ll learn:
- Why PowerShell (not just cmd)
- Cmdlet structure and discovery
- The pipeline – PowerShell’s superpower
- Variables, loops, and conditionals
- Your first useful scripts
Career Value: PowerShell scripting is the single skill that transforms admins from button-clickers to automation engineers. It’s the difference between a GBP 35k helpdesk role and a GBP 55k+ DevOps position. Every serious Windows job listing asks for it.
Quick Reference
| Cmdlet | Purpose | Example |
|---|---|---|
Get-Help |
Documentation | Get-Help Get-Process -Full |
Get-Command |
Find cmdlets | Get-Command *service* |
Get-Member |
See object properties | Get-Process | Get-Member |
Select-Object |
Choose properties | Get-Process | Select Name, CPU |
Where-Object |
Filter results | Get-Process | Where CPU -gt 10 |
ForEach-Object |
Loop through items | 1..10 | ForEach { $_ * 2 } |
Why PowerShell?
CMD is Dead (Mostly)
| CMD | PowerShell |
|---|---|
| Text-based output | Object-based output |
| Limited scripting | Full programming language |
| Windows only | Cross-platform (PowerShell 7) |
| No remoting built-in | Native remoting |
| Dying | The future |
Real Example: Finding Large Files
CMD:
forfiles /S /M * /C "cmd /c if @fsize GEQ 104857600 echo @path @fsize"
PowerShell:
Get-ChildItem -Recurse | Where-Object { $_.Length -gt 100MB } | Select-Object FullName, Length
PowerShell is readable. That matters when you’re troubleshooting at 2am.
The Verb-Noun Structure
Every PowerShell cmdlet follows the pattern: Verb-Noun
Common Verbs:
Get– Retrieve informationSet– Change somethingNew– Create somethingRemove– Delete somethingStart/Stop– Control services/processesEnable/Disable– Toggle features
Examples:
Get-Process # Get running processes
Set-Location # Change directory (cd)
New-Item # Create file/folder
Remove-Item # Delete file/folder
Start-Service # Start a service
Stop-Service # Stop a service
Pro Tip: If you can guess the verb and noun, you can often guess the cmdlet. Need to get services? Try Get-Service. Need to create a user? Try New-ADUser.
Finding Commands
Get-Command: Your Discovery Tool
# Find all commands with "service" in the name
Get-Command *service*
# Find all commands in a module
Get-Command -Module ActiveDirectory
# Find commands by verb
Get-Command -Verb Get
# Find commands that work with processes
Get-Command -Noun Process
Get-Help: Built-in Documentation
# Basic help
Get-Help Get-Process
# Full help with examples
Get-Help Get-Process -Full
# Just the examples
Get-Help Get-Process -Examples
# Online help (opens browser)
Get-Help Get-Process -Online
First time? Update help:
Update-Help -Force
The Pipeline: PowerShell’s Superpower
The pipeline (|) passes objects from one command to the next. This is what makes PowerShell powerful.
Basic Pipeline
# Get processes, sort by CPU, show top 10
Get-Process | Sort-Object CPU -Descending | Select-Object -First 10
# Get services, filter to stopped, format as table
Get-Service | Where-Object Status -eq 'Stopped' | Format-Table Name, Status
Understanding Objects
PowerShell passes objects, not text. This is crucial.
# See what properties an object has
Get-Process | Get-Member
# A process object has: Name, CPU, Memory, Id, etc.
# You can access any of these
Get-Process | Select-Object Name, CPU, WorkingSet
Filtering with Where-Object
# Services that are running
Get-Service | Where-Object { $_.Status -eq 'Running' }
# Shorthand (PowerShell 3+)
Get-Service | Where Status -eq 'Running'
# Multiple conditions
Get-Process | Where-Object { $_.CPU -gt 10 -and $_.WorkingSet -gt 100MB }
Selecting Properties
# Choose specific properties
Get-ADUser -Filter * -Properties EmailAddress | Select-Object Name, EmailAddress, Enabled
# Create calculated properties
Get-Process | Select-Object Name, @{Name='MemoryMB';Expression={$_.WorkingSet/1MB}}
Variables and Data Types
Basic Variables
# Assign a variable
$name = "John"
$number = 42
$servers = @("DC01", "DC02", "WEB01")
# Use a variable
Write-Output "Hello, $name"
Write-Output "The answer is $number"
Arrays
# Create an array
$servers = @("DC01", "DC02", "WEB01")
# Access elements
$servers[0] # DC01
$servers[-1] # WEB01 (last item)
# Loop through
foreach ($server in $servers) {
Write-Output "Processing $server"
}
Hash Tables
# Create a hash table
$user = @{
Name = "John Smith"
Department = "IT"
Title = "Sysadmin"
}
# Access values
$user.Name
$user["Department"]
Control Flow
If/Else
$service = Get-Service -Name "Spooler"
if ($service.Status -eq "Running") {
Write-Output "Print spooler is running"
} elseif ($service.Status -eq "Stopped") {
Write-Output "Print spooler is stopped"
Start-Service -Name "Spooler"
} else {
Write-Output "Unknown status: $($service.Status)"
}
ForEach Loop
$servers = @("DC01", "DC02", "WEB01")
foreach ($server in $servers) {
$result = Test-Connection -ComputerName $server -Count 1 -Quiet
if ($result) {
Write-Output "$server is online"
} else {
Write-Output "$server is OFFLINE"
}
}
ForEach-Object (Pipeline)
# Same thing, pipeline style
@("DC01", "DC02", "WEB01") | ForEach-Object {
$online = Test-Connection -ComputerName $_ -Count 1 -Quiet
[PSCustomObject]@{
Server = $_
Online = $online
}
}
Practical Scripts
Script 1: Find Large Files
# Find files over 100MB, export to CSV
$path = "C:\Users"
$minSize = 100MB
Get-ChildItem -Path $path -Recurse -File -ErrorAction SilentlyContinue |
Where-Object { $_.Length -gt $minSize } |
Select-Object FullName, @{N='SizeMB';E={[math]::Round($_.Length/1MB,2)}}, LastWriteTime |
Sort-Object SizeMB -Descending |
Export-Csv -Path "C:\Temp\LargeFiles.csv" -NoTypeInformation
Write-Output "Report saved to C:\Temp\LargeFiles.csv"
Script 2: Check Service Status Across Servers
$servers = @("DC01", "DC02", "WEB01")
$serviceName = "Spooler"
# Get-Service -ComputerName only exists in Windows PowerShell 5.1; PowerShell 7 removed it.
# Invoke-Command (WinRM remoting, enabled by default on Windows Server) works in both.
$results = foreach ($server in $servers) {
try {
$status = Invoke-Command -ComputerName $server -ErrorAction Stop -ScriptBlock {
(Get-Service -Name $using:serviceName -ErrorAction Stop).Status.ToString()
}
[PSCustomObject]@{
Server = $server
Service = $serviceName
Status = $status
}
} catch {
[PSCustomObject]@{
Server = $server
Service = $serviceName
Status = "ERROR: $($_.Exception.Message)"
}
}
}
$results | Format-Table -AutoSize
Script 3: Bulk User Creation
# CSV file with: FirstName,LastName,Department
$users = Import-Csv -Path "C:\Temp\NewUsers.csv"
# Prompt for the initial password rather than hardcoding one in the script
$initialPassword = Read-Host -AsSecureString "Initial password"
foreach ($user in $users) {
$username = "$($user.FirstName.Substring(0,1))$($user.LastName)".ToLower()
New-ADUser -Name "$($user.FirstName) $($user.LastName)" `
-GivenName $user.FirstName `
-Surname $user.LastName `
-SamAccountName $username `
-UserPrincipalName "[email protected]" `
-Department $user.Department `
-Path "OU=Users,DC=corp,DC=example,DC=com" `
-AccountPassword $initialPassword `
-Enabled $true `
-ChangePasswordAtLogon $true
Write-Output "Created user: $username"
}
PowerShell vs Bash: Quick Comparison
| Task | Bash | PowerShell |
|---|---|---|
| List files | ls -la |
Get-ChildItem |
| Find text in files | grep "text" file |
Select-String "text" file |
| Process list | ps aux |
Get-Process |
| Current directory | pwd |
Get-Location |
| Environment variable | echo $PATH |
$env:PATH |
| Loop | for i in 1 2 3; do echo $i; done |
1..3 | ForEach { $_ } |
Pro Tip: PowerShell has aliases for common Bash/CMD commands. ls, cd, pwd, cat all work – but they’re calling PowerShell cmdlets underneath.
Interview Questions
Q1: “What’s the difference between ForEach-Object and foreach?”
Good Answer: “foreach is a language statement – it loads all items into memory first, then iterates. ForEach-Object is a cmdlet used in the pipeline – it processes items one at a time as they flow through. For large datasets, ForEach-Object is more memory-efficient. For speed with smaller sets, foreach is faster.”
Q2: “How do you find out what a cmdlet does and what parameters it has?”
Good Answer: “Get-Help CmdletName -Full shows complete documentation including parameters and examples. Get-Command CmdletName shows the syntax. Get-Member piped after a cmdlet shows the properties and methods of the output objects. I usually start with Get-Help -Examples to see practical usage.”
Q3: “Write a one-liner to find all stopped services and start them.”
Good Answer:
Get-Service | Where Status -eq 'Stopped' | Start-Service
“Though in practice, I’d add -WhatIf first to preview, and probably filter to specific services rather than starting everything.”
Career Application
On your resume:
- “Automated user provisioning with PowerShell, reducing onboarding time by 80%”
- “Developed PowerShell scripts for server health monitoring across 50+ servers”
- “Created bulk AD management scripts for user lifecycle management”
Demonstrate:
- Object pipeline understanding
- Error handling (
try/catch) - Real scripts you’ve written
- Ability to read and modify existing scripts
Next Steps
- Next in series: Group Policy Deep Dive – apply settings at scale
- Related: Automation: Scripting Fundamentals – Bash + PowerShell comparison
- Practice: Automate something you do manually. Start small – a report, a cleanup task.
Scripting is what separates admins from senior admins. Next up: Group Policy – controlling hundreds of machines with a few clicks.
Windows Fundamentals Series
Part 3 of 6
Previous: Active Directory Essentials | Next: Group Policy Deep Dive
Frequently Asked Questions
Why should sysadmins learn PowerShell?
PowerShell is the primary management tool for Windows Server, Active Directory, Exchange, Azure, and Microsoft 365. CMD cannot manage modern Microsoft services. PowerShell’s object-oriented pipeline, remote management capabilities, and integration with .NET make it essential for automating Windows administration tasks at scale.
What is the PowerShell pipeline and why is it powerful?
The PowerShell pipeline passes objects (not text) between commands using the | symbol. Each command receives structured objects with properties and methods, not raw strings. This means you can filter, sort, select, and transform data without text parsing. For example, Get-Process | Where-Object CPU -gt 100 | Sort-Object CPU filters and sorts process objects directly.
How do I find PowerShell commands I need?
Use Get-Command with wildcards: Get-Command *user* finds all commands related to users. Use Get-Help for documentation: Get-Help Get-ADUser -Examples shows usage examples. All PowerShell commands follow a Verb-Noun naming convention (Get-Process, Set-Item, New-ADUser) which makes discovery intuitive once you learn the pattern.
What is the difference between PowerShell and CMD?
CMD is a legacy text-based command interpreter with limited scripting. PowerShell is a modern object-oriented shell and scripting language built on .NET. PowerShell handles complex data types, remote management, module imports, error handling, and integrates with APIs. CMD is still useful for quick one-liners but PowerShell replaces it for all serious administration.
Can I use PowerShell on Linux?
Yes. PowerShell 7 (the successor to PowerShell Core 6) runs on Linux, macOS, and Windows. Install via your package manager (apt, yum) or the official Microsoft repository. While Linux-native tools (bash, awk, sed) are typically preferred on Linux, cross-platform PowerShell is useful for managing Azure, Microsoft 365, and hybrid environments from a Linux workstation.
Developer tees & hoodies
It works on my machine.
Tees £19.99 · Hoodies £39.99 · 10% off any 2+ · Free UK shipping over £50

ReadTheManual is run, written and curated by Eric Lonsdale.
Eric has over 20 years of professional experience in IT infrastructure, cloud architecture, and cybersecurity, but started with PCs long before that.
He built his first machine from parts bought off tables at the local college campus, hoping they worked. He learned on BBC Micros and Atari units in the early 90s, and has built almost every PC he’s used between 1995 and now.
From helpdesk to infrastructure architect, Eric has worked across enterprise datacentres, Azure environments, and security operations. He’s managed teams, trained engineers, and spent two decades solving the problems this site teaches you to solve.
ReadTheManual exists because Eric believes the best way to learn IT is to build things, break things, and actually read the manual. Every guide on this site runs on infrastructure he owns and maintains.
Enjoyed this guide?
New articles on Linux, homelab, cloud, and automation every 2 days. No spam, unsubscribe anytime.





