BeginnerLesson 4 of 16

Control Flow, Loops, and Conditions

Control which code runs and how many times: use if/elseif/else for decisions, switch for multi-branch logic, and for/foreach/while loops to process collections and repeat operations automatically.

🧒 Simple Explanation (ELI5)

Imagine you are sorting packages at a warehouse: if the package is fragile, put it on the gentle shelf; if it is heavy, get help; otherwise handle it normally. That is an if statement. Loops are like repeatedly picking up the next package in the queue until the queue is empty. Automation scripts make the same kind of decisions and repetitions, just for servers and files instead of boxes.

🔧 Why Do We Need It?

⚙️ Technical Explanation

if / elseif / else: evaluates a boolean condition and executes the matching block. Conditions use comparison and logical operators from the previous lesson.

switch: matches a value against multiple patterns. PowerShell switch supports regex matching (-Regex), wildcard matching (-Wildcard), and file mode (-File). The default block runs when no case matches.

foreach (loop keyword): enumerates a collection with a named variable. ForEach-Object (cmdlet): streams pipeline objects. Both serve similar purposes but foreach loops load the collection into memory first and are better for index access and break/continue control.

for: classic index-based loop. while: continues while a condition is true—test before first iteration. do-while: always executes at least once—test after first iteration.

♾️
Avoiding Infinite Loops in Automation

While loops in production automation scripts must always have a maximum iteration count or a timeout guard. A script polling for a service to start can loop forever if the service never responds. Pattern: $attempts = 0; while ($attempts -lt 30 -and (Get-Service xyz).Status -ne 'Running') { Start-Sleep 5; $attempts++ }. If it exits because of the count limit, throw an error—never silently continue.

🔀
Switch vs if/elseif

Use switch when you are branching on the same single variable against multiple explicit values (status codes, environment names, exit codes). Use if/elseif when your conditions are complex expressions or involve multiple variables. A switch with 5 cases is far more readable than 5 elseif blocks on the same variable.

📊 Visual Representation

Control Flow Decision Tree
if ($env -eq 'prod')
→ true
Apply prod config
→ false → elseif ($env -eq 'staging')
→ true
Apply staging config
→ else
Apply dev config

⌨️ Commands / Syntax

powershell
# if / elseif / else
$diskPct = 85
if ($diskPct -ge 90) {
    Write-Warning "CRITICAL: disk above 90%"
} elseif ($diskPct -ge 80) {
    Write-Warning "WARNING: disk above 80%"
} else {
    Write-Host "Disk OK: $diskPct%"
}

# switch statement
$environment = "staging"
switch ($environment) {
    "dev"     { $rg = "rg-dev";     $sku = "Standard_B1s" }
    "staging" { $rg = "rg-staging"; $sku = "Standard_D2s_v3" }
    "prod"    { $rg = "rg-prod";    $sku = "Standard_D4s_v3" }
    default   { throw "Unknown environment: $environment" }
}

# foreach loop over a collection
$servers = @("web01", "web02", "web03")
foreach ($server in $servers) {
    Write-Host "Checking $server..."
    $svc = Get-Service -ComputerName $server -Name "wuauserv" -ErrorAction SilentlyContinue
    if ($svc -and $svc.Status -eq "Stopped") {
        Write-Warning "$server: Windows Update service is stopped"
    }
}

# for loop (index-based)
for ($i = 0; $i -lt $servers.Count; $i++) {
    Write-Host "Server $($i+1): $($servers[$i])"
}

# while loop with guard
$attempts = 0
$maxAttempts = 12
while ($attempts -lt $maxAttempts) {
    $status = (Get-Service -Name "AppServiceProd").Status
    if ($status -eq "Running") {
        Write-Host "Service is running after $attempts checks"
        break
    }
    Start-Sleep -Seconds 5
    $attempts++
}
if ($attempts -ge $maxAttempts) {
    throw "Service did not start within $($maxAttempts * 5) seconds"
}

