By Davy Rogers. Use these techniques only in environments you own or have explicit permission to test.

PowerShell: ForEach and Loops

Loops are essential for automating repetitive tasks. PowerShell provides multiple ways to iterate through collections: ForEach-Object, foreach statement, for, while, and do loops. This lesson covers all loop types with practical pentesting examples.

What You'll Learn

  • ForEach-Object cmdlet (pipeline)
  • foreach statement
  • for loop
  • while and do-while loops
  • Where-Object for filtering
  • Breaking and continuing loops
  • Practical automation scenarios

ForEach-Object Cmdlet

Pipeline Processing

# Syntax
Collection | ForEach-Object { Process each item }

# Aliases
ForEach-Object
foreach
%

Basic Usage

# Process each number
1..5 | ForEach-Object { $_ * 2 }
# Output: 2, 4, 6, 8, 10

# $_ represents current item in pipeline
Get-Process | ForEach-Object { $_.Name }

# Multiple commands
Get-ChildItem *.txt | ForEach-Object {
    Write-Host "Processing: $($_.Name)"
    $content = Get-Content $_.FullName
    Write-Host "Lines: $($content.Count)"
}

-Begin, -Process, -End

1..5 | ForEach-Object -Begin {
    Write-Host "Starting..."
    $sum = 0
} -Process {
    $sum += $_
} -End {
    Write-Host "Sum: $sum"
}

foreach Statement

Syntax

foreach ($item in $collection) {
    # Process $item
}

Basic Usage

# Iterate through array
$numbers = 1..5
foreach ($num in $numbers) {
    Write-Host ($num * 2)
}

# Iterate through files
$files = Get-ChildItem *.txt
foreach ($file in $files) {
    Write-Host $file.Name
}

# Process services
$services = Get-Service
foreach ($service in $services) {
    if ($service.Status -eq "Running") {
        Write-Host "$($service.Name) is running"
    }
}

for Loop

Syntax

for ($i = 0; $i -lt 10; $i++) {
    # Process
}

Examples

# Count 0 to 9
for ($i = 0; $i -lt 10; $i++) {
    Write-Host $i
}

# Count backwards
for ($i = 10; $i -gt 0; $i--) {
    Write-Host $i
}

# Step by 2
for ($i = 0; $i -lt 10; $i += 2) {
    Write-Host $i
}

# Iterate with index
$array = @("a", "b", "c")
for ($i = 0; $i -lt $array.Length; $i++) {
    Write-Host "$i: $($array[$i])"
}

while Loop

Syntax

while (condition) {
    # Code
}

Examples

# Count to 5
$i = 1
while ($i -le 5) {
    Write-Host $i
    $i++
}

# Read until empty line
while ($true) {
    $input = Read-Host "Enter text (empty to quit)"
    if ($input -eq "") { break }
    Write-Host "You entered: $input"
}

# Wait for service
while ((Get-Service -Name wuauserv).Status -ne "Running") {
    Write-Host "Waiting for service..."
    Start-Sleep -Seconds 2
}

do-while and do-until Loops

do-while

# Execute at least once, then check condition
$i = 1
do {
    Write-Host $i
    $i++
} while ($i -le 5)

do-until

# Execute until condition is true
$i = 1
do {
    Write-Host $i
    $i++
} until ($i -gt 5)

Where-Object (Filtering)

Syntax

Collection | Where-Object { Condition }

# Aliases
Where-Object
where
?

Examples

# Filter processes
Get-Process | Where-Object { $_.CPU -gt 10 }

# Multiple conditions
Get-Process | Where-Object {
    $_.CPU -gt 10 -and $_.WorkingSet -gt 100MB
}

# Filter files
Get-ChildItem | Where-Object { $_.Length -gt 1MB }

# Simplified syntax (PowerShell 3.0+)
Get-Process | Where-Object CPU -gt 10
Get-ChildItem | Where-Object Length -gt 1MB

Break and Continue

break Statement

# Exit loop entirely
foreach ($i in 1..10) {
    if ($i -eq 5) { break }
    Write-Host $i
}
# Output: 1, 2, 3, 4

continue Statement

# Skip to next iteration
foreach ($i in 1..10) {
    if ($i -eq 5) { continue }
    Write-Host $i
}
# Output: 1, 2, 3, 4, 6, 7, 8, 9, 10

