none
ParameterSet - ValidateSet RRS feed

  • Question

  • For the convenience of tab-completion helper, would like to have a ValidateSet which is determined by ParameterSetName.

    As a simple example, consider:

    Function Validate {
        param(
            [Parameter(Mandatory=$true,
            ParameterSetName="Set1")]
            [switch]
            $Param1,
            [Parameter(Mandatory=$true,
            ParameterSetName="Set2")]
            [switch]
            $Param2,
            [Parameter(Mandatory=$true,
            ParameterSetName="Set1")]
            [Parameter(Mandatory=$true,
            ParameterSetName="Set2")]
            [ValidateScript({
                switch ($PSCmdlet.ParameterSetName) {
                    "Set2" { "Set2","#2 Second Param" }
                    "Set1" { "Set1","#1 Second Param" }
                    Default { throw $ValidSet }
                }
            })]
            $SubParam
        )
        $PSCmdlet.ParameterSetName
        $SubParam
    }

    So, when calling the function Validate I can use -Param1 or -Param2, but not both.  Now, for the part in question, when using -Param1 -SubParam tab-completion should offer 'Set1' or '#1 Second Param'; conversely, when using -Param2 -SubParam tab-completion should offer 'Set2' or '#2 Second Param'.  Is this script parameter validation (tab-completion) behavior possible?  If so, how?

    I know [ValidateSet("Set1","Set2","#1 Second Param","#2 Second Param")] is possible, but not a viable solution to the greater Cmdlet/Module/Function I am working on.

    Thursday, January 2, 2014 5:23 PM

Answers

  • I realize this is just a simplified example, but could you adapt your function to do something like this?

    function Validate {
    
        param(
            [Parameter(Mandatory=$true, ParameterSetName="Set1")]
            [ValidateSet("Set1","#1 Second Param")]
            $Set1,
            [Parameter(Mandatory=$true, ParameterSetName="Set2")]
            [ValidateSet("Set2","#2 Second Param")]
            $Set2
        )
    
        $SubParam = $PsBoundParameters[$PSCmdlet.ParameterSetName]
    
        "ParameterSet: {0}" -f $PSCmdlet.ParameterSetName
        "SubParam: {0}" -f $SubParam
    }

    If not, here's a way you can do what I think you were originally asking. It uses dynamic parameters, though, which can be a little tricky:

    function Validate {
        param(
            [Parameter(Mandatory=$true, ParameterSetName="Set1")]
            [switch] $Param1,
            [Parameter(Mandatory=$true, ParameterSetName="Set2")]
            [switch] $Param2,
            [Parameter(ParameterSetName="Set3")]
            [switch] $Param3   # SubParam won't be limited
        )
    
        dynamicparam {
    
            # Create the dictionary that this scriptblock will return:
            $DynParamDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
    
            $ParamName = "SubParam"
            $AttributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute]
    
            # [Parameter()] stuff goes here:
            $ParamAttributes = New-Object System.Management.Automation.ParameterAttribute
            $ParamAttributes.Mandatory = $true
            $AttributeCollection.Add($ParamAttributes)
    
    
            # Now we'll add [ValidateSet()] stuff:
            switch ($PSCmdlet.ParameterSetName) {
                "Set1" {
                    $ValidateSet = New-Object System.Management.Automation.ValidateSetAttribute ("Set1","#1 Second Param")
                }
    
                "Set2" {
                    $ValidateSet = New-Object System.Management.Automation.ValidateSetAttribute ("Set2","#2 Second Param")
                }
    
                default {
                    # Null attribute won't be added
                    $ValidateSet = $null
                }
            }
    
            if ($ValidateSet) {
                $AttributeCollection.Add($ValidateSet)
            }
    
            # Create the actual parameter:
            $DynamicParameter = New-Object System.Management.Automation.RuntimeDefinedParameter (
                $ParamName,
                [string],   # Or whatever type you want
                $AttributeCollection
            )
    
            $DynParamDictionary.Add($ParamName, $DynamicParameter)
    
            # Return the dynamic parameters
            $DynParamDictionary
        }
    
        end {
            # If this isn't a pipelined function, put your
            # function code here; otherwise create begin{}
            # and/or process{} blocks
    
            "ParameterSet: {0}" -f $PSCmdlet.ParameterSetName
            "SubParam: {0}" -f $PsBoundParameters["SubParam"]
        }
    }

    I've added a third switch, Param3, that shows that SubParam can take anything (it's not limited by ValidateSet()).

    Please let me know if that works, and if you have any questions about what's going on.

    • Proposed as answer by David Wyatt Thursday, January 2, 2014 7:45 PM
    • Marked as answer by -J-o-h-n- Thursday, January 2, 2014 8:19 PM
    Thursday, January 2, 2014 5:59 PM

