下午好,
我最近创建了一个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
)
问题是,有时当你有大客户时,他们可能有多个集群,简单地键入";客户1";并让脚本在集群1/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的值,如果它等于"0";客户1";然后它用适当的集群替换他的值,但我觉得这个解决方案并不优雅。而且很难配置/维护,因为用户需要修改代码,所以如果这些参数可以从外部配置文件/用户输入中创建,那就更好了。
我还想知道是否可以从文件.csv中检索validateset的参数,以避免用户自定义主脚本.ps1,而是简单地在填充validateset参数的.csv中替换/写入他们的服务器和集群。
希望它是清楚的。。
谨致问候,Alessandro
这是一个概念验证,它实现了一个Invoke-VI
函数,该函数展示了所需的选项卡完成/验证行为,由JSON文件中定义的客户到集群的映射驱动(关于基于CSV的解决方案,请参阅其他答案(。
注:
-
JSON被选为CSV的更灵活的替代方案,因为后者需要每个客户固定数量的集群(列(。最终,像YAML或INI文件这样的文件对最终用户来说会更方便,但PowerShell目前缺乏对这些格式的内置支持;请参阅GitHub提案#3607和GitHub建议#9035;但是,PowerShell库中提供了第三方模块,如PowerShell yaml和PSIni。
-
实现了函数而不是脚本,因为在使用所需的选项卡完成和验证功能声明参数之前需要准备步骤。这意味着,您不需要直接运行
*.ps1
脚本,而需要点源代码(. ./viexec.ps1
(,然后调用Invoke-VI
函数。或者,将功能定义为(自动加载(模块的一部分。
首先,创建一个包含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
请注意客户名称是如何解析为关联的集群的。
以下是基于JSON的答案的变体:
-
根据请求使用CSV文件作为数据源。
-
显示了一个更简单的、组合的选项卡完成和验证解决方案,该解决方案基于将实现
System.Management.Automation.IValidateSetValuesGenerator
接口的类传递给ValidateSet
属性(非常感谢根据此答案改编(,但请注意,它需要PowerShell(Core(7+。
首先,创建一个包含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"
}