1

I have the following snippet of a functions parameters and their sets

function Test {
    [CmdletBinding(DefaultParameterSetName='StringConsole')]

    param (
        [Parameter(Mandatory,
                   ValueFromPipelineByPropertyName,
                   ParameterSetName = 'ObjectFile')]
        [Parameter(Mandatory,
                   ValueFromPipelineByPropertyName,
                   ParameterSetName = 'StringFile')]
        [Alias("PSPath")]
        [ValidateNotNullOrEmpty()]
        [string]
        $Path,

        [Parameter(Mandatory,
                   ValueFromPipeline,
                   ParameterSetName='StringFile',
                   Position = 0)]
        [Parameter(Mandatory,
                   ValueFromPipeline,
                   ParameterSetName='StringConsole',
                   Position = 0)]
        [ValidateNotNullOrEmpty()]
        [string]
        $Message,

        [Parameter(Mandatory,
                   ValueFromPipeline,
                   ParameterSetName='ObjectFile',
                   Position = 0)]
        [Parameter(Mandatory,
                   ValueFromPipeline,
                   ParameterSetName='ObjectConsole',
                   Position = 0)]
        [ValidateNotNullOrEmpty()]
        [object]
        $Object,
 
        [Parameter(ParameterSetName='StringFile')]
        [Parameter(ParameterSetName='StringConsole')]
        [ValidateSet('Information', 'Verbose', 'Warning', 'Error', 'Object')]
        [string]
        $Severity = 'Information',

        [Parameter(ParameterSetName='StringFile')]
        [Parameter(ParameterSetName='StringConsole')]
        [switch]
        $NoPreamble,

        [Parameter(ParameterSetName = 'StringConsole')]
        [Parameter(ParameterSetName = 'ObjectConsole')]
        [switch]
        $Console
    )

}

If I call the function using

Test 'Hello, World'

it properly uses the StringConsole default parameter set from CmdletBinding

If I call the function using

Test -Message 'Hello, World' -Path C:\SomeFile.txt

It properly uses the StringFile parameter set

But if I call the function using

Test 'Hello, World' -Path C:\SomeFile.txt

I get this error and the function doesn't execute:

Parameter set cannot be resolved using the specified named parameters

The error specifically states it couldn't resolve the parameter set using the NAMED parameters. If a parameter gets bound by position does it not also satisfy the "named" parameter? Or do you have to specifically bind the parameter using the name?

Is there anyway I could design the parameter sets to make my last example work and not throw an error?

Santiago Squarzon
  • 41,465
  • 5
  • 14
  • 37
Martin
  • 503
  • 1
  • 6
  • 20
  • It’s either bound positionally or named, sounds like they aren’t the same. – Doug Maurer Nov 24 '22 at 23:28
  • I'm unable to replicate, `Test 'Hello, World' -Path C:\SomeFile.txt` works well and it uses the `StringFile` parameter set – Santiago Squarzon Nov 24 '22 at 23:35
  • @SantiagoSquarzon Perhaps it's an issue with the other parameters. I've updated the question with the full set of parameters. Are you able to replicate when using all parameters? – Martin Nov 24 '22 at 23:41
  • Now yes, and the issue is clear, `-Message` is conflicting with `-Object` since they're both Position `0` and `[object]` matches a `[string]` argument. Remove `Position = 0` from your object parameter set and you will see what I mean – Santiago Squarzon Nov 24 '22 at 23:46
  • @SantiagoSquarzon That was it! Didn't even think about the fact that `[object]` would match a `[string]`. If you want to add that as an answer I'll mark it as correct. Thanks! – Martin Nov 24 '22 at 23:53
  • Hope the answer clarifies it a bit – Santiago Squarzon Nov 25 '22 at 00:06

1 Answers1

2

The logic used for your parameter sets looks perfectly fine but the issue is that you have 2 parameters with Position = 0 (-Message and -Object), normally this wouldn't be a problem but one of them is of the type System.Object and since all objects inherit from this class, no matter what you pass as argument in position 0 it will match this parameter. Since the other parameter on Position = 0 is of type System.String then 'Hello, World' (a string but also an object) matches both parameter sets and the binder has no idea which one did you mean to use.

A very easy way of seeing this, without changing your current code and just adding $PSCmdlet.ParameterSetName to the function's body, would be to pass an integer as positional parameter and everything works as expected:

function Test {
    [CmdletBinding(DefaultParameterSetName='StringConsole')]
    param(
        # same param block here
    )

    'Using: ' + $PSCmdlet.ParameterSetName
}

Test 0 -Path C:\SomeFile.txt # => Using: ObjectFile
Santiago Squarzon
  • 41,465
  • 5
  • 14
  • 37