Nested Loops

# Multiplication table
for ($i = 1; $i -le 5; $i++) {
    for ($j = 1; $j -le 5; $j++) {
        Write-Host -NoNewline "$($i * $j)`t"
    }
    Write-Host ""
}

ForEach-Object vs foreach Statement

Performance Comparison

# ForEach-Object (pipeline) - Slower but flexible
1..10000 | ForEach-Object { $_ * 2 }

# foreach statement - Faster
$numbers = 1..10000
foreach ($num in $numbers) { $num * 2 }

When to Use Each

Use ForEach-Object when:

  • Working with pipeline
  • Don't want to store entire collection in memory
  • Need -Begin/-Process/-End blocks

Use foreach statement when:

  • Have collection already in memory
  • Need maximum performance
  • Want simpler syntax

Practical Examples

Example 1: Process Multiple Files

# Rename all .txt files
Get-ChildItem *.txt | ForEach-Object {
    $newName = "backup_" + $_.Name
    Rename-Item $_.FullName $newName
}

Example 2: Check Multiple Servers

$servers = @("server1", "server2", "server3")
foreach ($server in $servers) {
    if (Test-Connection -ComputerName $server -Count 1 -Quiet) {
        Write-Host "$server is online" -ForegroundColor Green
    } else {
        Write-Host "$server is offline" -ForegroundColor Red
    }
}

Example 3: Generate Report

$processes = Get-Process | Where-Object { $_.CPU -gt 10 }
$report = @()

foreach ($proc in $processes) {
    $report += [PSCustomObject]@{
        Name = $proc.Name
        CPU = [math]::Round($proc.CPU, 2)
        Memory = [math]::Round($proc.WorkingSet / 1MB, 2)
    }
}

$report | Export-Csv high_cpu_processes.csv -NoTypeInformation

Example 4: Count Down Timer

for ($i = 10; $i -gt 0; $i--) {
    Write-Host "$i..."
    Start-Sleep -Seconds 1
}
Write-Host "Go!"

Example 5: Clean Old Files

$files = Get-ChildItem -Path C:\Logs -Recurse -File
foreach ($file in $files) {
    if ($file.LastWriteTime -lt (Get-Date).AddDays(-30)) {
        Write-Host "Deleting: $($file.FullName)"
        Remove-Item $file.FullName -Force
    }
}

Practical Pentesting Scenarios

Scenario 1: Port Scan

$target = "192.168.1.1"
$ports = 1..1000

foreach ($port in $ports) {
    $connection = Test-NetConnection -ComputerName $target -Port $port -WarningAction SilentlyContinue
    if ($connection.TcpTestSucceeded) {
        Write-Host "Port $port is OPEN" -ForegroundColor Green
    }
}

Scenario 2: Ping Sweep

$network = "192.168.1"
1..254 | ForEach-Object -Parallel {
    $ip = "$using:network.$_"
    if (Test-Connection -ComputerName $ip -Count 1 -Quiet) {
        "$ip is alive"
    }
} -ThrottleLimit 50

Scenario 3: Brute Force Username Enumeration

$usernames = Get-Content usernames.txt
$domain = "domain.com"

foreach ($user in $usernames) {
    try {
        $result = net user $user /domain 2>$null
        if ($result) {
            Write-Host "[+] Valid user: $user" -ForegroundColor Green
        }
    } catch {
        Write-Host "[-] Invalid user: $user" -ForegroundColor Red
    }
}

Scenario 4: Search for Credentials

$keywords = @("password", "pwd", "pass", "credential", "secret")
$files = Get-ChildItem -Recurse -Include *.txt, *.config, *.ini -ErrorAction SilentlyContinue

foreach ($file in $files) {
    $content = Get-Content $file.FullName -ErrorAction SilentlyContinue
    foreach ($keyword in $keywords) {
        $matches = $content | Select-String -Pattern $keyword -CaseSensitive:$false
        if ($matches) {
            Write-Host "[+] Found '$keyword' in: $($file.FullName)" -ForegroundColor Yellow
        }
    }
}

Scenario 5: Enumerate Shares

$computers = Get-Content computers.txt