# break and continue
foreach ($file in Get-ChildItem C:\Logs -Filter *.log) {
    if ($file.Length -eq 0) { continue }  # skip empty files
    if ($file.Name -like "CRITICAL*") { break }  # stop on critical
    Process-LogFile $file
}

💼 Example (Real-world Use Case)

A deployment health-check script iterates over a list of web server hostnames, checks whether the IIS service is running, and builds a report hashtable: healthy servers accumulate in one list, unhealthy ones in another. After the loop, the script emails the report if unhealthy count is above zero, and exits with a non-zero return code to fail the CI/CD pipeline gate.

🧪 Hands-on

  1. Write an if/else that writes "large" if a number is above 100, "small" otherwise.
  2. Write a switch statement for three environment names (dev/staging/prod) that assigns a resource group name.
  3. Loop over an array of five server names with foreach and print each one with a counter: "Server 1: web01".
  4. Write a while loop that counts to 5 with a 200ms sleep between iterations.
  5. Add a continue statement inside a foreach to skip any server whose name contains "decom".
🎮
Try It Yourself

Write a script that checks disk usage on your local drives using Get-PSDrive -PSProvider FileSystem and categorizes each drive as "OK", "Warning" (above 70% used), or "Critical" (above 90% used). Hint: calculate percentage as ($drive.Used / ($drive.Used + $drive.Free)) * 100. Use if/elseif/else inside a foreach loop over the drives.

🐛 Debugging Scenario

Problem: a while loop that polls for a deployment to complete runs forever and the pipeline hangs.

🎯 Interview Questions

Beginner

What is the syntax for an if/else statement in PowerShell?

if (condition) { ... } elseif (condition) { ... } else { ... }. Conditions use PowerShell comparison operators (-eq, -gt, -like, etc.) and logical connectors (-and, -or, -not).

What is the difference between foreach (keyword) and ForEach-Object (cmdlet)?

The foreach keyword enumerates a collection that is already in memory. ForEach-Object is a pipeline cmdlet that processes objects one at a time as they stream. For large datasets, ForEach-Object is more memory-efficient. The foreach keyword supports break and continue; ForEach-Object does not support break the same way.

When would you use a switch statement instead of if/elseif?

When branching on the same single variable against multiple known explicit values (environment names, service states, exit codes). switch is cleaner, easier to read, and supports regex and wildcard matching modes.

What does break do inside a loop?

break immediately exits the innermost enclosing loop, skipping any remaining iterations. It is used to stop processing once a condition is met—for example, stopping when a service is found running or when a critical file is encountered.

What is the difference between while and do-while?

while tests its condition before executing the body—it may execute zero times if the condition starts false. do-while executes the body once before testing, so it always runs at least once.

Scenario-based

A CI/CD script polls a deployment API to check if the deployment is complete, but it hangs forever on a failed deployment. How do you fix it?

Add a maximum attempt count and a timeout guard: check attempts against a max value alongside checking the success condition. When the limit is reached, throw an explicit error rather than silently exiting. Add the deployment's error response to the exception message so the pipeline failure log is actionable.

You need to process 200 log files but skip any file smaller than 1KB and stop completely if you find a file named STOP-PROCESSING.flag. How do you implement this?

Use foreach over Get-ChildItem results: check the Name for the stop flag file first inside the loop and call break if found. Check the Length and call continue for files below 1KB. This gives you clean early-stop and skip behavior without nested if logic.

🌐 Real-world Usage

Deployment scripts use switch on environment parameters to select Azure resource group names and VM sizes. Service health-check scripts use foreach to iterate over server lists and while loops to wait for services to start after deployments. Log processing scripts use loops with continue to skip empty files and break on fatal error indicators.

📝 Summary

Use if/elseif/else for conditional branches and switch for multi-value branching on a single variable. Foreach loops iterate over known collections; while loops continue based on a condition—always guard them with a maximum count. Use break to exit early and continue to skip an iteration. Control flow is the foundation of every non-trivial automation script.