powershellpowershell-coretab-completionpowershell-7.3validateset

How to prevent PowerShell validateSet argument completer from suggesting the same values that are already selected?


I have 2 class-based argument completers/validate sets for 2 of my PowerShell module's parameters.

[ValidateSet([PolicyIDz])][parameter(Mandatory = $false, ParameterSetName = "Remove Policies")][string[]]$PolicyIDs,
[ValidateSet([PolicyNamez])][parameter(Mandatory = $false, ParameterSetName = "Remove Policies")][string[]]$PolicyNames,
# argument tab auto-completion and ValidateSet for Policy names 
Class PolicyNamez : System.Management.Automation.IValidateSetValuesGenerator {
    [string[]] GetValidValues() {
        $PolicyNamez = ((CiTool -lp -json | ConvertFrom-Json).Policies | Where-Object { $_.IsSystemPolicy -ne "True" }).Friendlyname
           
        return [string[]]$PolicyNamez
    }
}   
    
# argument tab auto-completion and ValidateSet for Policy IDs     
Class PolicyIDz : System.Management.Automation.IValidateSetValuesGenerator {
    [string[]] GetValidValues() {
        $PolicyIDz = ((CiTool -lp -json | ConvertFrom-Json).Policies | Where-Object { $_.IsSystemPolicy -ne "True" }).policyID
           
        return [string[]]$PolicyIDz
    }
}

They are for Windows Defender Application Control and if you want to try it you need at least Windows 11 22H2 which has CITool built-in.

I want to have both validate set and argument completion for each of those parameters, and on top of that, prevent argument completer from suggesting the same values that I've already selected. I'm using latest PowerShell 7.4 version. Both of those parameters are used in the same cmdlet.

Remove-WDACConfig [-RemovePolicies] [-PolicyIDs <String[]>] [-PolicyNames <String[]>]

This question is related to another one I asked previously (and got answers).


This is the current behavior I'm trying to change

https://1drv.ms/u/s!AtCaUNAJbbvIhupxuJSHh3kSkBNTxw?e=KAtshL


Solution


  • The following is a simplified, self-contained example that uses hard-coded policy IDs and names and defines function Foo with 2 (positional) parameters that tab-complete as desired.

    # Argument tab auto-completion and ValidateSet for Policy names.
    Class PolicyNamez : System.Management.Automation.IValidateSetValuesGenerator {
      [string[]] GetValidValues() {
        # Use *hard-coded values for this sample code.
        return [string[]] ("VerifiedAndReputableDesktopFlightSupplemental", "VerifiedAndReputableDesktopEvaluationFlightSupplemental", "WindowsE_Lockdown_Flight_Policy_Supplemental", "Microsoft Windows Driver Policy")
      }
    }   
      
    # Argument tab auto-completion and ValidateSet for Policy IDs.
    Class PolicyIDz : System.Management.Automation.IValidateSetValuesGenerator {
      [string[]] GetValidValues() {
        # Use *hard-coded values for this sample code.
        return [string[]] ("1658656c-05ed-481f-bc5b-ebd8c091502d", "2698656d-05ea-481c-bc5b-ebd8c991802d", "5eaf656c-29ad-4a12-ab59-648917362e70", "d2bda972-cdf9-4364-ac5d-0b44497f6816")
      }
    }
    
    # Sample function.
    function Foo {
      [CmdletBinding()]
      param(
        [ArgumentCompleter({
            param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
            $candidates = [PolicyIDz]::new().GetValidValues()
            $existing = $commandAst.FindAll({ 
                $args[0] -is [System.Management.Automation.Language.StringConstantExpressionAst]
              }, 
              $false
            ).Value  
            Compare-Object -PassThru $candidates $existing | Where-Object SideIndicator -eq '<='
          })]
        [ValidateScript({
            if ($_ -notin [PolicyIDz]::new().GetValidValues()) { throw "Invalid policy ID: $_" }
            $true
          })]
        [string[]]$PolicyIDs,
    
        [ArgumentCompleter({
            param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
            $candidates = [PolicyNamez]::new().GetValidValues()
            $existing = $commandAst.FindAll({ 
                $args[0] -is [System.Management.Automation.Language.StringConstantExpressionAst]
              }, 
              $false
            ).Value  
          (Compare-Object -PassThru $candidates $existing | Where-Object SideIndicator -eq '<=').
            ForEach({ if ($_ -match ' ') { "'{0}'" -f $_ } else { $_ } })
          })]
        [ValidateScript({
            if ($_ -notin [PolicyNamez]::new().GetValidValues()) { throw "Invalid policy name: $_" }
            $true
          })]
        [string[]]$PolicyNames
      )
    }