powershellparameterspowershell-7.2

How can I prevent non position 0 parameters to appear in position 0 in PowerShell?


Here are my parameters of one of the nested modules in my main module:

    Param(
        [Parameter(Mandatory = $false, ParameterSetName = "set1", Position = 0, ValueFromPipeline = $true)][switch]$Get_BlockRules,
        [Parameter(Mandatory = $false, ParameterSetName = "set2", Position = 0, ValueFromPipeline = $true)][switch]$Get_DriverBlockRules,
        [Parameter(Mandatory = $false, ParameterSetName = "set3", Position = 0, ValueFromPipeline = $true)][switch]$Make_AllowMSFT_WithBlockRules,  
        [Parameter(Mandatory = $false, ParameterSetName = "set4", Position = 0, ValueFromPipeline = $true)][switch]$Deploy_LatestDriverBlockRules,                                                                                       
        [Parameter(Mandatory = $false, ParameterSetName = "set5", Position = 0, ValueFromPipeline = $true)][switch]$Set_AutoUpdateDriverBlockRules,
        [Parameter(Mandatory = $false, ParameterSetName = "set6", Position = 0, ValueFromPipeline = $true)][switch]$Prep_MSFTOnlyAudit,
        [Parameter(Mandatory = $false, ParameterSetName = "set7", Position = 0, ValueFromPipeline = $true)][switch]$Make_PolicyFromAuditLogs,  
        [Parameter(Mandatory = $false, ParameterSetName = "set8", Position = 0, ValueFromPipeline = $true)][switch]$Make_LightPolicy,
        [Parameter(Mandatory = $false, ParameterSetName = "set9", Position = 0, ValueFromPipeline = $true)][switch]$Make_SuppPolicy,
        [Parameter(Mandatory = $false, ParameterSetName = "set10", Position = 0, ValueFromPipeline = $true)][switch]$Make_DefaultWindows_WithBlockRules,
       
        [parameter(Mandatory = $true, ParameterSetName = "set9", ValueFromPipelineByPropertyName = $true)][string]$ScanLocation,
        [parameter(Mandatory = $true, ParameterSetName = "set9", ValueFromPipelineByPropertyName = $true)][string]$SuppPolicyName,
        [ValidatePattern('.*\.xml')][parameter(Mandatory = $true, ParameterSetName = "set9", ValueFromPipelineByPropertyName = $true)][string]$PolicyPath,

        [Parameter(Mandatory = $false, ParameterSetName = "set3")]
        [Parameter(Mandatory = $false, ParameterSetName = "set7")]
        [Parameter(Mandatory = $false, ParameterSetName = "set8")]        
        [parameter(Mandatory = $false, ParameterSetName = "set9")]
        [switch]$Deployit,

        [Parameter(Mandatory = $false, ParameterSetName = "set8")]
        [Parameter(Mandatory = $false, ParameterSetName = "set7")]
        [Parameter(Mandatory = $false, ParameterSetName = "set3")]
        [switch]$TestMode,
        
        [Parameter(Mandatory = $false, ParameterSetName = "set3")]
        [Parameter(Mandatory = $false, ParameterSetName = "set7")]
        [Parameter(Mandatory = $false, ParameterSetName = "set8")]
        [switch]$RequireEVSigners,

        [Parameter(Mandatory = $false, ParameterSetName = "set7")][switch]$Debugmode,

        [ValidateSet([Levelz])]
        [parameter(Mandatory = $false, ParameterSetName = "set7")]
        [parameter(Mandatory = $false, ParameterSetName = "set9")]
        [string]$Levels,

        [ValidateSet([Fallbackz])]
        [parameter(Mandatory = $false, ParameterSetName = "set7")]
        [parameter(Mandatory = $false, ParameterSetName = "set9")]
        [string[]]$Fallbacks, 

        [ValidateRange(1024KB, [int64]::MaxValue)]
        [Parameter(Mandatory = $false, ParameterSetName = "set6")]
        [Parameter(Mandatory = $false, ParameterSetName = "set7")]        
        [Int64]$LogSize,

        [Parameter(Mandatory = $false)][switch]$SkipVersionCheck    
    )

