Private/Get-VMInventory.ps1
|
function Get-VMInventory { <# .SYNOPSIS Returns a normalized VM inventory for a subscription, including the cheap signals (tags, scale-set membership, priority, creation time) used to exclude managed / ephemeral compute before any metric pull. .DESCRIPTION Prefers Azure Resource Graph (one paged query, returns tags + virtualMachineScaleSet + priority + timeCreated). Falls back to Get-AzVM when Az.ResourceGraph / Search-AzGraph is unavailable (then Created is $null, so -MinAgeDays can't apply). Both paths return the same record shape. .PARAMETER SubscriptionId Subscription to inventory (Resource Graph is scoped to it). The fallback uses the current Az context, so the caller must have selected this subscription. .PARAMETER ResourceGroupName Optional resource-group narrowing filter. .PARAMETER VMName Optional VM-name narrowing filter (exact names). .OUTPUTS [pscustomobject[]] records: Id, Name, ResourceGroup, Location, SkuName, Tags, Vmss, Priority, Created. #> [CmdletBinding()] [OutputType([pscustomobject[]])] param( [Parameter(Mandatory)] [string] $SubscriptionId, [string] $ResourceGroupName, [string[]] $VMName ) $records = [System.Collections.Generic.List[pscustomobject]]::new() if (Get-Command Search-AzGraph -ErrorAction SilentlyContinue) { $query = @' resources | where type == 'microsoft.compute/virtualmachines' | project id, name, resourceGroup, location, skuName = tostring(properties.hardwareProfile.vmSize), vmss = tostring(properties.virtualMachineScaleSet.id), priority = tostring(properties.priority), created = tostring(properties.timeCreated), tags '@ $skipToken = $null do { $argParams = @{ Subscription = $SubscriptionId; Query = $query; First = 1000; ErrorAction = 'Stop' } if ($skipToken) { $argParams['SkipToken'] = $skipToken } $page = Search-AzGraph @argParams foreach ($r in $page) { $records.Add([pscustomobject]@{ Id = $r.id Name = $r.name ResourceGroup = $r.resourceGroup Location = $r.location SkuName = $r.skuName Tags = $r.tags Vmss = $r.vmss Priority = $r.priority Created = if ($r.created) { [datetime]$r.created } else { $null } }) } $skipToken = $page.SkipToken } while ($skipToken) } else { Write-Verbose "Az.ResourceGraph not available; falling back to Get-AzVM (no creation-time signal)." $vmParams = @{ Status = $false; ErrorAction = 'Stop' } if ($ResourceGroupName) { $vmParams['ResourceGroupName'] = $ResourceGroupName } foreach ($vm in (Get-AzVM @vmParams)) { $records.Add([pscustomobject]@{ Id = $vm.Id Name = $vm.Name ResourceGroup = $vm.ResourceGroupName Location = $vm.Location SkuName = $vm.HardwareProfile.VmSize Tags = $vm.Tags Vmss = $vm.VirtualMachineScaleSet.Id Priority = $vm.Priority Created = $null }) } } $out = $records.ToArray() if ($ResourceGroupName) { $out = @($out | Where-Object { $_.ResourceGroup -ieq $ResourceGroupName }) } if ($VMName) { $out = @($out | Where-Object { $_.Name -in $VMName }) } return $out } |