powershellpowerclivalidateset

PowerShell - Is there a way to create a sort of 'alias' that contains multiple parameter of a validateset?


Good afternoon,

I recently created a PowerShell script to automate and test PowerCLI scripts before running them on vCenters/ESXi hosts.

At the moment, I use a validate set to let the user chose on which server/cluster they want to execute their scripts:

param(
    [string]$script = "help",
    [ValidateSet("localhost", "Server1", "Server2")]
    [string]$server,
    [ValidateSet("DC0_C0", "DC0_C1", "DC0_C2", "Customer1")]
    [string[]]$cluster,
    [switch]$help
)

The issues, is that sometimes when you have big customers, probably they have multiple clusters and it would be more convenient to simply type "customer1" and have the script run on cluster1/2 and 4 instead of manually typing all the clusters:

./viexec vminfo localhost Cluster1,Cluster3,Cluster10

./viexec vminfo localhost Customer1 #And have the script run automatically on all the right clusters.

I already tried using an if that checks the value of the variable $cluster and if it equals "Customer1" then it replace his value with the appropriate clusters, but I don't find this solution elegant. And it's hard to configure/maintain, since the user would need to modify the code, so even better if those parameters could be created from an external config file/user input.

I was also wondering if it was possible to retrieve the param of the validateset from a file/csv to avoid users to customize the main script.ps1, but instead simply replace/write their servers and clusters in a .CSV that populates the validateset parameter.

Hope it is clear..

Regards, Alessandro


Solution

  • Here's a variant of the JSON-based answer, which:

    First, create a sample CSV file that contains 2 customers and their associated clusters:

    # Create a sample CSV file that maps customer names to clusters.
    # This will serve as the basis for tab-completion / argument validation.
    # IMPORTANT: Be sure that you have enough headers (colum names) to cover 
    #            the maximum number of columns values.
    @'
    Customer,Cluster1,Cluster2,Cluster3,Cluster4,Cluster5
    Customer1,DC0_C0,DC0_C1,DC0_C2
    Customer2,DC1_C0,DC1_C1
    '@ > CustomersToClusters.csv
    

    The code that builds on it:

    # Parse the CSV file, assumed to be located in the same dir.
    # as this script.
    $csvRows = Import-Csv $PSScriptRoot/CustomersToClusters.csv
    
    # Build a hashtable of customer names mapped to their associated clusters.
    $colCount = $csvRows[0].psobject.Properties.Name.Count
    $htCustomersToClusters = [ordered] @{}
    foreach ($row in $csvRows) {
      $htCustomersToClusters[$row.Customer] = $row.psobject.Properties.Value[1..($colCount-1)] -ne $null
    }
    # Build an array of all customer and cluster names.
    [string[]] $allCustomersAndClusters = $htCustomersToClusters.Keys + $htCustomersToClusters.GetEnumerator().ForEach({ $_.Value })
    
    # Define the custom class that implements the System.Management.Automation.IValidateSetValuesGenerator
    # interface, to be passed to the [ValidateSet()] attribute.
    class CustomerAndClusters : System.Management.Automation.IValidateSetValuesGenerator {
      [string[]] GetValidValues() { return $script:allCustomersAndClusters }
    }
    
    function Invoke-VI {
      param(
        # Provide tab-completion and validation based on the values
        # returned by a [CustomersAndClusters] instance's .GetValidValues() call.
        [ValidateSet([CustomerAndClusters])]
        [string[]] $Cluster
      )
    
      # Resolve the specified cluster arguments and remove duplicates from the
      # resulting list of cluster-only names.
      $resolvedClusters = $Cluster | ForEach-Object {
        # If a customer name was specified, resolve it to the list of associated clusters.
        if ($customerClusters = $htCustomersToClusters[$_]) { $customerClusters }
        else { $_ }
      } | Select-Object -Unique
      
      "Specified or implied clusters: $resolvedClusters"
    
    }