Reputation: 21418
I was able to reproduce this in a more generic way, and the issue is different than originally presented. I have rewritten this question to reflect the issue experienced along with a generic reproducible example.
I have a cmdlet that sometimes produces no output when it doesn't find any data to return. However, I use this function to pass information to another cmdlet which accepts pipeline input via way of the ValueFromPipelineByPropertyName
attribute. When there is an actual object being passed down the pipeline, everything works as expected, including parameter validation checks. However, if the passed object is $null
, then parameter validation gets skipped. Note that this is not reproduceable when simply passing $null
down the pipeline; I've only been able to reproduce this when emitting no output down the pipeline.
I've been able to reproduce this generically. The parameters are defined with the same attributes as my real code:
Function Get-InfoTest {
Param(
[switch]$ReturnNothing
)
if( !$ReturnNothing ) {
[PSCustomObject]@{
Name = 'Bender'
Age = [int]::MaxValue
}
}
}
Function Invoke-InfoTest {
Param(
[Parameter(Mandatory, ValueFromPipelineByPropertyName)]
[string]$Name,
[Parameter(Mandatory, ValueFromPipelineByPropertyName)]
[int]$Age
)
Write-Host "Hello, $Name. I see you are $Age years old."
}
# With valid object
Get-InfoTest | Invoke-InfoTest
# Correct behavior when $null is directly passed into the cmdlet, throws error
$null | Invoke-InfoTest
# With returned null object, should throw an error but executes with an incorrect result
Get-InfoTest -ReturnNothing | Invoke-InfoTest
What is going on here? While it's not difficult to write null-or-whitespace checks in the function body, this is the point of the Mandatory
parameter option as well as the Validate*
parameter attributes. In my real code, I now need to write null-or-whitespace checks for several parameters which already have validation attributes set. As stated in the code comments, passing $null
into the target cmdlet results in the correct error being thrown, but no output produced from a function results in the function executing as if everything was provided correctly.
Upvotes: 2
Views: 117
Reputation: 21418
If you don't define begin
/process
/end
blocks, functions bodies default to an end
block. However, putting the function body in an explicit process
block results in the correct behavior:
The following modification to Invoke-InfoTest
results in the sample code working correctly for all cases:
Function Invoke-InfoTest {
Param(
[Parameter(Mandatory, ValueFromPipelineByPropertyName)]
[string]$Name,
[Parameter(Mandatory, ValueFromPipelineByPropertyName)]
[int]$Age
)
# Note that I've wrapped this in a process block
process {
Write-Host "Hello, $Name. I see you are $Age years old."
}
}
This works because as stated above, functions default to an end
block if unspecified. However, the end
and begin
blocks are executed regardless of the pipeline object being input. process
only gets executed when there is data passed in. Defining the code using the pipeline variables inside of a process
block effectively stops the code using the missing data from being executed, which seems to be by design.
Thanks to @SantiagoSquarzon in the comments for helping me realize the actual problem.
Upvotes: 2