【问题标题】:Optimize PowerShell code to avoid calling the cmdlet multiple times inside calculated properties?优化 PowerShell 代码以避免在计算属性中多次调用 cmdlet?
【发布时间】:2022-08-18 18:44:48
【问题描述】:
我需要一些建议和帮助来优化下面的代码,以避免在计算的属性下两次调用相同的命令。
https://docs.microsoft.com/en-us/powershell/module/az.compute/get-azvm
https://docs.microsoft.com/en-us/powershell/module/az.compute/get-azvmsize
Get-AzVM | Select-Object-Object Name,
@{ l = \'osdiskingb\'; e = { ($_.StorageProfile.OsDisk.DiskSizeGB) } }, `
@{ l = \'memory\'; e = { $size = $_.HardwareProfile.VmSize; Get-AzVMSize -vmname $_.Name -ResourceGroupName $_.ResourceGroupName | Where-Object { $_.name -eq $size } | Select-Object -expand MemoryInMB } }, `
@{ l = \'cpu\'; e = { $size = $_.HardwareProfile.VmSize; Get-AzVMSize -vmname $_.Name -ResourceGroupName $_.ResourceGroupName | Where-Object { $_.name -eq $size } | Select-Object -expand NumberOfCores } }, `
@{ l = \'nic\'; e = { $_.NetworkProfile.NetworkInterfaces.id.split(\'/\') | Select-Object -Last 1 } }, `
@{ l = \'ip\'; e = { $nic = $_.NetworkProfile.NetworkInterfaces.id.split(\'/\') | Select-Object -Last 1; Get-AzNetworkInterface -Name $nic | Select-Object -expand ipconfigurations | Select-Object -expand privateipaddress } }
上面的脚本适用于提取各种不同的 Azure VM。
如果有人知道如何做得更好,请建议更新的代码。
谢谢。
标签:
azure
powershell
azure-powershell
【解决方案1】:
虽然calculated properties的脚本块是按顺序执行的,但对于每个输入对象,它们都各自运行孩子范围相对于调用者,这使它们之间的共享状态变得复杂。
但是,您可以简单地创建一个要在父母范围,在脚本中最简单的情况下是 $script: 范围,如以下简化示例所示(它使用对 Get-Date 的调用代替对 Azure cmdlet 的调用作为您不想要的调用的示例重复):
# Share the result of the `Get-Date` call between calculated properties.
'foo' | Select-Object `
@{ n='Month'; e = { $script:dt = Get-Date; $dt.Month } },
@{ n='Year'; e = { $dt.Year } }
输出:
Month Year
----- ----
8 2022
这证明$script:-scoped $dt 变量在第二个计算属性中被成功使用。
如果您想可靠地定位父母范围,如果您在嵌套函数调用中运行,它可能与 $script: 范围不同,例如,将 $script:dt = Get-Date 替换为
Set-Variable -Scope 1 dt (Get-Date)
笔记:
- 计算属性的脚本块以及延迟绑定脚本块在孩子范围可能令人惊讶,因为它与传递给
ForEach-Object 和 Where-Object 的脚本块的行为形成对比,例如 - 有关讨论,请参阅 GitHub issue #7157。
【解决方案2】:
这可能无法完全回答您最初的问题,但是当代码变得过于复杂时,您可能会考虑删除计算的属性。相反,在 ForEach-Object 脚本块中使用 [pscustomobject]@{…} 文字。这样,您可以将公共代码从属性移到脚本块的开头。
Get-AzVM | ForEach-Object {
$size = $_.HardwareProfile.VmSize
$vmsize = Get-AzVMSize -vmname $_.Name -ResourceGroupName $_.ResourceGroupName | Where-Object { $_.name -eq $size }
$nic = $_.NetworkProfile.NetworkInterfaces.id.split('/') | Select-Object -Last 1
# Implicitly outputs an object with the given properties
[pscustomobject]@{
Name = $_.Name
osdiskingb = $_.StorageProfile.OsDisk.DiskSizeGB
memory = $vmsize.MemoryInMB
cpu = $vmsize.NumberOfCores
nic = $nic
ip = (Get-AzNetworkInterface -Name $nic).ipconfigurations.privateipaddress
}
}
附带说明一下,SomeCommand | Select-Object -Expand PropertyName 效率不高,可以用成员访问权限代替,就像我对 ip 属性所做的那样。关键是将命令括在括号中。