Conditional branching controls the flow of script execution by evaluating logical expressions and running specific code blocks based on whether conditions evaluate to $true or $false. PowerShell provides structured conditional statements, including if, elseif, else, and switch. These replace unstructured jump mechanisms (like Batch's goto) with readable, maintainable control flow. PowerShell 7 also adds a compact ternary operator for simple inline assignments.
The if Statement
The if statement evaluates a conditional test. If the condition is true, the script block within the curly braces ({}) executes.
$temperature = 85
if ($temperature -gt 80) {
Write-Output "It's hot outside."
}where:
- The condition
$temperature -gt 80is enclosed in parentheses. -gtis the greater than comparison operator.- The code block in
{ }runs only if the condition is$true.
./if-statement.ps1It's hot outside.if / elseif / else
Chain additional conditions using elseif, and provide a fallback with else.
Basic Syntax
if (<condition1>) {
}
elseif (<condition2>) {
}
else {
}Condition Evaluation
Unlike languages that rely on comparison symbols (==, >, <), PowerShell uses hyphenated comparison operators such as -gt, -ge, -lt, -le, -eq, -ne.
$score = 72
if ($score -ge 90) {
Write-Output "Grade: A"
} elseif ($score -ge 80) {
Write-Output "Grade: B"
} elseif ($score -ge 70) {
Write-Output "Grade: C"
} else {
Write-Output "Grade: F"
}./if-elseif-else.ps1Grade: Cwhere:
- Conditions are evaluated top to bottom; the first one that evaluates to
$trueruns, and the rest are skipped. elsecatches any case not matched by the preceding conditions.
A CPU-utilization example:
$CpuLoad = 85
if ($CpuLoad -ge 90) {
Write-Warning "Critical CPU utilization: $CpuLoad%"
}
elseif ($CpuLoad -ge 75) {
Write-Warning "Elevated CPU utilization: $CpuLoad%"
}
else {
Write-Output "CPU utilization normal: $CpuLoad%"
}pwsh ./evaluate-numbers.ps1WARNING: Elevated CPU utilization: 85%Nested if Statements
An if statement can be placed inside another if block to test additional conditions only when the outer condition is met.
$isLoggedIn = $true
$role = "Admin"
if ($isLoggedIn) {
if ($role -eq "Admin") {
Write-Output "Access granted: full admin dashboard."
} else {
Write-Output "Access granted: standard user dashboard."
}
} else {
Write-Output "Access denied: please log in."
}Access granted: full admin dashboard.Evaluating Variables and System State
PowerShell streamlines conditional checks that required specialized keywords in legacy shells like Batch (IF DEFINED, IF EXIST, IF ERRORLEVEL).
Checking for $null or Empty
$value = $null
if ($null -eq $value) {
Write-Output "Value is null."
}Value is null.Checking Integer Variables
$count = 0
if ($count -eq 0) {
Write-Output "Count is zero."
}Count is zero.Checking String Variables
$username = ""
if ([string]::IsNullOrEmpty($username)) {
Write-Output "Username is empty."
}Username is empty.where [string]::IsNullOrEmpty() is a static .NET method that safely checks for both $null and an empty string ("") in a single call.
Variable Verification vs. Batch IF DEFINED
To check whether a variable is assigned and contains data, compare it against $null:
if ($null -ne $DeploymentPath) {
Write-Output "Target path set: $DeploymentPath"
} else {
Write-Output "DeploymentPath variable is unassigned."
}To test whether a variable identifier exists in memory at all (even if assigned $null), query the variable provider:
if (Test-Path -Path "Variable:DeploymentPath") {
Write-Output "Variable exists in current scope."
}Checking Command-Line Arguments
param(
[string]$Name
)
if ([string]::IsNullOrEmpty($Name)) {
Write-Output "No name provided."
} else {
Write-Output "Hello, $Name!"
}./check-args.ps1 -Name "Tom"Hello, Tom!Filesystem Verification vs. Batch IF EXIST
File and directory validation is handled directly by the Test-Path cmdlet:
if (Test-Path -Path "C:\Configs\settings.json" -PathType Leaf) {
Write-Output "Configuration file found."
} else {
Write-Error "Required settings file is missing."
}where -PathType Leaf ensures the target is a file rather than a directory container.
A simpler example:
if (Test-Path "C:\Logs\app.log") {
Write-Output "Log file exists."
} else {
Write-Output "Log file not found."
}Log file exists.Execution Results vs. Batch IF ERRORLEVEL
To check whether a previous command executed successfully or failed:
$?: Contains$trueif the immediately preceding PowerShell command succeeded;$falseif it generated an error.$LASTEXITCODE: Stores the integer exit code of the last native external CLI executable run (0normally indicates success; non-zero indicates an error).
git status 2>$null
if ($LASTEXITCODE -ne 0) {
Write-Warning "Directory is not a tracked Git repository."
}The Absence of goto
Legacy batch files rely heavily on :label and goto label jumps to create loops and conditionals. PowerShell does not have a goto statement. Instead, control flow is structured using functions, conditional blocks (if/else), and loop constructs (foreach, while, break, continue).
Batch-to-PowerShell Equivalents Table
| Batch Concept | PowerShell Equivalent |
|---|---|
if defined VAR |
if (Test-Path variable:VAR) or if ($null -ne $VAR) |
if exist file.txt |
if (Test-Path "file.txt") |
if errorlevel 1 |
if ($LASTEXITCODE -ne 0) or checking $? |
goto :label |
Not used in PowerShell. Use functions, loops, or switch instead |
The switch Statement
The switch statement evaluates an expression against multiple matching criteria. It is simpler and more readable than writing repetitive if-elseif-else chains.
Basic Syntax
$day = "Wed"
switch ($day) {
"Mon" { Write-Output "Start of the week" }
"Wed" { Write-Output "Midweek" }
"Fri" { Write-Output "Almost weekend" }
default { Write-Output "Just another day" }
}where:
- Each label (
"Mon","Wed", etc.) is compared against$dayin order. defaultruns only if none of the preceding labels match, equivalent toelse.
./switch-basic.ps1MidweekA second example using explicit break statements:
$Environment = "Staging"
switch ($Environment) {
"Development" { Write-Output "Deploying to DEV cluster."; break }
"Staging" { Write-Output "Deploying to STAGING cluster."; break }
"Production" { Write-Output "Deploying to PROD cluster."; break }
Default { Write-Output "Unknown environment specified."; break }
}where:
Defaultexecutes if none of the explicit conditions match.breakstops further condition evaluation.
switch with Wildcards and Regex
The switch statement supports pattern matching modes using the -Wildcard and -Regex parameters, as well as direct file reading with -File.
Wildcard Matching (-Wildcard)
The -Wildcard parameter allows matching strings using standard shell wildcards (*, ?):
$file = "report_2026.csv"
switch -Wildcard ($file) {
"*.csv" { Write-Output "CSV file detected." }
"*.txt" { Write-Output "Text file detected." }
default { Write-Output "Unknown file type." }
}CSV file detected.A second example demonstrating default fallthrough (no break):
$FileName = "report-2026-q3.csv"
switch -Wildcard ($FileName) {
"*.log" { Write-Output "Target is a system log file." }
"report-*" { Write-Output "Target is an analytical report." }
"*.csv" { Write-Output "Target is a CSV spreadsheet." }
}Target is an analytical report.
Target is a CSV spreadsheet.Note that both blocks executed because no break statement was used.
Regex Matching (-Regex)
The -Regex parameter evaluates patterns using regular expressions and automatically populates the $Matches automatic variable for each matching clause.
$input = "Error Code: 404"
switch -Regex ($input) {
"\d{3}" { Write-Output "Contains a 3-digit code." }
"Error" { Write-Output "This is an error message." }
}where:
-Regexenables full regular expression pattern matching for each case label.- Because
switchevaluates every case by default, both cases above match and both output lines execute.
Contains a 3-digit code.
This is an error message.A more advanced example using named capture groups:
$ServerIdentifier = "LON-DB-PROD-01"
switch -Regex ($ServerIdentifier) {
"^(?<Region>[A-Z]{3})-(?<Role>[A-Z]+)-(?<Env>[A-Z]+)-\d+$" {
[PSCustomObject]@{
Region = $Matches.Region
Role = $Matches.Role
Env = $Matches.Env
}
break
}
Default {
Write-Warning "Server string does not match standard corporate naming pattern."
}
}Region Role Env
------ ---- ---
LON DB PRODReading Files Directly (-File)
Using the -File parameter, switch reads a file line by line without needing Get-Content, testing each line against the conditions:
switch -Regex -File "C:\Logs\app.log" {
"FATAL" { Write-Error "Fatal line detected: $_" }
"WARN" { Write-Warning "Warning line detected: $_" }
}The Ternary Operator (PowerShell 7+)
PowerShell 7 introduced the ternary operator (?:) as a compact shorthand for simple if/else assignments.
Syntax:
<condition> ? <if-true> : <if-false>$age = 20
$status = $age -ge 18 ? "Adult" : "Minor"
Write-Output $statusAdult$ServiceRunning = $true
$HostStatus = $ServiceRunning ? "Online" : "Offline"
$HostStatusOnlinewhere:
- The condition (
$age -ge 18or$ServiceRunning) is evaluated first. - If
$true, the value before:is returned; if$false, the value after:is returned.
In Windows PowerShell 5.1, you can achieve the same inline assignment using subexpression conditionals:
$HostStatus = if ($ServiceRunning) { "Online" } else { "Offline" }Practical Example: Deployment Target Validator
The following script accepts target parameters, evaluates disk capacity, verifies local prerequisites using Test-Path, and routes deployment actions using switch:
param (
[string]$NodeRole = "Web",
[string]$ConfigPath = "C:\App\appsettings.json"
)
if (-not (Test-Path -Path $ConfigPath)) {
Write-Warning "Configuration not found at $ConfigPath. Creating default baseline."
$ConfigValid = $false
} else {
$ConfigValid = $true
}
$DeploymentAction = switch ($NodeRole) {
"Web" { "Deploying ASP.NET Core Runtime and Kestrel Bindings" }
"DB" { "Initializing SQL Server instances and storage mounts" }
"Cache" { "Configuring Redis memory limits" }
Default { "Unsupported role: '$NodeRole'. Terminating." }
}
[PSCustomObject]@{
RoleTested = $NodeRole
ConfigPresent = $ConfigValid
ActionPlan = $DeploymentAction
EvaluatedByUser = $env:USERNAME
Timestamp = (Get-Date -Format "yyyy-MM-dd HH:mm:ss")
}where:
Test-Pathchecks whether the specified config file exists.- The
switchstatement assigns a deployment strategy directly to$DeploymentAction. [PSCustomObject]aggregates the resulting configuration into a structured record.
Execute the script from the console:
pwsh ./validate-deployment-node.ps1 -NodeRole "Web" -ConfigPath "C:\Missing\appsettings.json"WARNING: Configuration not found at C:\Missing\appsettings.json. Creating default baseline.
RoleTested : Web
ConfigPresent : False
ActionPlan : Deploying ASP.NET Core Runtime and Kestrel Bindings
EvaluatedByUser : Tom
Timestamp : 2026-09-22 08:40:24