2

Let's say I have a function like:

function Authenticate
{
    param
    (
        [ValidateSet('WindowsAuthentication','UsernameAndPassword')]
        [string]$AuthenticationType,

        [Parameter(ParameterSetName='ParamSet1')]
        [string]$Username,

        [Parameter(ParameterSetName='ParamSet1')]
        [string]$Password
    )
  ..
}

And I would like to enforce these rules:

  • Make the parameter set ParamSet1 mandatory if $AuthenticationType = 'UsernameAndPassword'
  • "Disable" ParamSet1 entirely if $AuthenticationType = 'WindowsAuthentication'

It this possible?

Paul π
  • 133
  • 11
cogumel0
  • 2,430
  • 5
  • 29
  • 45

2 Answers2

5

Using the link from Tim Ferrill's answer, I created the following function to help create dynamic parameters:

function New-DynamicParameter
{
    [CmdletBinding(DefaultParameterSetName = 'Core')]    
    param
    (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true)][string] $Name,
        [Parameter(Mandatory = $true, ParameterSetName = 'Core')][Parameter(Mandatory = $true, ParameterSetName = 'ValidateSet')][type] $Type,
        [Parameter(Mandatory = $false)][string] $ParameterSetName = '__AllParameterSets',
        [Parameter(Mandatory = $false)][bool] $Mandatory = $false,
        [Parameter(Mandatory = $false)][int] $Position,
        [Parameter(Mandatory = $false)][bool] $ValueFromPipelineByPropertyName = $false,
        [Parameter(Mandatory = $false)][string] $HelpMessage,
        [Parameter(Mandatory = $true, ParameterSetName = 'ValidateSet')][string[]] $ValidateSet,
        [Parameter(Mandatory = $false, ParameterSetName = 'ValidateSet')][bool] $IgnoreCase = $true
    )

    process
    {
        # Define Parameter Attributes
        $ParameterAttribute = New-Object System.Management.Automation.ParameterAttribute
        $ParameterAttribute.ParameterSetName = $ParameterSetName
        $ParameterAttribute.Mandatory = $Mandatory
        $ParameterAttribute.Position = $Position
        $ParameterAttribute.ValueFromPipelineByPropertyName = $ValueFromPipelineByPropertyName
        $ParameterAttribute.HelpMessage = $HelpMessage

        # Define Parameter Validation Options if ValidateSet set was used
        if ($PSCmdlet.ParameterSetName -eq 'ValidateSet')
        {
            $ParameterValidateSet = New-Object System.Management.Automation.ValidateSetAttribute -ArgumentList $ValidateSet -Strict (!$IgnoreCase)
        }

        # Add Parameter Attributes and ValidateSet to an Attribute Collection
        $AttributeCollection = New-Object Collections.ObjectModel.Collection[System.Attribute]
        $AttributeCollection.Add($ParameterAttribute)
        $AttributeCollection.Add($ParameterValidateSet)

        # Add parameter to parameter list
        $Parameter = New-Object System.Management.Automation.RuntimeDefinedParameter -ArgumentList @($Name, $Type, $AttributeCollection)

        # Expose parameter to the namespace
        $ParameterDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
        $ParameterDictionary.Add($Name, $Parameter)
        return $ParameterDictionary
    }
}

And solved my particular problem in the following way:

function Authenticate
{
  param
  (
    [ValidateSet('WindowsAuthentication','UsernameAndPassword')][string] $AuthenticationType,
  )

  DynamicParam
  {
    if ($AuthenticationType -eq 'UsernameAndPassword')
    {
      New-DynamicParameter Username [string] -Mandatory $true
      New-DynamicParameter Password [string] -Mandatory $true
    }
  }

  ...
}

It became unneeded to have a parameter set when using Dynamic Parameter so I removed the parameter set.

cogumel0
  • 2,430
  • 5
  • 29
  • 45
3

You can do this using DynamicParam. I saw a decent post on this recently here.

DynamicParam {
    if ($AuthenticationType -eq 'UsernameAndPassword') {
        #create ParameterAttribute Objects for the username and password
        $unAttribute = New-Object System.Management.Automation.ParameterAttribute
        $unAttribute.Mandatory = $true
        $unAttribute.HelpMessage = "Please enter your username:"
        $pwAttribute = New-Object System.Management.Automation.ParameterAttribute
        $pwAttribute.Mandatory = $true
        $pwAttribute.HelpMessage = "Please enter a password:"

        #create an attributecollection object for the attributes we just created.
        $attributeCollection = new-object System.Collections.ObjectModel.Collection[System.Attribute]

        #add our custom attributes
        $attributeCollection.Add($unAttribute)
        $attributeCollection.Add($pwAttribute)

        #add our paramater specifying the attribute collection
        $unParam = New-Object System.Management.Automation.RuntimeDefinedParameter('username', [string], $attributeCollection)
        $pwParam = New-Object System.Management.Automation.RuntimeDefinedParameter('password', [string], $attributeCollection)

        #expose the name of our parameter
        $paramDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
        $paramDictionary.Add('username', $unParam)
        $paramDictionary.Add('password', $pwParam)
        return $paramDictionary
    }
}

Process {
    $PSBoundParameters.username
    $PSBoundParameters.password
    }
Tim Ferrill
  • 1,648
  • 1
  • 12
  • 15
  • Your answer is partially correct and definitely pointed me in the right direction, but it does not state everything you have to do to define a dynamic parameter (the link does shows "most of it" though) – cogumel0 Jun 16 '14 at 11:45
  • Your question asked if you could make one parameter dependent on another. Did I miss something? – Tim Ferrill Jun 16 '14 at 14:44
  • I'd love to fix this for you, but I don't have access to StackOverflow from work other than with my phone. I'll fix it when I can. – Tim Ferrill Jun 16 '14 at 16:49
  • Updated and tested. This should reflect the full process. – Tim Ferrill Jun 17 '14 at 01:16
  • Much appreciated. Looks a lot better now. – cogumel0 Jun 17 '14 at 13:40
  • Shouldn't there be a separate $attributeCollection for each ParameterAttribute Object? How would the RuntimeDefinedParameter know which attributes belong to which parameter? – drouning Jan 15 '18 at 14:07