“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.
PowerShell: The automation engine for Windows infrastructure
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 * | Select-Object Name, Email, 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"
$results = foreach ($server in $servers) {
try {
$service = Get-Service -Name $serviceName -ComputerName $server -ErrorAction Stop
[PSCustomObject]@{
Server = $server
Service = $serviceName
Status = $service.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"
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=yourdomain,DC=local" `
-AccountPassword (ConvertTo-SecureString "Welcome123!" -AsPlainText -Force) `
-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

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.

