Parameter binding is a fundamental concept in PowerShell. You might be puzzled if you don't fully grasp this concept, as I recently found out.
I re-read advanced functions and tried a simple calculator script to test my recall. The problem started when I tried to supply parameter values from the pipeline. This post covers two key PowerShell pipeline parameter binding concepts: "byValue" and "byProperty".
Let us examine my first attempt at my calculator program. Below is a simple PowerShell script to perform basic arithmetic operations on two numbers.

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.

Figure 1b: Simple Calculator Part 2. Defining the output object and writing the output object into the pipeline.
The problem arises when I create a custom object with properties matching my function parameters and pass it to the Get-Calculation function in the pipeline, resulting in an error (Figure 3).

Figure 3: Passing a custom PS Object to Get-Calculation
Troubleshooting with Trace-Command
To better understand and debug the issue, I used the Trace-Command cmdlet to observe parameter binding 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).

Figure 4: Using Trace-Command to examine parameter binding
In figure 5, the entire object ($myObj) is supplied as the parameter value instead of its properties.

Figure 5: Parameter binding
Parameter Binding: byValue vs. byProperty
I set ValueFromPipeline=$True, causing the Pipeline to bind byValue.
"Bind by Value" means the incoming object is used as the parameter value without processing its properties. The pipeline attempts to bind the entire object (e.g., {firstNumber=1; secondNumber=4; operator=+}) to parameters like "firstNumber."
The solution is to bind byProperty. We must explicitly instruct the pipeline to bind the object's properties to our parameter. As shown below we changed our original script to bind byProperty (Figure 6).

Figure 6: Parameter attribute to accept value by property
Next, we again observe the behavior using Trace-Command. This time, we can observe that the object properties are accurately bound to our parameters (Figure 7). For this to work, the object's properties must match the function parameter names.

Figure 7: Parameter binding is successful
Test: Parameter Binding byValue and byProperty
In the next program iteration, we modified parameters to accept both by value and by property. The operator parameter was made non-mandatory with a default value of "+".

Figure 8: Accepting parameter byValue and byProperty
Now our function accepts parameter values using both the byValue and byProperty option. Here's a simple example: we send an Int object (Figure 9) and a custom PS object (Figure 10).

Figure 9: Supplying two objects to Get-Calculation
The first result of 8 might be confusing, but the program worked as expected for the second object (I'll leave the operator property as homework for you).
We are going to observe binding using our good old friend the Trace-command. In Figure 10, the Int object 4 binds to both firstNumber and secondNumber, resulting in a sum of 8. This shows that with the "byValue" option, the pipeline uses the same object for all parameters accepting pipeline values.

Figure 10: Parameter binding for Int object
For our PS custom object, the pipeline initially attempts byValue binding, fails, and then switches to byProperty, binding by property name (Figure 11).

Figure 11: Parameter binding for PS custom object
Conclusion
Here are some key takeaways from this post:
- Default option for parameter binding is to bind by value.
- If we have selected both byValue and byProperty option, the pipeline will start by binding using byValue option.
- When parameters are provided directly to the function, the process block runs only once.
- When parameters are piped, the process block runs for each object.
I hope this post would help you to further understand and implement advanced functions correctly.
Don't miss the latest PowerShell automation trends, expert tips, and best practices. Join thousands of IT professionals who rely on our monthly newsletter for actionable insights, script templates, and industry updates that drive operational excellence. → Subscribe to the ScriptRunner Newsletter.
