- Create a certificate-signed RDP shortcut via Group Policy - Fri, Aug 9 2019
- Monitor web server uptime with a PowerShell script - Tue, Aug 6 2019
- How to build a PowerShell inventory script for Windows Servers - Fri, Aug 2 2019
To demonstrate parameter binding, let's create our own simple example. We'll create a Get function and a Remove function. We will craft the Get function to return a specific object and its properties and then create the Remove function to understand these (bindings).
Demonstrating parameter binding
function Get-Widget { [CmdletBinding()] param() [pscustomobject]@{ FileName = 'foo.txt' Path = 'C:\foo.txt' } } function Remove-Widget { [CmdletBinding()] param( [Parameter(ValueFromPipeline)] [pscustomobject]$Widget ) Write-Host "Remove-Widget: I see a `$Widget property FileName [$($Widget.FileName)] and property Path is [$($Widget.Path)]" }
Notice in the example above that the Get-Widget function returns a type of pscustomobject with two properties: FileName and Path.
The Remove-Widget function has a single parameter called Widget that accepts an object type of pscustomobject.
When you run Get-Widget | Remove-Widget (which forces Remove-Widget to ingest whatever Get-Widget returns), you'll get this:
Remove-Widget: I see a $Widget property FileName [foo.txt] and property Path is [C:\foo.txt]
You can see Remove-Widget "sees" both properties because the entire object that Get-Widget returned was bound to $Widget. This type of binding is "binding by value." The entire object is bound to a parameter in the receiving command.
This doesn't just happen by itself though. This worked because of the ValueFromPipeline parameter attribute I've defined. This tells PowerShell to attempt to bind any appropriate object coming in from the pipeline to this parameter.
But what if you don't need the entire object to "map" to a parameter? Instead, perhaps you'd rather have only a single property represent a parameter on the receiving command. In such a case, we can set up binding by property. By setting a parameter to accept pipeline binding by property name, we can tell PowerShell to map only a single property of an object coming from the sending command to a parameter on the receiving command.
Using the same code for Get-Widget but changing the code for Remove-Widget a bit, we can change PowerShell's binding behavior. Notice below I've changed the $Widget parameter name to $Path in Remove-Widget, the type to string, and instead of using the parameter attribute ValueFromPipeline, I'm using ValueFromPipelineByPropertyName.
Parameter binding by property
function Get-Widget { [CmdletBinding()] param() [pscustomobject]@{ FileName = 'foo.txt' Path = 'C:\foo.txt' } } function Remove-Widget { [CmdletBinding()] param( [Parameter(ValueFromPipelineByPropertyName)] [pscustomobject]$Widget ) Write-Host "Remove-Widget: I see a `$Widget property FileName [$($Widget.FileName)] and property Path is [$($Widget.Path)]" }
If I've enabled strict mode by running Set-StrictMode -Version Latest, when I run this as is, it throws an error: The property 'FileName' cannot be found on this object. Verify that the property exists.
It's throwing this error because PowerShell didn't bind the entire object this time. Instead, it just bound the property name Path on Get-Widget's object. Under the hood, PowerShell is only attaching the value of Path to the $Path parameter.
I'll need to change up my Write-Host reference a bit now to use the Path variable (since it is the only thing bound) and run it again.
function Remove-Widget { [CmdletBinding()] param( [Parameter(ValueFromPipelineByPropertyName)] [string]$Path ) Write-Host "Remove-Widget: I see the `$Path parameter value is now $Path." }¬ Write-Host "Remove-Widget: I see the `$Path parameter value is now $Path."
After running Get-Widget | Remove-Widget again, I now get no errors with the output Remove-Widget: I see the $Path parameter value is now C:\foo.txt. $Path's value now is the value of the Path property on the object that Get-Widget returned.
Subscribe to 4sysops newsletter!
Conclusion
Parameter binding is a fascinating concept if you're a geek like me. If you're interested in learning more, check out exactly how both of these methods work. For further explanation on how the pipeline interacts with the begin, process, and end blocks, I highly recommend Boe Prox's blog article entitled Tips on Implementing Pipeline Support.
Hey sorry I know this article is three years old, but I don’t think the second code example has been modified as you say it have. It sure looks like the remove-widget function is still taking in the whole pscustomobject, and not just the $Path attribute like it is in the final code block. Am I reading this wrong?