Skip to content

Windows

PowerShell Basics for Admins

PowerShell is the automation and configuration shell built into Windows Server. Unlike a traditional text shell, it passes objects through the pipeline, which makes it far more reliable for administration than parsing screen output.

Tested on

Windows Server 2022 with Windows PowerShell 5.1 (preinstalled). The same cmdlets work in PowerShell 7.x (the cross-platform successor, installed separately) unless noted. Cross-platform PowerShell 7 also runs on Linux.

What is PowerShell, and why does it matter?

PowerShell is two things at once: an interactive command shell and a scripting language. The thing that sets it apart from cmd.exe or a Unix shell is that the pipeline carries .NET objects, not text.

In a text shell you run a command, get a block of text, then chop it up with tools like grep, awk, and cut - and your script breaks the moment the output format changes. In PowerShell, Get-Process hands the next command real objects with named properties (Name, Id, CPU, WorkingSet), so you select and filter on properties, never on column positions.

# Objects in the pipeline: filter on a real property, no text parsing
Get-Process | Where-Object CPU -gt 100 | Select-Object Name, Id, CPU

This is why PowerShell is the backbone of Windows automation - the same skills apply whether you manage Active Directory, IIS, or file shares.

Cmdlets: the Verb-Noun naming model

The native commands in PowerShell are cmdlets (pronounced "command-lets"), and they follow a strict Verb-Noun convention:

Cmdlet Verb Noun Does
Get-Service Get Service Reads services
Stop-Service Stop Service Stops a service
New-Item New Item Creates a file/folder
Set-Location Set Location Changes directory

Approved verbs are standardized (Get, Set, New, Remove, Start, Stop, Restart, Add, ...). Once you learn the pattern you can often guess a cmdlet name. See the full list with Get-Verb.

Aliases for muscle memory

Many cmdlets have short aliases, including some that mirror cmd/Unix: ls/dir -> Get-ChildItem, cd -> Set-Location, cat -> Get-Content, ps -> Get-Process. Use them interactively, but write full cmdlet names in scripts for readability.

The pipeline passes objects (not text)

The pipe character | sends the output objects of one cmdlet as input objects to the next. Because the objects keep their structure, downstream cmdlets bind to properties by name.

# Stop every service whose name starts with "Print", confirming each
Get-Service -Name 'Print*' | Stop-Service -WhatIf

-WhatIf previews what would happen without doing it - a habit worth keeping for any destructive command.

Discovery: find and learn cmdlets

You never need to memorize everything. Three cmdlets let you explore:

# Find cmdlets - by verb, noun, or wildcard
Get-Command -Verb Get -Noun Service
Get-Command *event*

# Read help (add -Examples or -Full); -Online opens the web docs
Get-Help Get-Service -Examples
Update-Help                      # download the latest help text once

# Inspect the OBJECT a cmdlet returns - its properties and methods
Get-Service | Get-Member

Get-Member is the most underrated tool: it tells you exactly which properties you can Select-Object or Where-Object on, because it shows the object's real shape.

Common cmdlets every admin uses

# Services and processes
Get-Service -Name Spooler
Get-Process -Name explorer

# Files and folders (the filesystem is a "provider")
Get-ChildItem C:\Logs -Recurse -Filter *.log
Set-Location C:\Logs

# The event logs - prefer the modern Get-WinEvent
Get-WinEvent -LogName System -MaxEvents 20
Get-WinEvent -FilterHashtable @{ LogName = 'System'; Level = 2 } -MaxEvents 50

# Get-EventLog is the older, classic-log-only cmdlet (still works on 2022)
Get-EventLog -LogName System -Newest 20

Get-WinEvent vs Get-EventLog

Get-EventLog only reads the classic logs (Application, System, Security). Get-WinEvent reads all logs, including the newer "Applications and Services" logs, and is much faster with server-side filtering via -FilterHashtable. Use Get-WinEvent for new work.

Filtering, selecting, sorting, formatting

These four cmdlets cover most day-to-day data shaping:

# Where-Object: keep only matching objects
Get-Service | Where-Object Status -eq 'Running'

# Select-Object: pick properties (or the first/last N)
Get-Process | Select-Object Name, Id, WorkingSet -First 5

# Sort-Object: order by a property
Get-Process | Sort-Object WorkingSet -Descending | Select-Object -First 5

# Format-Table / Format-List: presentation ONLY, always last in the pipe
Get-Service | Where-Object Status -eq 'Running' | Format-Table Name, DisplayName -AutoSize

Format-* breaks the pipeline

