I am looking to optimize the code below to avoid calling the same command twice under the calculated properties.
https://learn.microsoft.com/en-us/powershell/module/az.compute/get-azvm
https://learn.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 } }
The script above works for pulling various different Azure VMs.
What can I try next?
2
Answers
Note:
This answer addresses the question as asked, in the context of
Select-Object
and calculated properties.For a
ForEach-Object
-based alternative that uses explicit construction of[pscustomobject]
instances, see zett42’s helpful answer.While the script blocks of calculated properties are executed in sequence, for each input object, they each run in their own child scope relative to the caller, which complicates sharing state between them.
However, you can simply create a variable whose value you want to share in the parent scope, which in the simplest case inside a script is the
$script:
scope, as the following simplified example shows (which uses a call toGet-Date
in lieu of a call to Azure cmdlet as an example of a call you do not want to repeat):Output:
This proves that the
$script:
-scoped$dt
variable was successfully used in the second calculated property.If you want to reliably target the parent scope, which may differ from the
$script:
scope if you’re running inside a nested function call, for instance, replace$script:dt = Get-Date
withSet-Variable -Scope 1 dt (Get-Date)
Note:
ForEach-Object
andWhere-Object
, for instance – for a discussion, see GitHub issue #7157.This might not exactly answer your original question, but you might consider dropping calculated properties when the code becomes too complicated. Instead, use a
[pscustomobject]@{…}
literal in aForEach-Object
script block. This way you can move common code out of the properties to the begin of the script block.On a side note,
SomeCommand | Select-Object -Expand PropertyName
isn’t very efficient and can be replaced by member access, as I did for theip
property. The key is to enclose the command in parentheses.