【问题标题】:PowerShell - Is there a way to create a sort of 'alias' that contains multiple parameter of a validateset?PowerShell - 有没有办法创建一种包含验证集多个参数的“别名”?
【发布时间】:2021-04-30 12:59:48
【问题描述】:

下午好,

我最近创建了一个 PowerShell 脚本,用于在 vCenter/ESXi 主机上运行之前自动化和测试 PowerCLI 脚本。

目前,我使用验证集让用户选择他们想要在哪个服务器/集群上执行脚本:

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

问题是,有时当您有大客户时,他们可能有多个集群,只需键入“customer1”并让脚本在 cluster1/2 和 4 上运行会更方便,而不是手动键入所有集群:

./viexec vminfo localhost Cluster1,Cluster3,Cluster10

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

我已经尝试使用 if 来检查变量 $cluster 的值,如果它等于“Customer1”,那么它将用适当的集群替换他的值,但我认为这个解决方案并不优雅。而且很难配置/维护,因为用户需要修改代码,所以如果这些参数可以从外部配置文件/用户输入创建就更好了。

我还想知道是否可以从文件/csv 中检索 validateset 的参数,以避免用户自定义主 script.ps1,而是简单地将他们的服务器和集群替换/写入一个填充的 .CSV validateset 参数。

希望清楚..

问候, 亚历山德罗

【问题讨论】:

  • 如果不同的值是静态的,ValidateSets 效果最好,而这里显然不是。虽然它的学习曲线略高,但使用argument completion 更灵活。您可以在运行时从任何您喜欢的来源获取自动完成数据,包括但不限于 CSV 文件。
  • 能否请您提供一个示例来说明如何实现它?比如集群参数的Validateset?
  • 为了让@JeroenMostert 收到您的评论通知,您必须@-提及他(就像我刚才所做的那样)。他指的是实现制表符补全的命令-外部 方式,但是,它不包括参数validation。命令-external Register-ArgumentCompleter 解决方案的每个参数等效项是我的第一个答案中的命令-internal,每个参数System.Management.Automation.ArgumentCompleterAttribute 解决方案,也需要添加' l 验证工作。

标签: powershell powercli validateset


【解决方案1】:

这是一个概念证明,它实现了一个 Invoke-VI 函数,该函数展示了所需的制表符完成/验证行为,由 JSON 文件中定义的客户到集群映射驱动(有关基于 CSV 的解决方案,请参阅 the other answer)。

注意:

  • 选择 JSON 作为 CSV 的更灵活替代方案,因为后者需要每个客户固定数量的集群(列)。最终,像 YAML 或 INI 文件这样的东西对最终用户来说会更方便,但 PowerShell 目前缺乏对这些格式的内置支持;见GitHub proposal #3607GitHub proposal #9035;但是,PowerShell Gallery 中提供了第三方模块,例如powershell-yamlPSIni

  • 实现了一个函数而不是一个脚本,因为在使用所需的制表符完成和验证功能声明参数之前需要准备步骤。这意味着,您需要dot-source (. ./viexec.ps1) 然后调用Invoke-VI 函数,而不是直接 运行您的*.ps1 脚本。或者,将函数定义为(自动加载)module 的一部分。

首先,创建一个包含 2 个客户及其关联集群的示例 JSON 文件:

# Create a sample JSON file that maps customer names to clusters.
# This will serve as the basis for tab-completion / argument validation.
@'
{
  "Customer1": [ "DC0_C0", "DC0_C1", "DC0_C2" ],
  "Customer2": [ "DC1_C0", "DC1_C1" ]
}
'@ > CustomerToClusters.json

基于它的代码:

# Parse the JSON file, assumed to be located in the same dir.
# as this script.
$customerToClusterMapping = ConvertFrom-Json (Get-Content -Raw $PSScriptRoot/CustomerToClusters.json)

# Build the list of customer names and cluster names across all customers.
[string[]] $customerNames = $customerToClusterMapping.psobject.Properties.Name
[string[]] $allClusters = $customerToClusterMapping.psobject.Properties.Value

function Invoke-VI {
  param(
    # Tab-complete cluster names as well as customer names.
    [ArgumentCompleter({ param($cmd, $param, $wordToComplete) ($customerNames + $allClusters) -like "$wordToComplete*" })]
    # Ensure that only known customer / cluster names were specified.
    # NOTE: If validation fails, the (default) error message is unhelpful.
    #       Unfortunately, this cannot be helped in *Windows PowerShell*, but in
    #       PowerShell (Core) 7+, you can add an `ErrorMessage` property:
    #         [ValidateScript({ $_ -in ($customerNames + $allClusters) }, ErrorMessage = 'Unknown customer/cluster name: {0}')]
    [ValidateScript({ $_ -in ($customerNames + $allClusters) })]
    [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, eturn the list of clusters for the specified customer.
    if ($_ -in $customerNames) { $customerToClusterMapping.$_ } 
    else { $_ }
  } | Select-Object -Unique
  
  "Specified or implied clusters: $resolvedClusters"

}  

示例调用,在对上述代码进行点源之后:

PS> Invoke-VI Customer1    # Customer1 was tab-completed.
Specified or implied clusters: DC0_C0 DC0_C1 DC0_C2

注意客户名称是如何解析为与之关联的集群的。

【讨论】:

    【解决方案2】:

    这是JSON-based answer 的一个变体,它:

    首先,创建一个包含 2 个客户及其关联集群的示例 CSV 文件:

    # 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
    

    基于它的代码:

    # 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"
    
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-03-23
      • 1970-01-01
      • 2017-10-30
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多