How can I prevent parameters that are not position 0 to appear in position 0?

I've tried adding other positions like 1,2 etc. to other parameters but that didn't work.

Only parameters with position 0 are the main ones, the rest only need to be used/suggested when a position 0 parameter is first selected by the user.

To clarify, I'm using this

Set-PSReadlineKeyHandler -Key Tab -Function MenuComplete

and seeing parameters that don't belong to position 0 in the menu is just weird and I don't want them to appear there or be selectable.

enter image description here

the yellow underlines show parameters that don't make sense on their own if used in position 0.

Update, using Dynamic parameters, I did this but still the other yellow underlined parameters are showing up for position 0.

So maybe someone can post an answer showing how I can actually implement it in my function.

#requires -version 7.3.3
function New-WDACConfig {
    [CmdletBinding(
        DefaultParameterSetName = "set1",
        HelpURI = "https://github.com/HotCakeX/Harden-Windows-Security/wiki/WDACConfig",
        SupportsShouldProcess = $true,
        PositionalBinding = $false,
        ConfirmImpact = 'High'
    )]
    Param(
        [Parameter(Mandatory = $false, ParameterSetName = "set1")][switch]$Get_BlockRules,
        [Parameter(Mandatory = $false, ParameterSetName = "set2")][switch]$Get_DriverBlockRules,
        [Parameter(Mandatory = $false, ParameterSetName = "set3")][switch]$Make_AllowMSFT_WithBlockRules,  
        [Parameter(Mandatory = $false, ParameterSetName = "set4")][switch]$Deploy_LatestDriverBlockRules,                                                                                       
        [Parameter(Mandatory = $false, ParameterSetName = "set5")][switch]$Set_AutoUpdateDriverBlockRules,
        [Parameter(Mandatory = $false, ParameterSetName = "set6")][switch]$Prep_MSFTOnlyAudit,
        [Parameter(Mandatory = $false, ParameterSetName = "set7")][switch]$Make_PolicyFromAuditLogs,  
        [Parameter(Mandatory = $false, ParameterSetName = "set8")][switch]$Make_LightPolicy,
        [Parameter(Mandatory = $false, ParameterSetName = "set9")][switch]$Make_SuppPolicy,
        [Parameter(Mandatory = $false, ParameterSetName = "set10")][switch]$Make_DefaultWindows_WithBlockRules,
       
        [parameter(Mandatory = $true, ParameterSetName = "set9", ValueFromPipelineByPropertyName = $true)][string]$ScanLocation,
        [parameter(Mandatory = $true, ParameterSetName = "set9", ValueFromPipelineByPropertyName = $true)][string]$SuppPolicyName,
        
        [ValidatePattern('.*\.xml')]
        [parameter(Mandatory = $true, ParameterSetName = "set9", ValueFromPipelineByPropertyName = $true)]
        [string]$PolicyPath,

        [Parameter(Mandatory = $false, ParameterSetName = "set3")]
        [Parameter(Mandatory = $false, ParameterSetName = "set7")]
        [Parameter(Mandatory = $false, ParameterSetName = "set8")]        
        [parameter(Mandatory = $false, ParameterSetName = "set9")]
        [switch]$Deployit,

        [Parameter(Mandatory = $false, ParameterSetName = "set8")]
        [Parameter(Mandatory = $false, ParameterSetName = "set7")]
        [Parameter(Mandatory = $false, ParameterSetName = "set3")]
        [switch]$TestMode,
        
        [Parameter(Mandatory = $false, ParameterSetName = "set3")]
        [Parameter(Mandatory = $false, ParameterSetName = "set7")]
        [Parameter(Mandatory = $false, ParameterSetName = "set8")]
        [switch]$RequireEVSigners,

        [Parameter(Mandatory = $false, ParameterSetName = "set7")][switch]$Debugmode,

        [ValidateSet([Levelz])]
        [parameter(Mandatory = $false, ParameterSetName = "set7")]
        [parameter(Mandatory = $false, ParameterSetName = "set9")]
        [string]$Levels,

        [ValidateSet([Fallbackz])]
        [parameter(Mandatory = $false, ParameterSetName = "set7")]
        [parameter(Mandatory = $false, ParameterSetName = "set9")]
        [string[]]$Fallbacks, 

        [ValidateRange(1024KB, [int64]::MaxValue)]
        [Parameter(Mandatory = $false, ParameterSetName = "set6")]
        [Parameter(Mandatory = $false, ParameterSetName = "set7")]        
        [Int64]$LogSize,

        [Parameter(Mandatory = $false)][switch]$SkipVersionCheck
            
    )


   # parameter control for Make_AllowMSFT_WithBlockRules - Set3
   DynamicParam {
    if ($PSCmdlet.ParameterSetName -eq "Set3") {
        $paramDictionary = [System.Management.Automation.RuntimeDefinedParameterDictionary]::new()
        foreach ($param in 'RequireEVSigners', 'TestMode', 'Deployit') {
            [Parameter[]] $paramAttribute = [Parameter]@{ ParameterSetName = "Set3" }
            $runtimeParam = [System.Management.Automation.RuntimeDefinedParameter]::new($param, [Int64], $paramAttribute)
            $paramDictionary.Add($param, $runtimeParam)
        }
        return $paramDictionary
    }
 

    # parameter control for Prep_MSFTOnlyAudit - Set6

    if ($PSCmdlet.ParameterSetName -eq "Set6") {
        $paramDictionary = [System.Management.Automation.RuntimeDefinedParameterDictionary]::new()
        foreach ($param in 'LogSize') {
            [Parameter[]] $paramAttribute = [Parameter]@{ ParameterSetName = "Set6" }
            $runtimeParam = [System.Management.Automation.RuntimeDefinedParameter]::new($param, [Int64], $paramAttribute)
            $paramDictionary.Add($param, $runtimeParam)
        }
        return $paramDictionary
    }


    # parameter control for Make_PolicyFromAuditLogs - Set7

    if ($PSCmdlet.ParameterSetName -eq "Set7") {
        $paramDictionary = [System.Management.Automation.RuntimeDefinedParameterDictionary]::new()
        foreach ($param in 'Deployit', 'TestMode', 'RequireEVSigners') {
            [Parameter[]] $paramAttribute = [Parameter]@{ ParameterSetName = "Set7" }
            $runtimeParam = [System.Management.Automation.RuntimeDefinedParameter]::new($param, [switch], $paramAttribute)
            $paramDictionary.Add($param, $runtimeParam)
        }
        return $paramDictionary
    }

    if ($PSCmdlet.ParameterSetName -eq "Set7") {
        $paramDictionary = [System.Management.Automation.RuntimeDefinedParameterDictionary]::new()
        foreach ($param in 'Fallbacks', 'Levels') {
            [Parameter[]] $paramAttribute = [Parameter]@{ ParameterSetName = "Set7" }
            $runtimeParam = [System.Management.Automation.RuntimeDefinedParameter]::new($param, [string], $paramAttribute)
            $paramDictionary.Add($param, $runtimeParam)
        }
        return $paramDictionary
    }

    if ($PSCmdlet.ParameterSetName -eq "Set7") {
        $paramDictionary = [System.Management.Automation.RuntimeDefinedParameterDictionary]::new()
        foreach ($param in 'LogSize') {
            [Parameter[]] $paramAttribute = [Parameter]@{ ParameterSetName = "Set7" }
            $runtimeParam = [System.Management.Automation.RuntimeDefinedParameter]::new($param, [Int64], $paramAttribute)
            $paramDictionary.Add($param, $runtimeParam)
        }
        return $paramDictionary
    }


    # parameter control for Make_LightPolicy - Set8

    if ($PSCmdlet.ParameterSetName -eq "Set8") {
        $paramDictionary = [System.Management.Automation.RuntimeDefinedParameterDictionary]::new()
        foreach ($param in 'Deployit', 'TestMode', 'RequireEVSigners') {
            [Parameter[]] $paramAttribute = [Parameter]@{ ParameterSetName = "Set8" }
            $runtimeParam = [System.Management.Automation.RuntimeDefinedParameter]::new($param, [switch], $paramAttribute)
            $paramDictionary.Add($param, $runtimeParam)
        }
        return $paramDictionary
    }


    # parameter control for Make_SuppPolicy - Set9

    if ($PSCmdlet.ParameterSetName -eq "Set9") {
        $paramDictionary = [System.Management.Automation.RuntimeDefinedParameterDictionary]::new()
        foreach ($param in 'Fallbacks', 'Levels', 'Deployit', 'PolicyPath', 'SuppPolicyName', 'ScanLocation') {
            [Parameter[]] $paramAttribute = [Parameter]@{ ParameterSetName = "Set9" }
            $runtimeParam = [System.Management.Automation.RuntimeDefinedParameter]::new($param, [switch], $paramAttribute)
            $paramDictionary.Add($param, $runtimeParam)
        }
        return $paramDictionary
    }

    if ($PSCmdlet.ParameterSetName -eq "Set9") {
        $paramDictionary = [System.Management.Automation.RuntimeDefinedParameterDictionary]::new()
        foreach ($param in 'Fallbacks', 'Levels', 'PolicyPath', 'SuppPolicyName', 'ScanLocation') {
            [Parameter[]] $paramAttribute = [Parameter]@{ ParameterSetName = "Set9" }
            $runtimeParam = [System.Management.Automation.RuntimeDefinedParameter]::new($param, [string], $paramAttribute)
            $paramDictionary.Add($param, $runtimeParam)
        }
        return $paramDictionary
    }

    if ($PSCmdlet.ParameterSetName -eq "Set9") {
        $paramDictionary = [System.Management.Automation.RuntimeDefinedParameterDictionary]::new()
        foreach ($param in 'Deployit') {
            [Parameter[]] $paramAttribute = [Parameter]@{ ParameterSetName = "Set9" }
            $runtimeParam = [System.Management.Automation.RuntimeDefinedParameter]::new($param, [switch], $paramAttribute)
            $paramDictionary.Add($param, $runtimeParam)
        }
        return $paramDictionary
    }
}
}
}

