The PowerShell ForEach loop is an excellent tool for iterating through a collection, such as an array of items. You can take action on each item in the array using one or more commands.
In this tutorial, you will learn all about the different ForEach statements available in PowerShell and how to implement each.
Prerequisites
To follow along with this tutorial, you will need PowerShell 7. This tutorial uses version 7.3.4. Most of the examples can work in earlier PowerShell versions except the use of ForEach-Object -Parallel
, which Microsoft introduced in PowerShell 7.
ForEach Statement
The foreach statement syntax is outlined below. You begin with the foreach
keyword followed by a set of parentheses ( )
. In the parentheses, you reference the collection or array you want to iterate through ($collection
) and how to identify the current item in the loop ($item
). PowerShell automatically creates the $item
variable, so you do not need to define it beforehand. Finally, you enclose the action to take on each loop iteration in a set of curly brackets { }
.
foreach ($item in $collection) {
# loop statements go here
}
Examples
Let’s take a look at an example. You define an array called $namesArray
with some values ("Jeff","Mary","John","Heather"
). When creating the foreach statement, you can give any name to the $item variable you want. However, you will typically see PowerShell authors use a singular version of the items they are looping through. This example uses the singular $name since the foreach loop is iterating through the $namesArray
.
Finally, output the current item in the foreach script block by referencing the $name
variable.
# Define the array
$namesArray = "Jeff","Mary","John","Heather"
# Create a foreach loop to iterate through each array item
foreach ($name in $namesArray) { $name }

The example above used a previously defined array. You can also use foreach statements with a cmdlet that returns a collection of items. In the example below, the foreach statement loops through the results of the Get-Process
cmdlet.
Inside the script block, an if
statement checks to see if the process name starts with the letter A
. If true, the loop outputs the process name. If the collection items are an object with defined properties, you can access those properties using dot notation with the current item variable name, such as $process.ProcessName
.
foreach ($process in Get-Process) {
if ($process.ProcessName -like "A*") {
$process.ProcessName
}
}

ForEach-Object
The ForEach-Object is similar to the foreach statement in that it loops through a collection of objects and performs operations against each. However, the difference is the ForEach-Object statement is used in the pipeline.
Get-Process | ForEach-Object # ...
Note: ForEach-Object does have an alias of “foreach”. This alias is not to be confused with the previous foreach statement in the tutorial. They are two separate commands and have different functionality.
ForEach-Object also uses the percent sign % as an alias. This alias is commonly used when typing commands at a PowerShell prompt but should be avoided when writing scripts and functions. Always use complete commands when writing scripts and functions.
In the previous foreach examples, you defined a collection beforehand or used the results of a command in the foreach statement definition. You call the cmdlet and then pipe the results to the ForEach-Object statement. The pipeline processes the results to its left one at a time. You then define actions to take in a script block enclosed in curly brackets { }
.
There are three ways to use a ForEach-Object statement.
Script block
Much like the foreach statement, you can use a script block to specify the operations to perform on each item coming in from the pipeline. Instead of defining the variable (like $item
), use the $_
variable to represent the current item. The script block can contain any number of PowerShell commands or scripts.
Let’s rewrite the last foreach example. First, run Get-Process
and pipe the results to the ForEach-Object
statement. Inside the script block, repeat the if
statement checking to see if the ProcessName
name starts with A by referencing the current object using $_
. Finally, output the process name.
Get-Process | ForEach-Object {
if ($_.ProcessName -like "A*") {
$_.ProcessName
}
}

You can also use multiple script blocks with Begin, Process, and End. The Begin block runs first at the beginning; then, the Process block executes for each object. Finally, the End block runs at the end.
Review the code and screenshot below where 1..5
is piped to ForEach-Object
. A Begin
block outputs Starting
, then the Process
block outputs the current object. Finally, the End
block outputs Ending
.
1..5 | ForEach-Object -Begin {"Starting"} -Process {$_} -End {"Ending"}

