I have defined a cmdlet with two parametersets. Each paramset allows parameters to be passed from the pipeline by the propertyName. The cmdlet works fine if I pass the parameters directly. If I pipeline an object, Cmdlet always resolves the parameter set to Default instead of TestParamSet
CmdLet Code:
```c#
[Cmdlet("Get", "SampleCmdlet", DefaultParameterSetName = "Default")]
[OutputType(typeof(string))]
public class GetSampleCmdlet : PSCmdlet
{
[Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true, ParameterSetName = "Default")]
[Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true, ParameterSetName = "TestParamSet")]
public string ParamOne { get; set; }
[Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true, ParameterSetName = "TestParamSet")]
public string ParamTwo { get; set; }
protected override void ProcessRecord()
{
Console.Out.WriteLine($"ParamSet: {ParameterSetName}");
Console.Out.WriteLine($"ParamOne: {ParamOne} ParamTwo: {ParamTwo}");
WriteObject("Hello World!");
}
}
Invoking the Cmdlet with parameters:
```powershell
PS /Users/> Get-SampleCmdlet -ParamOne "One"
ParamSet: Default
ParamOne: One ParamTwo:
Hello World!
PS /Users/> Get-SampleCmdlet -ParamOne "one" -ParamTwo "two"
ParamSet: TestParamSet
ParamOne: one ParamTwo: two
Hello World!
PS /Users/> skumbham-mac:~ skumbham$ pwsh
Passing the Parameters through the pipeline. Powershell always resolves the parameterset to Default
$test=@{}
$test["ParamOne"]="pipeline-one"
$test["ParamTwo"]="pipeline-two"
$obj = [pscustomobject]$test
$obj | Get-SampleCmdlet
ParamSet: $Default
ParamOne: pipeline-one ParamTwo:
Hello World!
ParameterSet should be TestParamSet
ParameterSet defaults to Default paramset.
PSVersionTable
Name Value
---- -----
PSVersion 6.2.4
PSEdition Core
GitCommitId 6.2.4
OS Darwin 18.7.0 Darwin Kernel Version 18.7.0: Tue Aug 20 16:57:14 PDT 2019; root:xnu-4903.271.2~2/RELEASE_X86_64
Platform Unix
PSCompatibleVersions {1.0, 2.0, 3.0, 4.0鈥
PSRemotingProtocolVersion 2.3
SerializationVersion 1.1.0.1
WSManStackVersion 3.0
/cc @mklement0 Maybe dup?
Here is a repro in PowerShell:
function Get-SampleCmdlet
{
[CmdletBinding(DefaultParameterSetName = "Default")]
param
(
[Parameter(Mandatory,ValueFromPipelineByPropertyName,ParameterSetName = "Default")]
[Parameter(Mandatory,ValueFromPipelineByPropertyName,ParameterSetName = "TestParamSet")]
[string]
$ParamOne,
[Parameter(Mandatory,ValueFromPipelineByPropertyName, ParameterSetName = "TestParamSet")]
[string]
$ParamTwo
)
process {
"ParamSet: $($PSCmdlet.ParameterSetName)"
"ParamOne: $ParamOne"
"ParamTwo: $ParamTwo"
}
}
[pscustomobject]@{
ParamOne = 'pipeline-one'
ParamTwo = 'pipeline-two'
} |
Get-SampleCmdlet
Output:
ParamSet: Default
ParamOne: pipeline-one
ParamTwo:
Removing DefaultParameterSetName = "Default" results in
ParamSet: TestParamSet
ParamOne: pipeline-one
ParamTwo: pipeline-two
Changing the call site to
[pscustomobject]@{
ParamOne = 'pipeline-one'
ParamTwo = 'pipeline-two'
} |
Get-SampleCmdlet -ParamTwo 'named-two'
results in
ParamSet: TestParamSet
ParamOne: pipeline-one
ParamTwo: named-two
It's not obvious to me that affinity to the Default parameter set in the OP should be considered wrong. It seems reasonable to me that, when there are two viable parameter sets, the binder would choose the default parameter set.
@alx9r thanks for your response. It seems like if we remove the DefaultParameterSetName, normal cmdlet invocation seems to be breaking. here's the output after removing DefaultParameterSetName
PS /Users/> Get-SampleCmdlet
Get-SampleCmdlet : Parameter set cannot be resolved using the specified named parameters. One or more parameters issued cannot be used together or an insufficient number of parameters were provided.
At line:1 char:1
+ Get-SampleCmdlet
+ ~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [Get-SampleCmdlet], ParameterBindingException
+ FullyQualifiedErrorId : AmbiguousParameterSet,ocipstools_objectstorage.CmdLets.GetSampleCmdlet
Pipelining the object works fine though:
PS /Users/> $test=@{}
PS /Users/> $test["ParamOne"]="pipeline-one"
PS /Users/> $test["ParamTwo"]="pipeline-two"
PS /Users/> $obj = [pscustomobject]$test
PS /Users/> $obj | Get-SampleCmdlet
ParamSet: TestParamSet
ParamOne: pipeline-one ParamTwo: pipeline-two
Hello World!
Any other suggestions on how to fix this? @alx9r @iSazonov
Here's my suggestion
function Get-SampleCmdlet
{
[CmdletBinding(DefaultParameterSetName = "Default")]
param
(
[Parameter(Mandatory,ValueFromPipelineByPropertyName)]
[string]
$ParamOne,
[Parameter(Mandatory,ValueFromPipelineByPropertyName, ParameterSetName = "TestParamSet")]
[string]
$ParamTwo
)
process {
"ParamSet: $($PSCmdlet.ParameterSetName)"
"ParamOne: $ParamOne"
"ParamTwo: $ParamTwo"
}
}
Then we get
> [pscustomobject]@{ ParamOne = 'pipeline-one' ; ParamTwo = 'pipeline-two'} | SampleCmdlet
ParamSet: TestParamSet
ParamOne: pipeline-one
ParamTwo: pipeline-two
and with one field but not the second
[pscustomobject]@{ParamOne = 'pipeline-one'} | Get-SampleCmdlet
ParamSet: Default
ParamOne: pipeline-one
ParamTwo:
No input
>Get-SampleCmdlet
cmdlet Get-SampleCmdlet at command pipeline position 1
Supply values for the following parameters:
ParamOne: aaa
ParamSet: Default
ParamOne: aaa`
Optional param piped but mandatory missing
[pscustomobject]@{ ParamTwo = 'pipeline-two'} | Get-SampleCmdlet
Get-SampleCmdlet : The input object cannot be bound because it did not contain the information required to bind all mandatory parameters: ParamOne
Optional piped mandatory on the command line
[pscustomobject]@{ ParamTwo = 'pipeline-two'} | Get-SampleCmdlet -ParamOne foo
ParamSet: TestParamSet
ParamOne: foo
ParamTwo: pipeline-two
@kumbham
It seems like if we remove the DefaultParameterSetName, normal cmdlet invocation seems to be breaking. ...
PS /Users/> Get-SampleCmdlet Get-SampleCmdlet : Parameter set cannot be resolved...
Because each parameter has the Mandatory attribute, the parameter binder considers there to be no viable parameter set.
I think using another unused parameter set name like None for DefaultParameterSetName might achieve the behavior you are expecting:
function TwoSets{
[CmdletBinding(DefaultParameterSetName = 'None')]
param ( $NoSet,
[parameter(ParameterSetName = 'A', Mandatory = $true)]$A,
[parameter(ParameterSetName = 'B', Mandatory = $true)]$B )
process { $PSCmdlet.ParameterSetName }
}
TwoSets # None
TwoSets -A 'a' # A
TwoSets -B 'b' # B
The matter of creating a parameter set with no mandatory parameters at all was puzzling enough to me that it led to stackoverflow#43701900.
Note that this part of the parameter binder is the topic of some recent discussion on #11143 and #11237.
Most helpful comment
@kumbham
Because each parameter has the
Mandatoryattribute, the parameter binder considers there to be no viable parameter set.I think using another unused parameter set name like
NoneforDefaultParameterSetNamemight achieve the behavior you are expecting:The matter of creating a parameter set with no mandatory parameters at all was puzzling enough to me that it led to stackoverflow#43701900.
Note that this part of the parameter binder is the topic of some recent discussion on #11143 and #11237.