PowerShell console window showing cmdlet output

PowerShell Basics for Sysadmins: From Zero to Scripting

“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 information
  • Set – Change something
  • New – Create something
  • Remove – Delete something
  • Start / Stop – Control services/processes
  • Enable / 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

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

Enjoyed this guide?

New articles on Linux, homelab, cloud, and automation every 2 days. No spam, unsubscribe anytime.

Scroll to Top