foreach ($computer in $computers) {
    Write-Host "`n[*] Checking: $computer"
    
    try {
        $shares = Get-WmiObject -Class Win32_Share -ComputerName $computer -ErrorAction Stop
        foreach ($share in $shares) {
            Write-Host "  [+] Share: \\$computer\$($share.Name)" -ForegroundColor Green
        }
    } catch {
        Write-Host "  [-] Cannot access $computer" -ForegroundColor Red
    }
}

Scenario 6: Hash Cracking Progress

$hashes = Get-Content hashes.txt
$wordlist = Get-Content rockyou.txt
$cracked = @()

foreach ($hash in $hashes) {
    foreach ($password in $wordlist) {
        $testHash = (Get-FileHash -InputStream ([System.IO.MemoryStream]::new([System.Text.Encoding]::UTF8.GetBytes($password))) -Algorithm MD5).Hash
        
        if ($testHash -eq $hash) {
            Write-Host "[+] Cracked: $hash = $password" -ForegroundColor Green
            $cracked += [PSCustomObject]@{
                Hash = $hash
                Password = $password
            }
            break
        }
    }
}

$cracked | Export-Csv cracked.csv -NoTypeInformation

Scenario 7: Process Multiple Targets

$targets = Import-Csv targets.csv

foreach ($target in $targets) {
    Write-Host "`n[*] Scanning: $($target.IP)"
    
    # Check if alive
    if (Test-Connection -ComputerName $target.IP -Count 1 -Quiet) {
        Write-Host "  [+] Host is up"
        
        # Check common ports
        $commonPorts = @(21, 22, 23, 80, 443, 445, 3389)
        foreach ($port in $commonPorts) {
            $connection = Test-NetConnection -ComputerName $target.IP -Port $port -WarningAction SilentlyContinue
            if ($connection.TcpTestSucceeded) {
                Write-Host "  [+] Port $port is open" -ForegroundColor Green
            }
        }
    } else {
        Write-Host "  [-] Host is down" -ForegroundColor Red
    }
}

Parallel Processing (PowerShell 7+)

ForEach-Object -Parallel

# Process items in parallel
1..10 | ForEach-Object -Parallel {
    Start-Sleep -Seconds 2
    "Processed $_"
} -ThrottleLimit 5

# Ping sweep (parallel)
1..254 | ForEach-Object -Parallel {
    $ip = "192.168.1.$_"
    if (Test-Connection -ComputerName $ip -Count 1 -Quiet) {
        "$ip is alive"
    }
} -ThrottleLimit 50

Quick Reference

ForEach-Object (Pipeline):
Collection | ForEach-Object { $_ }
Collection | % { $_ }

foreach Statement:
foreach ($item in $collection) { }

for Loop:
for ($i = 0; $i -lt 10; $i++) { }

while Loop:
while (condition) { }

do-while Loop:
do { } while (condition)

do-until Loop:
do { } until (condition)

Where-Object (Filter):
Collection | Where-Object { condition }
Collection | ? { condition }

Control:
break       # Exit loop
continue    # Skip to next iteration

Parallel (PS 7+):
Collection | ForEach-Object -Parallel { } -ThrottleLimit N

Performance Tips

1. Use foreach Statement for Speed

# Faster
$data = 1..10000
foreach ($item in $data) { $item * 2 }

# Slower
1..10000 | ForEach-Object { $_ * 2 }

2. Filter Early

# Good - filter first
Get-ChildItem | Where-Object { $_.Length -gt 1MB } | ForEach-Object { /* process */ }

# Bad - process all, then filter
Get-ChildItem | ForEach-Object { /* process */ } | Where-Object { $_.Length -gt 1MB }

3. Use -Parallel When Possible (PS 7+)

# Much faster for I/O operations
$servers | ForEach-Object -Parallel {
    Test-Connection $_ -Count 1
} -ThrottleLimit 10

Resources

Help

Get-Help ForEach-Object -Examples
Get-Help about_ForEach
Get-Help about_While
Get-Help about_Break

Online


Next Steps

  1. Practice loops daily - automate everything!
  2. Learn ForEach-Object -Parallel (PowerShell 7+)
  3. Combine with Where-Object for powerful filtering
  4. Master break/continue for flow control
  5. Benchmark performance - choose right loop type

Loops are the heart of automation. Master them and you'll process thousands of items effortlessly!


"ForEach-Object: Because doing things once is never enough."