Untitled
  1. <#
  2. .SYNOPSIS
  3. Inventory Hyper-V VMs: state, IPs, MACs, VLAN, disk provisioned/used, checkpoints. Exports to CSV.
  4.  
  5. .EXAMPLE
  6. .\Get-HyperVInventory.ps1
  7. .\Get-HyperVInventory.ps1 -ComputerName HV01 -OutputDir D:\reports
  8. #>
  9. [CmdletBinding()]
  10. param(
  11. [string]$ComputerName = $env:COMPUTERNAME,
  12. [string]$OutputDir = 'C:\temp'
  13. )
  14.  
  15. #Requires -Modules Hyper-V
  16. $ErrorActionPreference = 'Stop'
  17.  
  18. New-Item -Path $OutputDir -ItemType Directory -Force | Out-Null
  19. $outFile = Join-Path $OutputDir ("hyperv-inventory-{0}-{1}.csv" -f $ComputerName, (Get-Date -Format yyyyMMdd-HHmm))
  20.  
  21. # Sum file sizes across a differencing-disk chain (.avhdx -> parent -> ... -> base)
  22. function Get-VhdChainBytes {
  23. param([string]$Path)
  24. $total = 0
  25. while ($Path) {
  26. $v = Get-VHD -ComputerName $ComputerName -Path $Path -ErrorAction SilentlyContinue
  27. if (-not $v) { Write-Warning "Could not read VHD: $Path"; break }
  28. $total += $v.FileSize
  29. $Path = $v.ParentPath
  30. }
  31. $total
  32. }
  33.  
  34. $rows = Get-VM -ComputerName $ComputerName | ForEach-Object {
  35. $vm = $_
  36. $nics = Get-VMNetworkAdapter -VM $vm
  37. $disks = @(Get-VMHardDiskDrive -VM $vm | Where-Object Path) # skips passthrough disks
  38. $vhds = $disks | ForEach-Object { Get-VHD -ComputerName $ComputerName -Path $_.Path -ErrorAction SilentlyContinue }
  39.  
  40. $vlans = $nics | ForEach-Object {
  41. $vl = Get-VMNetworkAdapterVlan -VMNetworkAdapter $_
  42. if ($vl.OperationMode -eq 'Access') { $vl.AccessVlanId } else { $vl.OperationMode }
  43. }
  44.  
  45. $ips = @($nics.IPAddresses)
  46.  
  47. [pscustomobject]@{
  48. Host = $ComputerName
  49. VMName = $vm.Name
  50. State = $vm.State
  51. Generation = $vm.Generation
  52. vCPU = $vm.ProcessorCount
  53. MemoryGB = [math]::Round($vm.MemoryAssigned / 1GB, 1)
  54. Switch = ($nics.SwitchName | Select-Object -Unique) -join ', '
  55. VLAN = ($vlans -join ', ')
  56. MAC = ($nics.MacAddress) -join ', '
  57. IPv4 = ($ips | Where-Object { $_ -match '^\d+\.\d+\.\d+\.\d+$' }) -join ', '
  58. IPv6 = ($ips | Where-Object { $_ -match ':' -and $_ -notmatch '^fe80' }) -join ', '
  59. Disks = $disks.Count
  60. DiskPaths = ($disks.Path) -join '; '
  61. ProvisionedGB = [math]::Round((($vhds | Measure-Object Size -Sum).Sum) / 1GB, 1)
  62. UsedGB = [math]::Round((($disks | ForEach-Object { Get-VhdChainBytes $_.Path } | Measure-Object -Sum).Sum) / 1GB, 1)
  63. Checkpoints = @(Get-VMSnapshot -VM $vm).Count
  64. KVP = (Get-VMIntegrationService -VM $vm | Where-Object { $_.Name -match 'Key-Value|Data Exchange' }).Enabled -join ''
  65. }
  66. }
  67.  
  68. $rows | Sort-Object VMName | Export-Csv -Path $outFile -NoTypeInformation -Encoding UTF8
  69.  
  70. $rows | Sort-Object VMName | Format-Table VMName, State, IPv4, ProvisionedGB, UsedGB, Checkpoints -AutoSize
  71. "Total used: {0} GB across {1} VMs" -f ([math]::Round(($rows | Measure-Object UsedGB -Sum).Sum, 1)), @($rows).Count
  72. "CSV written to $outFile"
Pasted 2026-09-25 16:02:38

Short link: