Collectors/VirtualMachines.ps1
|
function Get-AerVirtualMachines { [CmdletBinding()] param( [Parameter(Mandatory)] [string[]] $SubscriptionIds, [Parameter(Mandatory)] $SubscriptionMap ) $subLookup = @{} if ($SubscriptionMap -is [hashtable]) { $subLookup = $SubscriptionMap } elseif ($SubscriptionMap) { $SubscriptionMap.PSObject.Properties | ForEach-Object { $subLookup[$_.Name] = $_.Value } } # ── Virtual machines (core fields + first NIC + image reference) ───────── $vmRows = @() try { $vmRows = Invoke-AerArgQuery -SubscriptionIds $SubscriptionIds -Query @' resources | where type =~ 'microsoft.compute/virtualmachines' | extend img = properties.storageProfile.imageReference | project id, name, subscriptionId, resourceGroup, location, osType = tostring(properties.storageProfile.osDisk.osType), vmSize = tostring(properties.hardwareProfile.vmSize), imgPublisher= tostring(img.publisher), imgOffer = tostring(img.offer), imgSku = tostring(img.sku), imgId = tostring(img.id), bootDiag = tobool(properties.diagnosticsProfile.bootDiagnostics.enabled), timeCreated = tostring(properties.timeCreated), powerState = tostring(properties.extended.instanceView.powerState.code), nicId = tolower(tostring(properties.networkProfile.networkInterfaces[0].id)), tags '@ } catch { Write-Warning "[VirtualMachines.vms] $($_.Exception.Message)" } # ── Network interfaces → private IP + associated public IP id ──────────── $nicMap = @{} try { $nicRows = Invoke-AerArgQuery -SubscriptionIds $SubscriptionIds -Query @' resources | where type =~ 'microsoft.network/networkinterfaces' | extend ipcfg = properties.ipConfigurations[0] | project nicId = tolower(id), privateIp = tostring(ipcfg.properties.privateIPAddress), publicIpId = tolower(tostring(ipcfg.properties.publicIPAddress.id)), subnetId = tostring(ipcfg.properties.subnet.id) '@ foreach ($n in $nicRows) { if ($n.nicId) { $nicMap[$n.nicId] = $n } } } catch { Write-Warning "[VirtualMachines.nics] $($_.Exception.Message)" } # ── Public IP addresses → address ──────────────────────────────────────── $pipMap = @{} try { $pipRows = Invoke-AerArgQuery -SubscriptionIds $SubscriptionIds -Query @' resources | where type =~ 'microsoft.network/publicipaddresses' | project pipId = tolower(id), ip = tostring(properties.ipAddress) '@ foreach ($p in $pipRows) { if ($p.pipId) { $pipMap[$p.pipId] = $p.ip } } } catch { Write-Warning "[VirtualMachines.publicips] $($_.Exception.Message)" } # ── Managed disks → total provisioned size per owning VM ───────────────── $diskMap = @{} try { $diskRows = Invoke-AerArgQuery -SubscriptionIds $SubscriptionIds -Query @' resources | where type =~ 'microsoft.compute/disks' | where isnotempty(tostring(managedBy)) | summarize DiskGB = sum(toint(properties.diskSizeGB)) by vmId = tolower(tostring(managedBy)) '@ foreach ($d in $diskRows) { if ($d.vmId) { $diskMap[$d.vmId] = [int]$d.DiskGB } } } catch { Write-Warning "[VirtualMachines.disks] $($_.Exception.Message)" } # ── vCore / memory per SKU via Compute vmSizes REST (one call per region) ─ # ARG doesn't expose SKU capabilities, so resolve them from the regional # vmSizes catalog. Key the map by "region|size" (lowercased). $sizeMap = @{} foreach ($grp in ($vmRows | Group-Object location)) { $region = $grp.Name $subForRegion = ($grp.Group | Select-Object -First 1).subscriptionId if (-not $region -or -not $subForRegion) { continue } try { $resp = Invoke-AzRestMethod -Method GET ` -Path "/subscriptions/$subForRegion/providers/Microsoft.Compute/locations/$region/vmSizes?api-version=2023-07-01" ` -ErrorAction Stop if ($resp.StatusCode -eq 200) { foreach ($s in @(($resp.Content | ConvertFrom-Json).value)) { $sizeMap["$region|$($s.name)".ToLowerInvariant()] = [pscustomobject]@{ Cores = [int]$s.numberOfCores MemoryMB = [int]$s.memoryInMB } } } } catch { Write-Warning "[VirtualMachines.vmSizes:$region] $($_.Exception.Message)" } } # ── Assemble per-VM records ────────────────────────────────────────────── $vms = foreach ($v in $vmRows) { $nic = if ($v.nicId) { $nicMap[$v.nicId] } else { $null } $privateIp = $nic.privateIp $publicIp = if ($nic -and $nic.publicIpId) { $pipMap[$nic.publicIpId] } else { $null } $vnet = ''; $subnet = '' if ($nic -and $nic.subnetId) { $sp = $nic.subnetId -split '/' $iv = [array]::IndexOf($sp, 'virtualNetworks'); if ($iv -ge 0 -and ($iv + 1) -lt $sp.Count) { $vnet = $sp[$iv + 1] } $isub = [array]::IndexOf($sp, 'subnets'); if ($isub -ge 0 -and ($isub + 1) -lt $sp.Count) { $subnet = $sp[$isub + 1] } } $os = switch -Regex ($v.osType) { 'Windows' { 'Windows'; break } 'Linux' { 'Linux'; break } default { 'Other' } } $image = if ($v.imgPublisher) { "$($v.imgPublisher):$($v.imgOffer):$($v.imgSku)" } elseif ($v.imgId) { 'Custom: ' + ($v.imgId -split '/')[-1] } else { '' } $vmIdLower = if ($v.id) { $v.id.ToLowerInvariant() } else { '' } $diskGB = if ($vmIdLower -and $diskMap.ContainsKey($vmIdLower)) { [int]$diskMap[$vmIdLower] } else { 0 } $sz = $sizeMap["$($v.location)|$($v.vmSize)".ToLowerInvariant()] $cores = if ($sz) { [int]$sz.Cores } else { 0 } $memMB = if ($sz) { [int]$sz.MemoryMB } else { 0 } $memGB = if ($memMB) { [math]::Round($memMB / 1024, 1) } else { 0 } $subName = if ($v.subscriptionId) { $subLookup[$v.subscriptionId.ToLowerInvariant()] } else { $null } $power = if ($v.powerState) { ($v.powerState -split '/')[-1] } else { '' } [pscustomobject]@{ Id = $v.id Name = $v.name SubscriptionId = $v.subscriptionId SubscriptionName = $subName ?? $v.subscriptionId ResourceGroup = $v.resourceGroup Os = $os Location = $v.location Sku = $v.vmSize Image = $image Tags = $v.tags PrivateIp = $privateIp PublicIp = $publicIp Vnet = $vnet Subnet = $subnet BootDiagnostics = [bool]$v.bootDiag TimeCreated = $v.timeCreated Status = $power VCores = $cores MemoryMB = $memMB MemoryGB = $memGB DiskGB = $diskGB } } $vms = @($vms) $totalMemMB = ($vms | Measure-Object MemoryMB -Sum).Sum ?? 0 return [pscustomobject]@{ TotalVMs = $vms.Count LinuxVMs = @($vms | Where-Object { $_.Os -eq 'Linux' }).Count WindowsVMs = @($vms | Where-Object { $_.Os -eq 'Windows' }).Count TotalvCores = [int](($vms | Measure-Object VCores -Sum).Sum ?? 0) TotalMemoryGB = [math]::Round($totalMemMB / 1024, 0) TotalDiskGB = [int](($vms | Measure-Object DiskGB -Sum).Sum ?? 0) VirtualMachines = $vms } } |