Module 15 Lesson 1 of 6 🕑 ~55 min

> cat module-15-1-core-language.md

Core Language

The building blocks every PowerShell script is made of: the pipeline, variables, strings, conditional logic, loops, and functions. Get comfortable with these six and the rest of this module is really just applying them to different problems.

1 The Pipeline

The pipeline operator | sends the output of one command into the next:

Get-Service | Where-Object Status -eq "Running"

Meaning: get all services → send them to Where-Object → only return the ones whose status is Running. A more elaborate chain:

Get-Process |
Where-Object CPU -gt 100 |
Sort-Object CPU -Descending

This finds processes that have consumed significant CPU time and sorts them by usage. Select-Object picks specific properties (Get-Service | Select-Object Name, Status); Where-Object filters (Get-Service | Where-Object {$_.Status -eq "Stopped"}). The special variable $_ means "the current object moving through the pipeline" — you'll see it constantly in real scripts, since it's how each command in a pipeline refers to whatever the previous command just handed it.

2 Variables

PowerShell variables begin with $: $name = "Alice", then Write-Host $name returns Alice.

Strings
$Username = "jsmith"
Integers
$Retries = 3
Booleans
$Enabled = $true or $Enabled = $false
Arrays
$Servers = @("SERVER01", "SERVER02", "SERVER03") — access one element with $Servers[0], which returns SERVER01.
Hash tables
Key/value pairs: $User = @{ Username = "jsmith"; Department = "Finance"; Location = "London" }. Retrieve a value with $User["Department"] or $User.Department.

PowerShell can also read operating-system environment variables — $env:USERNAME, $env:COMPUTERNAME, $env:PATH — extremely useful for scripts that need to adapt to whichever user or computer they're actually running on. Useful built-in variables include $PSVersionTable (and specifically $PSVersionTable.PSVersion to check which PowerShell you're running — relevant given Section 1 of the hub's Windows PowerShell vs PowerShell 7 distinction), plus $HOME, $PWD, $LASTEXITCODE, $Error, and $?.

3 String Handling

Combining strings: $FirstName = "Alice"; $LastName = "Smith"; $FullName = "$FirstName $LastName" produces Alice Smith.

🔮 Predict first

$name = "Alice". What does Write-Host "Hello $name" print, versus Write-Host 'Hello $name'?

Reveal the answer

Double quotes print Hello Alice — they expand variables. Single quotes print Hello $name literally — they normally don't. This distinction prevents a genuinely common class of scripting mistake, especially in longer strings where a variable silently fails to expand because someone used single quotes without realising it.

4 Conditional Logic

Automation constantly needs to make decisions, using if/elseif/else:

$Service = Get-Service -Name Spooler

if ($Service.Status -eq "Running") {
    Write-Host "Service is running"
}
else {
    Write-Host "Service is stopped"
}

An enterprise example — monitoring an authentication service:

$Service = Get-Service -Name "AuthenticationService"

if ($Service.Status -ne "Running") {
    Write-Host "WARNING: Authentication service is not running"
}

A mature version of this might then write an event, create an alert, restart the service, send telemetry, or open a ticket. Comparison operators: -eq equal, -ne not equal, -gt/-lt greater/less than, -ge/-le greater/less-or-equal, -like wildcard matching, -match regular expression matching. Logical operators: -and, -or, -not — e.g. if (($Department -eq "Finance") -and ($Enabled -eq $true)) { Write-Host "Active Finance user" }.

5 Loops

Loops repeat an operation — this is where automation starts getting genuinely powerful. Told to disable 500 contractor accounts, doing it manually could take hours; a loop can do it in seconds.

foreach is the most common loop:

$Servers = @("SERVER01", "SERVER02", "SERVER03")

foreach ($Server in $Servers) {
    Write-Host "Checking $Server"
}

A practical version tests connectivity to real servers:

$Servers = @("DC01", "WEB01", "SQL01")

foreach ($Server in $Servers) {
    Test-Connection $Server -Count 1
}

ForEach-Object is the pipeline equivalent: Get-Service | ForEach-Object { Write-Host $_.Name } — again using $_ for the current object. A traditional for loop also exists: for ($i = 1; $i -le 10; $i++) { Write-Host "Iteration $i" }, as does while: $Counter = 1; while ($Counter -le 5) { Write-Host $Counter; $Counter++ }. Be careful — a badly written loop can become infinite, and an infinite loop in a script that's also making changes is a genuinely dangerous combination.

6 Functions

Functions package reusable code. Without them, scripts quickly become repetitive:

function Test-Server {
    param(
        $ComputerName
    )
    Test-Connection $ComputerName -Count 1
}

Test-Server -ComputerName "SERVER01"

Enterprise scripts should use typed, mandatory parameters where appropriate:

function Test-Server {
    param(
        [Parameter(Mandatory)]
        [string]$ComputerName
    )
    Test-Connection -ComputerName $ComputerName -Count 1
}

This makes scripts easier to understand, easier to maintain, harder to misuse, and more suitable for larger teams. A slightly richer example:

function Get-ServiceHealth {
    param(
        [string]$ServiceName
    )
    $Service = Get-Service -Name $ServiceName
    if ($Service.Status -eq "Running") {
        Write-Output "$ServiceName is healthy"
    }
    else {
        Write-Output "$ServiceName is not running"
    }
}

Get-ServiceHealth -ServiceName "Spooler"

Imagine a cybersecurity team repeatedly checking an API, an authentication service, a certificate, an event log, disk space, and network connectivity. Instead of duplicating those commands throughout a huge script, functions like Test-API, Test-Service, Test-Certificate, Test-EventLog, Test-DiskSpace and Test-Network keep the automation far easier to maintain — and this is exactly the pattern Lesson 6's toolkit-building lab asks you to build for real.

Lesson Outcome

You should now be able to build a pipeline chain with Where-Object/Select-Object and understand $_, create and use strings/integers/booleans/arrays/hash tables, explain the double-vs-single-quote distinction, write if/elseif/else logic with comparison and logical operators, write foreach/for/while loops, and package reusable logic into functions with typed parameters. Lesson 2 puts these to work on real data — files, CSV, and JSON.