Prof. Powershell

PowerShell Pipeline Binding Trick

Pipeline binding can get you what you want, as long as you use some of the PowerShell tricks up your sleeve.

Last time we explored in graphic detail what happens inside a PowerShell pipeline. I want to take this a step further, because there is a little more going on and once you understand it, you'll be able to write incredible PowerShell expressions. Let's go back and look at a service example:

PS C:\> get-service wsearch | stop-service -passthru

Status   Name     DisplayName
------   ----     -----------
Stopped  wsearch  Windows Search

The first part of the pipeline retrieves the Windows Search service object, which is piped to a second cmdlet, Stop-Service, which does its thing. But why did this work? Not every cmdlet supports pipeline input. Let's look at cmdlet help:

PS C:\> help stop-service -full

As you look at the different parameters, you'll see that some accept pipeline input and some don't. For the sake of discussion, look at just the Name parameter:

PS C:\> help stop-service -Parameter Name

As you can see, it accepts pipelined input:

Accept pipeline input? true (ByValue, ByPropertyName)

This parameter uses what is referred to as pipeline binding. In this particular case it will bind either ByValue or ByPropertyName. Here's what this means to you.

The ByValue reference essentially means any object that the cmdlet sees it will try to treat as a service name. Here's an example:

PS C:\> "wsearch","wuauserv" | stop-service -whatif

What if: Performing operation "Stop-Service" on Target "Windows Search (wsearch)".
What if: Performing operation "Stop-Service" on Target "Windows Update (wuauserv)".

The two strings, wsearch and wuauserv are piped to Stop-Service. The cmdlet looks at the incoming objects, takes their values and binds them to the -Name parameter. Here's another example using the Restart-Service cmdlet which also uses pipeline binding:

PS C:\> get-content C:\work\services.txt | restart-service -whatif

What if: Performing operation "Restart-Service" on Target "Windows Search (wsearch)".
What if: Performing operation "Restart-Service" on Target "Windows Update (wuauserv)".
What if: Performing operation "Restart-Service" on Target "Windows Remote Management (WS-Management) (winrm)".
What if: Performing operation "Restart-Service" on Target "Background Intelligent Transfer Service (bits)".

The text file is simply a list of service names that are piped to Restart-Service.

The other way to accomplish pipeline binding is by PropertyName. In other words, the cmdlet looks at the incoming object and if it sees a matching property name for the parameter, it binds. The object can be anything. Here's a CSV file I want to import into PowerShell:

PS C:\> import-csv c:\work\data.csv

name      comment  data
----      -------  ----
bits      ok       service
wuauserv  ok       update
wsearch   ok       service

The Import-CSV writes a custom object to the pipeline so I can use it like this:

PS C:\> import-csv c:\work\data.csv | Restart-Service -passthru

Status   Name      DisplayName
------   ----      -----------
Running  bits      Background Intelligent Transfer Ser...
Running  wuauserv  Windows Update
Running  wsearch   Windows Search

How cool is that? Well, let me wrap up this lesson with a little trick so you can take advantage of pipeline binding. Suppose my CSV file didn't have the right property name:

PS C:\> import-csv c:\work\data.csv

svc       comment  data
---       -------  ----
bits      ok       service
wuauserv  ok       update
wsearch   ok       service

I can't pipe this directly to any of the service cmdlets because there are no properties that can be bound to any parameters. Here's the trick: Use Select-Object with a hash table and create a new property on the fly:

PS C:\> import-csv c:\work\data.csv | Select @{Name="Name";Expression={$_.svc}} | Restart-Service -whatif

What if: Performing operation "Restart-Service" on Target "Background Intelligent Transfer Service (bits)".
What if: Performing operation "Restart-Service" on Target "Windows Update (wuauserv)".
What if: Performing operation "Restart-Service" on Target "Windows Search (wsearch)".

The hash table creates a new property called "Name" that will bind to the Name parameter in Restart-Service. The value will be the value of the "svc" property of each object in the pipeline. Alternatively, I could have used ForEach-Object:

PS C:\> import-csv c:\work\data.csv | Foreach { Restart-Service -name $_.svc -whatif}

but I like using Select-Object, especially if I want to bind multiple properties to multiple parameters. Are you beginning to see possibilities?

You'll also sometimes run into this situation with cmdlets that write an object to the pipeline with a property name that can't be used without a little magic. For example, Get-ADComputer uses -name but all the cmdlets you'd like to pipe to are expecting -Computername. In these situations, use my magic trick.

About the Author

Jeffery Hicks is an IT veteran with over 25 years of experience, much of it spent as an IT infrastructure consultant specializing in Microsoft server technologies with an emphasis in automation and efficiency. He is a multi-year recipient of the Microsoft MVP Award in Windows PowerShell. He works today as an independent author, trainer and consultant. Jeff has written for numerous online sites and print publications, is a contributing editor at Petri.com, and a frequent speaker at technology conferences and user groups.

comments powered by Disqus
Most   Popular