All replies

  • I realize this is just a simplified example, but could you adapt your function to do something like this?

    function Validate {
    
        param(
            [Parameter(Mandatory=$true, ParameterSetName="Set1")]
            [ValidateSet("Set1","#1 Second Param")]
            $Set1,
            [Parameter(Mandatory=$true, ParameterSetName="Set2")]
            [ValidateSet("Set2","#2 Second Param")]
            $Set2
        )
    
        $SubParam = $PsBoundParameters[$PSCmdlet.ParameterSetName]
    
        "ParameterSet: {0}" -f $PSCmdlet.ParameterSetName
        "SubParam: {0}" -f $SubParam
    }

    If not, here's a way you can do what I think you were originally asking. It uses dynamic parameters, though, which can be a little tricky:

    function Validate {
        param(
            [Parameter(Mandatory=$true, ParameterSetName="Set1")]
            [switch] $Param1,
            [Parameter(Mandatory=$true, ParameterSetName="Set2")]
            [switch] $Param2,
            [Parameter(ParameterSetName="Set3")]
            [switch] $Param3   # SubParam won't be limited
        )
    
        dynamicparam {
    
            # Create the dictionary that this scriptblock will return:
            $DynParamDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
    
            $ParamName = "SubParam"
            $AttributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute]
    
            # [Parameter()] stuff goes here:
            $ParamAttributes = New-Object System.Management.Automation.ParameterAttribute
            $ParamAttributes.Mandatory = $true
            $AttributeCollection.Add($ParamAttributes)
    
    
            # Now we'll add [ValidateSet()] stuff:
            switch ($PSCmdlet.ParameterSetName) {
                "Set1" {
                    $ValidateSet = New-Object System.Management.Automation.ValidateSetAttribute ("Set1","#1 Second Param")
                }
    
                "Set2" {
                    $ValidateSet = New-Object System.Management.Automation.ValidateSetAttribute ("Set2","#2 Second Param")
                }
    
                default {
                    # Null attribute won't be added
                    $ValidateSet = $null
                }
            }
    
            if ($ValidateSet) {
                $AttributeCollection.Add($ValidateSet)
            }
    
            # Create the actual parameter:
            $DynamicParameter = New-Object System.Management.Automation.RuntimeDefinedParameter (
                $ParamName,
                [string],   # Or whatever type you want
                $AttributeCollection
            )
    
            $DynParamDictionary.Add($ParamName, $DynamicParameter)
    
            # Return the dynamic parameters
            $DynParamDictionary
        }
    
        end {
            # If this isn't a pipelined function, put your
            # function code here; otherwise create begin{}
            # and/or process{} blocks
    
            "ParameterSet: {0}" -f $PSCmdlet.ParameterSetName
            "SubParam: {0}" -f $PsBoundParameters["SubParam"]
        }
    }

    I've added a third switch, Param3, that shows that SubParam can take anything (it's not limited by ValidateSet()).

    Please let me know if that works, and if you have any questions about what's going on.

    • Proposed as answer by David Wyatt Thursday, January 2, 2014 7:45 PM
    • Marked as answer by -J-o-h-n- Thursday, January 2, 2014 8:19 PM
    Thursday, January 2, 2014 5:59 PM
  • Thanks and awesome work, Rohn!  I will be able to use this in multiple other projects!

    It took me a few minutes to chew through it all, but I figured how to tweak it to my specs.  I needed to add an extra ParameterAttribute in the System.Attribute collection.  SubParam is only needed for Set1 and Set2, but not by itself or for Set3.  The greater Cmdlet/Module/Function I am working on will use Invoke-WebRequest to query or post to a certain site, it can query any one of the 3 Params, but can only post to 2 Params.  Ex.:
    Param1:  http://site.com/report1 {query;post}
    Param2:  http://site.com/report2 {query;post}
    Param3:  http://site.com/summary {query}

    I will post my quick edits just in case someone else needs something similar.  Thanks again, Rohn!

    function Validate {
        param(
            [Parameter(Mandatory=$true, ParameterSetName="Set1")]
            [switch] $Param1,
            [Parameter(Mandatory=$true, ParameterSetName="Set2")]
            [switch] $Param2,
            [Parameter(Mandatory=$true, ParameterSetName="Set3")]
            [switch] $Param3   # SubParam won't be available
        )
    
        dynamicparam {
    
            # Create the dictionary that this scriptblock will return:
            $DynParamDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
    
            $ParamName = "SubParam"
            $AttributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute]
    
            # [Parameter()] stuff goes here:
            $ParamAttributes = New-Object System.Management.Automation.ParameterAttribute
            $ParamAttributes.Mandatory = $false
            $ParamAttributes.ParameterSetName = "Set1"
            $AttributeCollection.Add($ParamAttributes)
    
            # [Parameter()] stuff goes here:
            $ParamAttributes = New-Object System.Management.Automation.ParameterAttribute
            $ParamAttributes.Mandatory = $false
            $ParamAttributes.ParameterSetName = "Set2"
            $AttributeCollection.Add($ParamAttributes)
    
            # Now we'll add [ValidateSet()] stuff:
            switch ($PSCmdlet.ParameterSetName) {
                "Set1" {
                    $ValidateSet = New-Object System.Management.Automation.ValidateSetAttribute ("Set1","#1 Second Param")
                }
    
                "Set2" {
                    $ValidateSet = New-Object System.Management.Automation.ValidateSetAttribute ("Set2","#2 Second Param")
                }
            }
    
            $AttributeCollection.Add($ValidateSet)
    
            # Create the actual parameter:
            $DynamicParameter = New-Object System.Management.Automation.RuntimeDefinedParameter (
                $ParamName,
                [string],   # Or whatever type you want
                $AttributeCollection
            )
    
            $DynParamDictionary.Add($ParamName, $DynamicParameter)
    
            # Return the dynamic parameters
            $DynParamDictionary
        }
    
        process {
            "ParameterSet: {0}" -f $PSCmdlet.ParameterSetName
            if ($PSCmdlet.ParameterSetName -ne "Set3") {"SubParam: {0}" -f $PsBoundParameters["SubParam"]}
        }
    }

    Thursday, January 2, 2014 8:18 PM