The PowerShell foreach loop runs a block of code once for every item in a collection, such as an array, a list of files or the output of a command. The current item is stored in the variable you name before in:
foreach ($item in $collection) {
# do something with $item
}
Here’s a real one that walks through an array of office names:
$cities = 'Seattle', 'Denver', 'Austin', 'Boston'
foreach ($city in $cities) {
"Checking the $city office"
}
Output:
Checking the Seattle office
Checking the Denver office
Checking the Austin office
Checking the Boston office

In this guide, I’ll loop through arrays, files and hashtables, show how break and continue work, and explain the difference between foreach, ForEach-Object and the .ForEach() method.
I tested everything in PowerShell 7.6 and Windows PowerShell 5.1. The two traps near the end caught me more than once, so don’t skip them.
Loop through an array with foreach
Arrays are the most common use. You don’t need an index or a counter, and the loop stops on its own after the last item.
This one adds up a list of order amounts:
$orders = 120.50, 89.99, 240.00, 15.25
$total = 0
foreach ($amount in $orders) {
$total += $amount
}
"Orders: $($orders.Count) Total: {0:C}" -f $total
Output:
Orders: 4 Total: $465.74
The {0:C} format shows the total as currency. On a US system, that’s dollars with two decimals.
Loop through files in a folder
Anything that returns objects can go after in, including a command. Here’s every log file in a folder with its size:
foreach ($file in Get-ChildItem -Path C:\psfaqs\Logs -Filter *.log) {
'{0,-8} {1,8:N1} KB' -f $file.Name, ($file.Length / 1KB)
}
Output:
app.log 2.3 KB
sql.log 93.8 KB
web.log 17.6 KB

For more file examples, see how to loop through files in a folder with PowerShell.
Loop through a hashtable
A hashtable isn’t enumerated item by item on its own. Call GetEnumerator() so each pass gets one key/value pair:
$offices = [ordered]@{ Seattle = 'WA'; Austin = 'TX'; Boston = 'MA' }
foreach ($entry in $offices.GetEnumerator()) {
"$($entry.Key) is in $($entry.Value)"
}
Output:
Seattle is in WA
Austin is in TX
Boston is in MA
I used [ordered] so the output keeps the order I typed. A plain hashtable doesn’t guarantee any order.
How do I get the index in a foreach loop?
The foreach statement doesn’t track the position for you. Keep your own counter:
$tasks = 'Back up the database', 'Patch the servers', 'Rotate the logs'
$i = 0
foreach ($task in $tasks) {
$i++
"Step ${i}: $task"
}
Output:
Step 1: Back up the database
Step 2: Patch the servers
Step 3: Rotate the logs
Note the ${i} braces. Without them, PowerShell reads $i: as a drive-qualified variable and the line breaks. If you need the index a lot, a for loop is usually cleaner.
Break and continue in a foreach loop
continue skips to the next item, and break ends the loop:
$servers = 'WEB01', 'OFFLINE02', 'WEB03', 'STOP', 'WEB05'
foreach ($server in $servers) {
if ($server -like 'OFFLINE*') { continue } # skip this one
if ($server -eq 'STOP') { break } # leave the loop
"Patching $server"
}
Output:
Patching WEB01
Patching WEB03
Break out of nested loops with a label
In nested loops, break and continue only affect the inner loop. Add a label to the outer loop and name it:
:outer foreach ($region in 'East', 'West', 'Central') {
foreach ($server in 1..3) {
if ($server -eq 2) { continue outer } # jump to the next region
if ($region -eq 'Central') { break outer } # stop both loops
"$region-SRV$server"
}
}
Output:
East-SRV1
West-SRV1
I go deeper into this in how to break out of a foreach loop.
foreach vs. ForEach-Object vs. .ForEach()
PowerShell has three ways to do “for each”, and they aren’t interchangeable:
| Form | Where it’s used | Current item |
|---|---|---|
foreach ($x in $list) { } | A statement in a script. Fastest for data already in memory. | $x |
... | ForEach-Object { } | Inside a pipeline. Streams items one at a time. | $_ or $PSItem |
$list.ForEach({ }) | A method on a collection (PowerShell 4 and later). | $_ |
Here’s ForEach-Object in a pipeline:
Get-ChildItem -Path C:\psfaqs\Logs -Filter *.log |
ForEach-Object { '{0} is {1:N0} bytes' -f $_.Name, $_.Length }
Output:
app.log is 2,400 bytes
sql.log is 96,000 bytes
web.log is 18,000 bytes
And the .ForEach() method:
(1..5).ForEach({ $_ * 10 })
Output:
10
20
30
40
50
One thing that confuses people: foreach typed after a pipe (1..3 | foreach { $_ }) is an alias for ForEach-Object, not the foreach statement.
In my timing test with 100,000 items, the foreach statement was several times faster than ForEach-Object. I show the numbers in the for loop article.
Trap 1: continue inside ForEach-Object stops the whole script
This is the one that bites most people. ForEach-Object runs a script block, not a loop, so continue looks for an enclosing loop. When it doesn’t find one, it stops the script:
1..5 | ForEach-Object {
if ($_ -eq 3) { continue }
$_
}
'This line never runs'
Output:
1
2

