PowerShell While and Do-While Loops Explained

PowerShell While and Do-While Loops Explained

PowerShell Tips Editor 4 min read
PowerShell While and Do-While Loops Explained

Some problems can’t be solved with a fixed-count loop — you need to keep going until something changes. Waiting for a service to start. Retrying a failed network call. Reading user input until it’s valid. These are the scenarios where the PowerShell while loop shines. This guide covers the full syntax of while, do-while, and do-until, shows you how to prevent infinite loops, and walks through practical polling and retry patterns used in real automation scripts.

While Loop Syntax

A while loop checks its condition before each iteration and stops as soon as the condition is false:

# Count from 1 to 5
$i = 1
while ($i -le 5) {
    Write-Output "Count: $i"
    $i++
}

# Process a queue until empty
$queue = [System.Collections.Queue]::new()
$queue.Enqueue('task1')
$queue.Enqueue('task2')
$queue.Enqueue('task3')

while ($queue.Count -gt 0) {
    $task = $queue.Dequeue()
    Write-Output "Processing: $task"
}
Count: 1
Count: 2
Count: 3
Count: 4
Count: 5
Processing: task1
Processing: task2
Processing: task3

The condition is evaluated before the body executes. If the condition is false from the start, the body never runs at all. This is the key difference from do-while.

Do-While vs Do-Until

Both do-while and do-until execute the body at least once before checking the condition:

# do-while: keep looping WHILE condition is true
$attempts = 0
do {
    $attempts++
    Write-Output "Attempt: $attempts"
} while ($attempts -lt 3)

# do-until: keep looping UNTIL condition becomes true
$value = 0
do {
    $value += 10
    Write-Output "Value: $value"
} until ($value -ge 30)
Attempt: 1
Attempt: 2
Attempt: 3
Value: 10
Value: 20
Value: 30

do-until is syntactic sugar for a do-while with a negated condition. Use whichever reads more naturally for your scenario. do-until reads well for “keep trying until it works” patterns.

Avoiding Infinite Loops

The most dangerous aspect of while loops is creating one that never terminates. Always ensure:

  1. The loop variable changes inside the body.
  2. There’s a maximum iteration count or timeout as a safety net.
# Safe pattern: always include a max iteration count
$maxTries = 10
$tries    = 0
$success  = $false

while (-not $success -and $tries -lt $maxTries) {
    $tries++
    Write-Output "Try $tries of $maxTries..."

    # Simulate work that may fail
    if ((Get-Random -Minimum 1 -Maximum 5) -eq 1) {
        $success = $true
    }
}

if ($success) {
    Write-Output "Succeeded after $tries tries"
} else {
    Write-Warning "Failed after $maxTries attempts"
}
Try 1 of 10...
Try 2 of 10...
Try 3 of 10...
Succeeded after 3 tries

Building a Retry Loop

Retry logic is one of the most practical uses of while loops in automation scripts:

function Invoke-WithRetry {
    param(
        [scriptblock] $ScriptBlock,
        [int]         $MaxRetries = 3,
        [int]         $DelaySeconds = 5
    )

    $attempt = 0
    while ($attempt -lt $MaxRetries) {
        try {
            & $ScriptBlock
            return   # Success — exit the function
        } catch {
            $attempt++
            Write-Warning "Attempt $attempt failed: $($_.Exception.Message)"
            if ($attempt -lt $MaxRetries) {
                Write-Output "Retrying in $DelaySeconds seconds..."
                Start-Sleep -Seconds $DelaySeconds
            }
        }
    }
    throw "Operation failed after $MaxRetries attempts"
}

# Usage
Invoke-WithRetry -ScriptBlock {
    Invoke-WebRequest -Uri 'https://api.example.com/health' | Out-Null
    Write-Output 'Health check passed'
} -MaxRetries 3 -DelaySeconds 2

Polling a Service Until Ready

Waiting for a Windows service to reach a target state is a classic while loop scenario:

$serviceName  = 'Spooler'
$targetStatus = 'Running'
$timeoutSec   = 60
$pollSec      = 5
$elapsed      = 0

Write-Output "Waiting for $serviceName to reach status: $targetStatus"

while ((Get-Service -Name $serviceName).Status -ne $targetStatus) {
    if ($elapsed -ge $timeoutSec) {
        throw "Timeout: $serviceName did not reach $targetStatus in $timeoutSec seconds"
    }
    Start-Sleep -Seconds $pollSec
    $elapsed += $pollSec
    Write-Output "  Still waiting... ($elapsed/$timeoutSec seconds)"
}

Write-Output "$serviceName is now $targetStatus"
Waiting for Spooler to reach status: Running
Spooler is now Running

Breaking Out with Break

Use break to exit a while loop early when a condition inside the body is met:

$i = 0
while ($true) {
    $i++
    if ($i -eq 5) {
        Write-Output "Breaking at $i"
        break
    }
    Write-Output $i
}
1
2
3
4
Breaking at 5

Common Errors and Fixes

  • Infinite loop due to condition never becoming false: If your loop variable isn’t modified inside the loop body, or if the modification doesn’t affect the condition, the loop runs forever. Press Ctrl + C to terminate. Always trace through your condition logic before running a while loop for the first time.
  • Do-While executes at least once — often a surprise: Even if the initial condition is false, the body of a do-while runs once. This is intentional but can cause unexpected behavior (like making an API call before validating credentials). Use a regular while when the body should not run if the initial condition is false.

Related Cmdlets / See Also

Wrapping Up

Use while when you need to repeat code until an external condition changes. Always include a maximum iteration count or timeout as a safety net. Use do-while or do-until when the body must run at least once. The retry and polling patterns shown here are directly applicable to service management, deployment scripts, and API integrations. Your next step: wrap a flaky network call in the Invoke-WithRetry function and see how much more resilient your script becomes.

Send-Item -To