Read more about Begin, Process, and End blocks in PowerShell Begin Process End Blocks Demystified.
Operation statement
Operation statements do not use a script block to define action, which is more PowerShell-like. You can list a property value or call methods on the input objects.
For example, access the ProcessName
property from the output of Get-Process
.
Get-Process | ForEach-Object ProcessName
You can also perform actions on the incoming pipeline objects. This example takes a string of items separated by commas and splits them into individual strings. Use the MemberName
parameter to specify a method (Split
) and the ArgumentList
parameter to send arguments to the selected method.
"one,two,three,four" | ForEach-Object -MemberName Split -ArgumentList ","

Parallel processing
PowerShell 7 introduced a third parameter that enables running each script block in parallel. Use the Parallel
parameter to allow parallel processing and the ThrottleLimit
parameter to dictate the number of parallel scripts running simultaneously. PowerShell creates a new runspace for each loop iteration to ensure an isolated environment for each parallel process.
Like the script block from earlier, use the $_
variable to reference the current input object. You can reference variables outside the script block with the $using:
keyword.
The example below defines a string variable named $Message
with a value of "Processing:"
. Next, use the range operator to generate a list of numbers from 1 through 12 and pipe this to ForEach-Object
.
Next, add the Parallel
parameter with a script block. The script block references the $Message
variable using the $using:Message
syntax and outputs the current input object. The script block then sleeps for 2 seconds. Finally, set ThrottleLimit
to 4
.
$Message = "Processing:"
1..12 | ForEach-Object -Parallel {
"$using:Message $_"
Start-Sleep -Seconds 2
} -ThrottleLimit 4
When the command executes, PowerShell displays the first numbers in parallel as the throttle limit is 4. The command sleeps for 2 seconds and processes the next batch of 4 input items. Watch the GIF below as PowerShell outputs each batch.

ForEach Method
When you have a collection (such as an array or a list), you can use the .ForEach()
method to iterate over each element of the collection and perform a specified action on each element. The syntax for using the .ForEach()
method in PowerShell is as follows. The $collection
variable refers to an array or list, and { <action> }
is the script block containing the commands to execute on each item.
$collection.ForEach({ <action> })
Take the $namesArray
from earlier and use the .ForEach()
method to display a string containing the current item.
$namesArray = "Jeff","Mary","John","Heather"
$namesArray.ForEach( {"My name is $_."} )

PowerShell ForEach Performance
The performance of each of these ForEach methods is not the same. While the examples below are very basic, they illustrate a difference in the performance capabilities.
First, define a range of numbers from 1 to 9999999 and save this to a variable named $nums
.
$nums = 1..9999999
Next, use Measure-Command
to find the execution time for each option. Let’s start with the foreach statement and output the current $item
in the script block. View the results in the screenshot below the code. You’ll see this command completes in about 2 seconds.
Measure-Command { foreach ($item in $nums) {$item} }

Next, use the .ForEach()
method and perform the same action of outputting $_
to the console. This command completes in about 11 seconds, almost five times as long as the foreach statement!
Measure-Command { $nums.ForEach( {$_} ) }

Finally, use the ForEach-Object
command to output the current object to the console. This command executes in about 15 seconds or eight times as long!
Measure-Command { $nums | ForEach-Object {$_} }

While these are simple examples and commands, they illustrate that some methods are not as efficient as others. The difference can be negligible if you are dealing with a small number of items. However, if you are performing multiple actions on many objects, try each method to see which provides the best performance.
Check out a previous article exploring this same performance topic in Measuring Performance in PowerShell.
PowerShell ForEach Conclusion
PowerShell ForEach provides many options for iterating through a collection of items. Which one you use may come down to personal preference or, as you saw, performance capabilities. Familiarizing yourself with each provides another tool and ability in your future PowerShell writing.
Reference:
about Foreach – PowerShell | Microsoft Learn
ForEach-Object | Microsoft Learn