It behaves the same in PowerShell 7. To skip one item inside ForEach-Object, use return instead:
1..5 | ForEach-Object {
if ($_ -eq 3) { return } # skip 3, keep going
$_
}
'Done'
Output:
1
2
4
5
Done
Trap 2: changing the collection while you loop
You can’t add to or remove from a list while a foreach loop is walking it:
$queue = [System.Collections.Generic.List[string]]@('job1', 'job2', 'job3')
foreach ($job in $queue) {
if ($job -eq 'job2') { $queue.Add('job4') }
}
Output:
OperationStopped: C:\psfaqs\modify.ps1:3
Line |
3 | foreach ($job in $queue) {
| ~~~~
| Collection was modified; enumeration operation may not execute.
Loop over a copy instead. @($queue) makes a new array, so changing the original is safe:
$queue = [System.Collections.Generic.List[string]]@('job1', 'job2', 'job3')
foreach ($job in @($queue)) { # loop over a copy
if ($job -eq 'job2') { $queue.Add('job4') }
}
$queue -join ', '
Output:
job1, job2, job3, job4
What happens when the collection is empty or $null?
Nothing, which is what you want. Since PowerShell 3.0, a foreach over $null runs zero times:
$servers = $null
foreach ($server in $servers) {
"Restarting $server"
}
'No servers, and no error'
Output:
No servers, and no error
Run a foreach in parallel (PowerShell 7)
PowerShell 7 added ForEach-Object -Parallel, which runs items at the same time. Four one-second tasks finish in about one second instead of four:
1..4 | ForEach-Object -Parallel {
Start-Sleep -Seconds 1
"Finished item $_"
} -ThrottleLimit 4 | Sort-Object
Output in PowerShell 7:
Finished item 1
Finished item 2
Finished item 3
Finished item 4
Windows PowerShell 5.1 doesn’t have the parameter:
1..4 | ForEach-Object -Parallel { "Finished item $_" }
Output in Windows PowerShell 5.1:
ForEach-Object : Parameter set cannot be resolved using the specified named parameters.
At C:\psfaqs\parallel51.ps1:1 char:8
+ 1..4 | ForEach-Object -Parallel { "Finished item $_" }
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : MetadataError: (:) [ForEach-Object], ParameterBindingException
+ FullyQualifiedErrorId : AmbiguousParameterSet,Microsoft.PowerShell.Commands.ForEachObjectCommand
Parallel script blocks run in separate runspaces, so use $using:variable to read variables from your script. Microsoft explains the details in the ForEach-Object documentation.
Frequently Asked Questions
What is the syntax of a foreach loop in PowerShell?
foreach ($item in $collection) { <statements> }. $item holds the current element on each pass.
What is the difference between foreach and ForEach-Object?
foreach is a statement that works on a collection already in memory and is faster. ForEach-Object is a cmdlet that processes pipeline input one item at a time, using $_.
How do I skip an item in ForEach-Object?
Use return. A continue inside ForEach-Object stops the whole script unless there’s an enclosing loop.
How do I loop through a hashtable in PowerShell?
Use foreach ($entry in $hash.GetEnumerator()) { $entry.Key; $entry.Value }, or loop over $hash.Keys.
Can a PowerShell foreach loop run in parallel?
Yes, in PowerShell 7 with ForEach-Object -Parallel. Windows PowerShell 5.1 doesn’t support it. See Microsoft’s about_Foreach for the statement itself.
Related loop tutorials:
- PowerShell for loop
- PowerShell ForEach-Object
- While loop in PowerShell
- Loop through files in a folder
Bijay Kumar is an esteemed author and the mind behind PowerShellFAQs.com, where he shares his extensive knowledge and expertise in PowerShell, with a particular focus on SharePoint projects. Recognized for his contributions to the tech community, Bijay has been honored with the prestigious Microsoft MVP award. With over 15 years of experience in the software industry, he has a rich professional background, having worked with industry giants such as HP and TCS. His insights and guidance have made him a respected figure in the world of software development and administration. Read more.