PowerShell for Windows Admins

Jan 9 2015   11:30AM GMT

foreach, pipelines and $_

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

Tags:
Active Directory
Powershell

I’ve recently seen a few questions where people have been using a pipeline inside a foreach loop and experienced problems when they’ve tried to access properties on the looping objects. To illustrate we’ll start with a CSV file containing email addresses and job titles.

£> Import-Csv -Path C:\Test\userdata.txt

emailaddress             title
————             —–
gdreen@Manticore.org     Boss
dbrown@Manticore.org     Underling
dwhite@Manticore.org     Underling
jdaven@Manticore.org     Minion
fgreen@Manticore.org     Minion
dgreensmth@Manticore.org Minion
dgreenly@Manticore.org   Minion

This is definitely an employee friendly organization

At the moment AD doesn’t contain any job title information

£> $users = Import-Csv -Path C:\Test\userdata.txt

foreach ($user in $users){
$mail = $user.EmailAddress

Get-ADUser -Filter {EmailAddress -eq $mail} -Properties Title |
select Name, Title

}

Name                                      Title
—-                                      —–
Dave Green
Dave Brown
Dave White
Jo Daven
Fred Green
Dale Greensmith
Dave Greenly

The approach that causes problems is this:

£> $users = Import-Csv -Path C:\Test\userdata.txt

foreach ($user in $users){
$mail = $user.EmailAddress

Get-ADUser -Filter {EmailAddress -eq $mail} -Properties Title |
foreach {Set-ADUser -Identity $_ -Title $_.Title} |

Get-ADUser -Filter {EmailAddress -eq $mail} -Properties Title |
select Name, Title
}

Name                                      Title
—-                                      —–
Dave Green
Dave Brown
Dave White
Jo Daven
Fred Green
Dale Greensmith
Dave Greenly

When you use foreach as a keyword the $_ and $psitem variables aren’t available. These variables represent the current object on the pipeline.  The foreach keyword loop doesn’t have a pipeline as such.

Inside the foreach a pipeline is created

Get-ADUser -Filter {EmailAddress -eq $mail} -Properties Title |
foreach {Set-ADUser -Identity $_ -Title $_.Title -PassThru} |
select Name, Title

$_ is used correctly to identify the object on which Set-ADUser is to work – its the current object on the pipeline.

The use of  $_.Title  to set the user’s job title is where the problem really bites.  $_.Title  refers to the Title property of the current object on the pipeline so you are setting the Title to its existing value.

You need to reach back to the $user object that represents the current object from the set you are looping through with foreach to get the correct value

£> $users = Import-Csv -Path C:\Test\userdata.txt

foreach ($user in $users){
$mail = $user.EmailAddress

Get-ADUser -Filter {EmailAddress -eq $mail} -Properties Title |
foreach {Set-ADUser -Identity $_ -Title $user.Title} |

Get-ADUser -Filter {EmailAddress -eq $mail} -Properties Title |
select Name, Title
}

Name                                      Title
—-                                      —–
Dave Green                                Boss
Dave Brown                                Underling
Dave White                                Underling
Jo Daven                                  Minion
Fred Green                                Minion
Dale Greensmith                           Minion
Dave Greenly                              Minion

You’ll see similar problems if you have nested foreach-object loops or a switch statement inside a foreach-object loop.  $_ always refers to the current context and you have to either reach back to the looping variable in the csae of a foreach or set variables on the data in the outer foreach before entering the nested foreach.

 Comment on this Post

 
There was an error processing your information. Please try again later.
Thanks. We'll let you know when a new response is added.
Send me notifications when other members comment.

Forgot Password

No problem! Submit your e-mail address below. We'll send you an e-mail containing your password.

Your password has been sent to:

Share this item with your network: