PowerShell Pipeline

Introduction to the ForEach Method in PowerShell

This is a continuation of my series on a series on the Where and ForEach methods which were made available in PowerShell V4.

In this article, I will discuss using the ForEach() method and how you can use it in your day-to-day activities.

Using the .ForEach() Method

Using the .ForEach() method requires that we work with a collection, otherwise we will not have the option to use this method. Once we have a collection to work with, the next step is determining how we actually use this approach. Luckily, we can force an error which gives us more of an idea on how it can be used to iterate through the collection.

@().ForEach()

[Click on image for larger view.] Figure 1. Finding the use by looking at the error.X

As we can see in the image above, we can use an expression, which we use in our usual activities with ForEach or ForEach-Object, but we also have an additional arguments member that can provide some other options that we can use with .ForEach() to further enhance its usefulness.

Simple Expression

The obligatory simple example of using this will simply take a collection and iterate through each item while performing a simple action. In this case, I am going through some numbers and multiplying them by two.

@(1..10).ForEach({$_ * 2})

Note here that I am forcing my collection using @(), even though in this case it is not necessary. I can use the well-known $_ to show that I am working with the pipeline, or I could use $PSItem instead. Either way, I am working through each item in the collection and performing an action against it.

Displaying a Property Name of an Object

We can simply specify a known property name that exists in the object to have it displayed in the method.

(Get-Service).ForEach('DisplayName')

[Click on image for larger view.] Figure 2. Displaying the Name property of the service object.

This only works against a single property name, anything more will cause it to throw an error. If you wanted to list more than one property, stay tuned as I will show you another way to accomplish this using a param statement.

Type Conversion Example

This example shows how we can supply a type conversion against each item in the collection. In this example I will take a collection of integers and then convert them to a byte. We can verify this by piping the output to Get-Member and seeing the type.

@(1..10).ForEach([byte]) | Get-Member

[Click on image for larger view.] Figure 3. Running a method against an object.

Just be specifying a particular type in the ForEach method, it will attempt to convert the item to that type.

Specify a Method to Run Against Item

Similar to how we handled a property to only display, we can provide a single method with or without arguments to run against each item in the collection. Let's assume that we have notepad.exe opened up and wanted to close it for whatever reason. We can get the collection of processes with notepad and run the Kill method against it.

Start-Process -FilePath Notepad.Exe @(Get-Process -Name Notepad).ForEach('Kill')

Now we will run another example using Get-WMIObject to get a service and change its start mode to manual (I did change this one back to auto because I wanted to keep getting my updates).

@(Get-WmiObject -Class Win32_Service -Filter "name = 'wuauserv'").ForEach('ChangeStartMode','Manual').ReturnValue

[Click on image for larger view.] Figure 4. Running a method with arguments against an object.

The 0 being returned tells me that this was successfully changed. If I had a collection of services that I needed to change the startmode of, I would filter for the services and then just run the method like this to make that change.

Using a Param Statement with ForEach

Now we can look at using a Param statement along with our expression to pull out more property names and build an object to display the data.

#Display name parameter for the scriptblock

(Get-Service).ForEach({Param($Name)$_.$Name},'DisplayName')

I simply provide the param statement with the parameter and then use that parameter in the scriptblock to list the data in the parameter. My property name is then used outside of the scriptblock which will display the DisplayName of the service in this case.

We can add as many parameters as we need to the ForEach() method and it will handle each one in the order that it was supplied.

(Get-Service).ForEach({Param($Name,$Status)[pscustomobject]@{DisplayName=$_.$Name;Status=$_.$Status}},'DisplayName','Status')

[Click on image for larger view.] Figure 5. Providing parameters to a ForEach method scriptblock.

Mixing it up With ForEach and Where Methods

I've shown how we can use .Where() and .ForEach() during this article and the previous one and now it is time to merge these and show how we can not only provide some filtering, but also iterate through the filtered collection by chaining these methods together.

(Get-Service).Where({$_.Status -eq 'Running'}).

ForEach({Param($Name,$Status)[pscustomobject]@{DisplayName=$_.$Name;Status=$_.$Status}},'DisplayName','Status')

In this case, I didn't do anything special with the Where filtering other than just filtering the data. But I could use one of the possible SelectionModes if needed. As soon as it is filtered, it then processes each item under the ForEach method which then would display the object showing only services that are currently running.

With that, you should go out and play with these methods and see where they could benefit you in your day-to-day operations using PowerShell!