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 }
powershell foreach
Looping through an array

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
    }
}
powershell foreach
Looping through the output of Get-Process

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
    }
}
powershell foreach-object foreach
Using ForEach-Object in a pipeline

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"}
powershell foreach-object foreach
Using ForEach-Object with Begin, Process, End blocks

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 ","
powershell foreach-object foreach
ForEach-Object with operation statements

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.

powershell foreach-object foreach
ForEach-Object with parallel processing

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 $_."} )
.ForEach() method on an array

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} }
foreach performance

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( {$_} ) }
ForEach method performance

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 {$_} }
ForEach-Object performance

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