codewario
codewario

Reputation: 21418

Pipeline input not being validated when a function emits no output down the pipeline for ValueFromPipelineByPropertyName parameters

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

Answers (1)

codewario
codewario

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

Related Questions