Parameter Binding concepts in PowerShell

Table of Contents

Post Featured Image

Parameter binding is a fundamental concept in PowerShell. And you can end up scratching your head for a while if you do not understand this concept fully, as I recently experienced first-hand.

In my case it was a simple re-read of advanced functions and then trying a simple script to make a calculator, in order to re-test how much I can recall. The problem started when I tried to supply parameter values from the pipeline. In this blog post I will be going over two fundamental PowerShell pipeline parameter binding concepts: “byValue” and “byProperty”. 

Let us examine my first attempt at my calculator program. As you can see below it is a simple PowerShell script to add, subtract, multiply and divide two numbers (Figure 1a + 1b). 

 

Screenshot: Calculator script. Name of Function: Get-Calculation; Three Parameters: firstNumber, secondNumber and Operator

Figure 1a: Simple Calculator Part 1. Name of Function: Get-Calculation; Three Parameters: $firstNumber, $secondNumber and $operator. Parameter Attributes: Mandatory and Accept value from Pipeline. The operator parameter has a fixed set of values.


Screenshot: Calculator script. Defining the output object and writing the output into the pipeline

Figure 1b: Simple Calculator Part 2. Defining the output object and writing the output object into the pipeline.

 

Here is where the problem starts: when I try to create a custom object with properties same as my function parameters and pass it to Get-Calculation function in the pipeline, it gives me an error (Figure 3).

Screenshot: Passing a custom PS Object to Get-Calculation

Figure 3: Passing a custom PS Object to Get-Calculation


   

Troubleshooting with Trace-Command

To further understand and debug the issue I decided to use the Trace-Command cmdlet to observe how parameter binding is taking place in the pipeline. I ran trace-command with following options to observe parameter binding “ParameterBinderBase, ParameterBinderController, ParameterBinding”. The screenshot below shows the output from Trace-Command (Figure 4).
Screenshot: Using Trace-Command to examine parameter binding

Figure 4: Using Trace-Command to examine parameter binding

 

We can observe in figure 5 that instead of binding object properties to parameter the “whole” object ($myObj) is supplied as value to the parameter.

Screenshot: Parameter binding

Figure 5: Parameter binding


   

Parameter Binding: byValue vs. byProperty

It’s happening because when I defined parameter attributes, I decided to set “ValueFromPipeline=$True”, this causes the Pipeline to bind “byValue”.

“Bind by Value” means that the incoming object will be supplied as value to the parameter. The Pipeline will not process the object and bind specific properties of the object to parameters for the next cmdlet/function in the pipeline. If we observe the output from Trace-Command, the pipeline is trying to bind our object ({firstNumber=1; secondNumber=4; operator=+}) to the parameter “firstNumber” and so on.

The solution is to bind “byProperty”. In our example we need to explicitly tell the pipeline to bind the incoming object’s properties to our parameter. As shown below we changed our original script to bind byProperty (Figure 6).

Screenshot: Parameter attribute to accept value by propert

Figure 6: Parameter attribute to accept value by property

 

Next, we again observe the behavior using Trace-Command. As we can see this time, object properties are bound to our parameters correctly (Figure 7). However, there is one catch: in order for this to work the incoming object’s properties should have the same names as the function parameters.

Screenshot: Trace-Command reveals, parameter binding is successful

Figure 7: Parameter binding is successful


   

Test: Parameter Binding byValue and byProperty

In the next iteration of the same program we changed our parameters to accept both parameter by value and by property (Figure 8). For demonstration, I decided to change the operator parameter and make it non-mandatory and assigned it a default value of “+”.
Screenshot: Accepting parameter byValue and byProperty

Figure 8: Accepting parameter byValue and byProperty


 

Now our function accepts parameter values using both the byValue and byProperty option. Let’s take a simple example where we send two object one of type Int (Figure 9) and the other a custom PS object (Figure 10).

Screenshot: Supplying two objects to Get-Valculation

Figure 9: Supplying two objects to Get-Calculation


 

The first result where we got 8 might be a bit confusing but the program worked as expected for second object (In case you’re wondering out operator property, I will leave it as an homework for you).

We are going to observe binding using our good old friend the Trace-command. As you can observe in Figure 10 the first object, number 4 of type Int is bind to firstNumber and secondNumber resulting in sum being 8. This demonstrates that the pipeline will plug in the same object to all the parameters which are accepting value from the pipeline if we are using “byValue” option.

Screenshot: Parameter binding for Int object

Figure 10: Parameter binding for Int object


 

In the case of our PS custom object, we can observe that the pipeline first tries to bind byValue but it fails and then, as expected, switches to byProperty option and starts binding parameter by property name (Figure 11).

Screenshot: Parameter binding for PS custom object

Figure 11: Parameter binding for PS custom object


   

Conclusion

Here are some key takeaways from this post:

  1. Default option for parameter binding is to bind by value.
  2. If we have selected both byValue and byProperty option, the pipeline will start by binding using byValue option.
  3. If the parameters are supplied directly to the function, then the process block is executed exactly once.
  4. If the parameters are supplied by pipeline, the process block is executed once for each object in the pipeline.

I hope this post would help you to further understand and implement advanced functions correctly.

 

 

About the author: