foreach, pipelines and $_

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 emailaddresses 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 organisation Smile

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.

Advertisements
This entry was posted in PowerShell and Active Directory, Powershell Basics. Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s