Skip to content

PowerShell For Loop

How to use PowerShell's for loop with initialization, condition, and increment steps for precise iteration control over arrays, ranges, or custom counters.

The for loop is an index-driven control flow structure that repeats a block of code a specific number of times. Commonly referred to as the "classic" or three-part for loop, it initializes a control variable, tests a conditional expression before each pass, and executes an increment or step statement after each iteration. It's the preferred choice when the number of iterations is known in advance and you need direct control over the counter variable.

The Classic for Loop Syntax

The for loop statement header consists of three sections separated by semicolons (;), followed by an executable script block:

Syntax Structure

for (<Init>; <Condition>; <Repeat>) {
}

The loop operates in four sequential phases:

  1. <Init>: Executes once before the loop starts. Typically used to declare and initialize a loop control variable (e.g., $i = 0).
  2. <Condition>: Evaluated before each iteration. If it evaluates to $true, the loop body executes. If it evaluates to $false, the loop terminates.
  3. Loop Body ({ ... }): Executes the enclosed statements.
  4. <Repeat>: Executes after each iteration (e.g., $i++), before returning to step 2 to re-evaluate the condition.
       +------------------+
       |   1. <Init>      | (Runs once at startup)
       +--------+---------+
                |
                v
       +------------------+
+----->|  2. <Condition>  |
|      +--------+---------+
|               |
|      ($true)  |  ($false)
|         +-----+-----+
|         |           |
|         v           v
|  +------------+  +---------------------+
|  |  Loop Body |  | Exit loop & proceed |
|  +------+-----+  +---------------------+
|         |
|         v
|  +------------+
+--+ 3. <Repeat>| (e.g., $i++)
   +------------+
for-basic-counter.ps1powershell
for ($i = 0; $i -lt 5; $i++) {
    Write-Output "Processing iteration index: $i"
}

where:

  • $i = 0 initializes the index variable $i to 0.
  • $i -lt 5 evaluates whether $i is strictly less than 5 before entering the script block.
  • Write-Output emits the status string to the pipeline.
  • $i++ uses the postfix increment operator to add 1 to $i at the end of each pass.
Consolepowershell
pwsh ./for-basic-counter.ps1
Outputplaintext
Processing iteration index: 0
Processing iteration index: 1
Processing iteration index: 2
Processing iteration index: 3
Processing iteration index: 4

Custom Step Increments

The <Repeat> expression is not restricted to single-unit increments ($i++). You can use any assignment or arithmetic operator to adjust the step size or iterate in reverse.

for-step.ps1powershell
for ($i = 0; $i -le 20; $i += 5) {
    Write-Output "Value: $i"
}
Consolepowershell
./for-step.ps1
Outputplaintext
Value: 0
Value: 5
Value: 10
Value: 15
Value: 20

To skip elements or process items in batches, use compound assignment operators like +=:

for-step-increment.ps1powershell
for ($port = 8000; $port -le 8040; $port += 10) {
    Write-Output "Configuring listener on port: $port"
}
Consolepowershell
pwsh ./for-step-increment.ps1
Outputplaintext
Configuring listener on port: 8000
Configuring listener on port: 8010
Configuring listener on port: 8020
Configuring listener on port: 8030
Configuring listener on port: 8040

Reverse Iteration and Counting Downward

Reversing the initializer, condition, and increment produces a descending loop.

for-countdown.ps1powershell
for ($i = 5; $i -ge 1; $i--) {
    Write-Output "T-minus $i"
}
Write-Output "Liftoff!"
Consolepowershell
./for-countdown.ps1
Outputplaintext
T-minus 5
T-minus 4
T-minus 3
T-minus 2
T-minus 1
Liftoff!

To iterate backwards through an array or run a countdown, initialize the counter to the highest bound, check for a lower bound using -ge, and decrement using --:

for-reverse-countdown.ps1powershell
for ($secondsRemaining = 3; $secondsRemaining -gt 0; $secondsRemaining--) {
    Write-Output "Launch window initializes in: $secondsRemaining seconds..."
}
Write-Output "Window open."
Consolepowershell
pwsh ./for-reverse-countdown.ps1
Outputplaintext
Launch window initializes in: 3 seconds...
Launch window initializes in: 2 seconds...
Launch window initializes in: 1 seconds...
Window open.

Iterating Over an Array by Index

Unlike foreach, a for loop gives direct access to the index, which is useful when you need to know an item's position or compare it with neighboring elements.

for-array-index.ps1powershell
$servers = @("WEB01", "WEB02", "DB01", "DB02")

for ($i = 0; $i -lt $servers.Count; $i++) {
    Write-Output "Server $($i + 1): $($servers[$i])"
}

where:

  • $servers.Count determines the total number of iterations dynamically, based on the array's actual size.
  • $servers[$i] accesses the array element at the current index.
Consolepowershell
./for-array-index.ps1
Outputplaintext
Server 1: WEB01
Server 2: WEB02
Server 3: DB01
Server 4: DB02

Nesting for Loops

for loops can be nested to iterate over multi-dimensional data, such as a grid or matrix.

for-nested.ps1powershell
for ($row = 0; $row -lt 3; $row++) {
    for ($col = 0; $col -lt 3; $col++) {
        Write-Output "Cell [$row,$col]"
    }
}
Consolepowershell
./for-nested.ps1
Outputplaintext
Cell [0,0]
Cell [0,1]
Cell [0,2]
Cell [1,0]
Cell [1,1]
Cell [1,2]
Cell [2,0]
Cell [2,1]
Cell [2,2]

Omitted Clauses and Infinite Loops

All three components of the for statement header are optional:

  • Omitting <Init> assumes the counter is already initialized earlier in the script.
  • Omitting <Repeat> assumes the counter is updated manually inside the loop body.
  • Omitting <Condition> defaults to $true.

Leaving all three clauses empty produces an infinite loop:

Consolepowershell
for (;;) {
    break
}

This syntax is functionally equivalent to while ($true), but while ($true) is generally preferred for script clarity.

Practical Example: Staged Batch Deployment with Progress Tracking

The classic for loop is ideal for iterating across collections when you need direct access to the numeric index to compute completion percentages or synchronize multiple parallel arrays:

staged-node-deployment.ps1powershell
param ()

$ClusterNodes = @("lon-web-01", "lon-web-02", "lon-web-03", "lon-web-04")
$TotalNodes   = $ClusterNodes.Count

$Results = for ($index = 0; $index -lt $TotalNodes; $index++) {
    $CurrentNode = $ClusterNodes[$index]
    
    $PercentComplete = [math]::Round((($index + 1) / $TotalNodes) * 100, 1)

    Start-Sleep -Milliseconds 100

    [PSCustomObject]@{
        StageIndex      = $index + 1
        NodeName        = $CurrentNode
        PercentComplete = "$PercentComplete%"
        DeploymentState = "Provisioned"
        Timestamp       = (Get-Date -Format "HH:mm:ss.fff")
    }
}

$Results | Format-Table -AutoSize

where:

  • $TotalNodes = $ClusterNodes.Count determines the upper array bound.
  • for ($index = 0; $index -lt $TotalNodes; $index++) tracks the array offset from 0 to 3.
  • $ClusterNodes[$index] uses the loop counter to retrieve the target hostname from the array.
  • [math]::Round((($index + 1) / $TotalNodes) * 100, 1) calculates progress completion based on the current step.
  • $Results = for (...) { ... } captures the objects emitted by every iteration directly into an array variable.

Execute the script from the console:

Consolepowershell
pwsh ./staged-node-deployment.ps1
Outputplaintext
StageIndex NodeName   PercentComplete DeploymentState Timestamp
---------- --------   --------------- --------------- ---------
         1 lon-web-01 25%             Provisioned     19:14:10.182
         2 lon-web-02 50%             Provisioned     19:14:10.307
         3 lon-web-03 75%             Provisioned     19:14:10.417
         4 lon-web-04 100%            Provisioned     19:14:10.528
Navigation