Solution

  • To complement briantist's helpful answer:

    Context:


    Solution:

    The only way to ensure that only the desired subset of parameters is shown when you initially type - and tab-complete is to:

    A minimal example:

    [CmdletBinding(PositionalBinding = $false)]
    param(
      # The only parameters to show initially.
      [Parameter(Mandatory, ParameterSetName = "set1")] [switch] $Get_BlockRules,
      [Parameter(Mandatory, ParameterSetName = "set2")] [switch] $Get_DriverBlockRules,
      [Parameter(Mandatory, ParameterSetName = "set3")] [switch] $Make_AllowMSFT_WithBlockRules
    
      # All other parameters are declared *dynamically*
    )
    
    dynamicParam {
      # Create a dynamic-parameters dictionary
      $dict = [System.Management.Automation.RuntimeDefinedParameterDictionary]::new()
            
      # Define and add the -Deployit switch, which only belongs to 
      # parameter set "set3"
      $paramName = 'Deployit'
      $dict.Add(
        $paramName,
        [System.Management.Automation.RuntimeDefinedParameter]::new(
          $paramName,
          [switch],
          [System.Management.Automation.ParameterAttribute] @{
            ParameterSetName = 'set3'
          }
        )
      )
    
      # Define and add the -SkipVersionCheck switch, which belongs to *all*
      # parameter sets.
      $paramName = 'SkipVersionCheck'
      $dict.Add(
        $paramName,
        [System.Management.Automation.RuntimeDefinedParameter]::new(
          $paramName,
          [switch],
          [System.Management.Automation.ParameterAttribute] @{
            ParameterSetName = '__allParameterSets'
          }
        )
      )
    
      # Return the dictionary
      return $dict
    }
    
    process {
      # Print all parameters that were bound.
      $PSBoundParameters
    }
    

    Note: