Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Conflict between DynamicParam and regular parameter

Tags:

powershell

I have a function with a dynamic parameter and a regular parameter.

The dynamic parameter is named Role and the regular one is named RolePattern.

They are part of different sets. The default set is using the dynamic parameter.

If I try to use the set with the dynamic parameter, Powershell thinks I used the regular one.

If I change the name of the regular parameter to something that doesn't start with Role, it works fine. What am I doing wrong ?

Here is the test script:

function Test-ParameterConflict {
    [CmdletBinding(DefaultParameterSetName = 'Dynamic')]
    [OutputType()]

    Param (
        [Parameter(Mandatory = $false, ParameterSetName = 'Pattern')]
        [ValidateNotNullOrEmpty()]
        [string] $RolePattern
    )

    dynamicParam {
        # Create the dictionary 
        $runtimeParameterDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary

        # Set the dynamic parameter's name
        $parameterName = 'Role'

        # Create and set the parameter's attributes
        $parameterAttribute = New-Object System.Management.Automation.ParameterAttribute
        $parameterAttribute.Mandatory = $true
        $parameterAttribute.ValueFromPipeline = $true
        $parameterAttribute.ValueFromPipelineByPropertyName = $true
        $parameterAttribute.ParameterSetName = 'Dynamic'

        # Create the collection of attributes
        $attributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute]

        # Add the attributes to the attributes collection
        $attributeCollection.Add($parameterAttribute)

        # Generate and set the ValidateSet 
        $validValues = @('Role1', 'Role2', 'Role3')
        $validateSetAttribute = New-Object System.Management.Automation.ValidateSetAttribute($validValues)

        # Add the ValidateSet to the attributes collection
        $attributeCollection.Add($validateSetAttribute)

        # Create and add the dynamic parameter to the dictionary
        $runtimeParameter = New-Object System.Management.Automation.RuntimeDefinedParameter($parameterName, [string[]], $attributeCollection)
        $runtimeParameterDictionary.Add($parameterName, $runtimeParameter)

        return $runtimeParameterDictionary
    }

    Process {
        "ParameterSetName = $($PSCmdlet.ParameterSetName)"
        $PSCmdlet.MyInvocation.BoundParameters
    }
}

So basically the syntax is:

Test-ParameterConflict [-RolePattern <string>]
Test-ParameterConflict -Role <string[]> 

Here is the output:

Test-ParameterConflict -Role Role1 | ft -AutoSize
ParameterSetName = Pattern

Key         Value
---         -----
RolePattern Role1
like image 889
geoced Avatar asked Sep 11 '26 14:09

geoced


1 Answers

This sounds like an issue with PowerShell. It is not dealing with the dynamic parameter in the same way that it deals with regular parameters - whether a bug or by design.


If I change the name of the regular parameter to something that doesn't start with 'Role', it works fine

That part is important, and explains why you are experiencing this issue. PowerShell automatically completes parameters where possible, and returns an error if the parameter name is ambigious.

For example:

PS C:\> Get-help Test -f
Get-Help : Parameter cannot be processed because the parameter name 'f' is ambiguous. Possible matches
include: -Functionality -Full.

In the script, using -R should lead to a similar error as the script should be unable to tell us we are referring to -Role or RolePattern. Instead, we get:

PS C:\> Test-ParameterConflict -R "1"
ParameterSetName = Pattern

Key         Value
---         -----
RolePattern 1

So the script is auto-completing the parameter name from -Role to -RolePattern. Even though tab complete and the syntax say otherwise. Apparently the regular parameters have a higher precedence than the dynamic parameters, so anything that matches both will be treated as -RolePattern


One workaround is to use a different name, as you mentioned in the original post. Another is to put both parameters as dynamic, or regular.

E.g. having both parameters as dynamic:

function Test-ParameterConflict {
    [CmdletBinding(DefaultParameterSetName = 'Dynamic')]
    [OutputType()]

    Param (
    )

    dynamicParam {
            # Create the dictionary 
            $runtimeParameterDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary

            # Set the dynamic parameter's name
            $parameterName = 'Role'

            # Create and set the parameter's attributes
            $parameterAttribute = New-Object System.Management.Automation.ParameterAttribute
            $parameterAttribute.Mandatory = $true
            $parameterAttribute.ValueFromPipeline = $true
            $parameterAttribute.ValueFromPipelineByPropertyName = $true
            $parameterAttribute.ParameterSetName = 'Dynamic'

            # Create the collection of attributes
            $attributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute]

            # Add the attributes to the attributes collection
            $attributeCollection.Add($parameterAttribute)

            # Generate and set the ValidateSet 
            $validValues = @('Role1', 'Role2', 'Role3')
            $validateSetAttribute = New-Object System.Management.Automation.ValidateSetAttribute($validValues)

            # Add the ValidateSet to the attributes collection
            $attributeCollection.Add($validateSetAttribute)

            # Create and add the dynamic parameter to the dictionary
            $runtimeParameter = New-Object System.Management.Automation.RuntimeDefinedParameter($parameterName, [string[]], $attributeCollection)
            $runtimeParameterDictionary.Add($parameterName, $runtimeParameter)

            #===============================================================================
            # Copied from above, with different $parameterName and $validValues

            $parameterName = 'RolePattern'

            $parameterAttribute = New-Object System.Management.Automation.ParameterAttribute
            $parameterAttribute.Mandatory = $true
            $parameterAttribute.ValueFromPipeline = $true
            $parameterAttribute.ValueFromPipelineByPropertyName = $true
            $parameterAttribute.ParameterSetName = 'Pattern'
            $attributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute]
            $attributeCollection.Add($parameterAttribute)

            $validValues = @('Role4', 'Role5', 'Role6')

            $validateSetAttribute = New-Object System.Management.Automation.ValidateSetAttribute($validValues)
            $attributeCollection.Add($validateSetAttribute)
            $runtimeParameter = New-Object System.Management.Automation.RuntimeDefinedParameter($parameterName, [string[]], $attributeCollection)
            $runtimeParameterDictionary.Add($parameterName, $runtimeParameter)
             #===============================================================================

            return $runtimeParameterDictionary
    }

    Process {
        "ParameterSetName = $($PSCmdlet.ParameterSetName)"
        $PSCmdlet.MyInvocation.BoundParameters
    }
}
like image 140
G42 Avatar answered Sep 15 '26 02:09

G42



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!