kurbitz
kurbitz

Reputation: 23

How to make ValidateSet show different data depending on previous parameters

I want to validate user inputs in a script which sets the attributes Company, Department, and Office in Active Directory.

This is what I've got so far:

$ApprovedLocations = #A table with known Company,Department,Office values
Class Company : System.Management.Automation.IValidateSetValuesGenerator {
    [String[]] GetValidValues(){
        return $global:ApprovedLocations.Company
    }
}
Class Department : System.Management.Automation.IValidateSetValuesGenerator {
    [String[]] GetValidValues(){
        return $global:ApprovedLocations.Department
    }
}
Class Office : System.Management.Automation.IValidateSetValuesGenerator {
    [String[]] GetValidValues(){
        return $global:ApprovedLocations.Office
    }
}    
function Set-AttributeTest{
    param (
        [Parameter(Mandatory)][string]$Name,
        [ValidateSet([Company])][string]$Company,
        [ValidateSet([Department])][string]$Department,
        [ValidateSet([Office])][string]$Office
    )
#Do stuff
}

This works to prevent typos. But since not all companies have the same departments and offices I'd like the ValidateSet to allow different values depending on previous parameter. E.g. only allow office and department combinations which actually exist at the different companies.

Example:

Company A
      |-- HR
      |    |-- Office A
      |    |-- Office B
      |
      |-- IT
           |-- Office C
           |-- Office D

Company B
      |-- Design
      |    |-- Office E
      |    |-- Office F
      |
      |-- Finance
// ...etc

Is there a way to do this with parameters in Powershell or should I rethink the entire thing?

Upvotes: 2

Views: 485

Answers (1)

filimonic
filimonic

Reputation: 4634

There are variants:


Example using Dynamic Parameters for single dynamic parameter.

$global:CompanyDepts = @{ 
    'Contoso' = @('Sales', 'IT')
    'Adatum' = @('HR', 'Shipping')
}

function Test-DynParam {
  [CmdletBinding()]
  Param(
      [Parameter(Mandatory=$true)]
      [ValidateSet('Contoso', 'Adatum')]
      [String]$Company
  )

  DynamicParam {
    if (-not [String]::IsNullOrWhiteSpace($Company) -and $global:CompanyDepts.ContainsKey($Company) )
    {
      $attrDept = [System.Management.Automation.ParameterAttribute]::new()
      $attrDept.ParameterSetName = 'CompanySet'
      $attrDept.Mandatory = $true
            
      $attrDeptValidation = [System.Management.Automation.ValidateSetAttribute]::new([String[]]$global:CompanyDepts[$Company])

      $attributeCollection = [System.Collections.ObjectModel.Collection[System.Attribute]]::new()
      $attributeCollection.Add($attrDept)
      $attributeCollection.Add($attrDeptValidation )

      $param = [System.Management.Automation.RuntimeDefinedParameter]::new('Department', [String], $attributeCollection)
      $paramDict = [System.Management.Automation.RuntimeDefinedParameterDictionary]::new()
      $paramDict.Add('Department', $param)
      return $paramDict
    }
  }

  process {
    $PSBoundParameters.Keys | % { Write-Host "$($_)=$($PSBoundParameters[$_])" }
  }
}

Note that you SHOULD help users to achieve best usage expirience, so remember that one user can start typing My-Cmdlet -Company ... presuming Department autocomplete, and another user prefer typing My-Cmdlet -Department ... presuming Company autocomplete, so there sjhould be a combination of ParameterSets and DynamicParams

Upvotes: 3

Related Questions