Format-Table and Format-List produce formatting objects, not data. Put them at the end of a pipeline only. Never pipe their output into Where-Object, Export-Csv, etc. - you will lose the real objects. Use Export-Csv / ConvertTo-Json before any Format-*.

Comparison operators are words, not symbols: -eq, -ne, -gt, -lt, -ge, -le, -like (wildcards), -match (regex), -contains.

Variables and basic scripting

Variables start with $. PowerShell is loosely typed but you can pin a type.

$name    = 'Spooler'
$svc     = Get-Service -Name $name
[int]$max = 5

if / foreach

$svc = Get-Service -Name Spooler
if ($svc.Status -eq 'Running') {
    Write-Output "$($svc.Name) is up"
} else {
    Start-Service $svc.Name
}

# Loop over a collection of objects
foreach ($s in Get-Service | Where-Object Status -eq 'Stopped') {
    Write-Output "Stopped: $($s.Name)"
}

Save scripts as .ps1 files and run them with .\script.ps1. Comment lines start with #; block comments use <# ... #>.

Execution policy

By default Windows Server may refuse to run scripts. The execution policy is a safety setting (not a security boundary) that controls which scripts can run.

# See the current policy
Get-ExecutionPolicy -List

# Allow local scripts; downloaded scripts must be signed (sensible default)
Set-ExecutionPolicy -Scope CurrentUser RemoteSigned

Run PowerShell as Administrator to set a machine-wide (LocalMachine) scope. RemoteSigned is the common, balanced choice; avoid Unrestricted/Bypass except for short, deliberate troubleshooting.

Remoting: run commands on other servers

PowerShell Remoting (over WS-Management / WinRM) lets you manage servers without RDP. On most Windows Server installs WinRM is already enabled; if not, run Enable-PSRemoting -Force on the target.

# Interactive session ON a remote server (like SSH-ing in)
Enter-PSSession -ComputerName SERVER01 -Credential CORP\admin
# ... run cmdlets as if local ...
Exit-PSSession

# One-off command on one or many servers (runs in parallel)
Invoke-Command -ComputerName SERVER01, SERVER02 -ScriptBlock {
    Get-Service -Name Spooler
}

# Reusable session for several commands
$s = New-PSSession -ComputerName SERVER01
Invoke-Command -Session $s -ScriptBlock { Get-Process }
Remove-PSSession $s

Invoke-Command returns real objects from each machine, tagged with a PSComputerName property so you know which server each result came from.

PowerShell vs Bash

If you also work on Linux, the mental model is different. See the Linux Bash scripting page for the other side.

Concept PowerShell Bash
Pipeline carries Objects (.NET) with properties Text (byte streams)
Filtering Where-Object Status -eq 'Running' grep, awk, cut on text
Selecting fields Select-Object Name, Id (by property) awk '{print $1}' (by column)
Command naming Verb-Noun (Get-Service) freeform (systemctl, ls)
Variables $x = 'v' x=v (no spaces)
Comparison -eq, -gt, -like -eq/==, -gt, [[ ... ]]
Elevation Run shell as Administrator sudo per command
Remote exec Invoke-Command (WinRM) ssh host 'cmd'
Help Get-Help, Get-Member man, --help

The big takeaway: in Bash you parse text; in PowerShell you query objects.

Verify your work

  1. Run Get-Command -Verb Get -Noun Service and confirm Get-Service is listed.
  2. Run Get-Service | Get-Member and confirm you can see a Status property.
  3. Run Get-Service | Where-Object Status -eq 'Running' | Select-Object -First 3 and confirm only running services are returned with the columns you asked for.
  4. Run Get-ExecutionPolicy -List and confirm CurrentUser shows RemoteSigned after setting it.
  5. If you have a second server, run Invoke-Command -ComputerName SERVER01 -ScriptBlock { hostname } and confirm it returns that server's name.

Summary

  • PowerShell is a shell and a scripting language whose pipeline carries objects, so you filter on properties instead of parsing text.
  • Cmdlets follow Verb-Noun naming; discover them with Get-Command, Get-Help, and Get-Member.
  • Everyday cmdlets: Get-Service, Get-Process, Get-ChildItem, Set-Location, Get-WinEvent (prefer over Get-EventLog).
  • Shape data with Where-Object, Select-Object, Sort-Object, and (last only) Format-Table/Format-List.
  • Use $ variables, if/foreach, set Set-ExecutionPolicy RemoteSigned, and manage remote servers with Enter-PSSession and Invoke-Command.
  • Versus Bash: objects vs text is the key difference. These basics power the rest of the Windows Server track.

Test yourself