<#
.SYNOPSIS
Inventory Hyper-V VMs: state, IPs, MACs, VLAN, disk provisioned/used, checkpoints. Exports to CSV.
.EXAMPLE
.\Get-HyperVInventory.ps1
.\Get-HyperVInventory.ps1 -ComputerName HV01 -OutputDir D:\reports
#>
[CmdletBinding()]
param(
[string]$ComputerName = $env:COMPUTERNAME,
[string]$OutputDir = 'C:\temp'
)
#Requires -Modules Hyper-V
$ErrorActionPreference = 'Stop'
New-Item -Path $OutputDir -ItemType Directory -Force | Out-Null
$outFile = Join-Path $OutputDir ("hyperv-inventory-{0}-{1}.csv" -f $ComputerName, (Get-Date -Format yyyyMMdd-HHmm))
# Sum file sizes across a differencing-disk chain (.avhdx -> parent -> ... -> base)
function Get-VhdChainBytes {
param([string]$Path)
$total = 0
while ($Path) {
$v = Get-VHD -ComputerName $ComputerName -Path $Path -ErrorAction SilentlyContinue
if (-not $v) { Write-Warning "Could not read VHD: $Path"; break }
$total += $v.FileSize
$Path = $v.ParentPath
}
$total
}
$rows = Get-VM -ComputerName $ComputerName | ForEach-Object {
$vm = $_
$nics = Get-VMNetworkAdapter -VM $vm
$disks = @(Get-VMHardDiskDrive -VM $vm | Where-Object Path) # skips passthrough disks
$vhds = $disks | ForEach-Object { Get-VHD -ComputerName $ComputerName -Path $_.Path -ErrorAction SilentlyContinue }
$vlans = $nics | ForEach-Object {
$vl = Get-VMNetworkAdapterVlan -VMNetworkAdapter $_
if ($vl.OperationMode -eq 'Access') { $vl.AccessVlanId } else { $vl.OperationMode }
}
$ips = @($nics.IPAddresses)
[pscustomobject]@{
Host = $ComputerName
VMName = $vm.Name
State = $vm.State
Generation = $vm.Generation
vCPU = $vm.ProcessorCount
MemoryGB = [math]::Round($vm.MemoryAssigned / 1GB, 1)
Switch = ($nics.SwitchName | Select-Object -Unique) -join ', '
VLAN = ($vlans -join ', ')
MAC = ($nics.MacAddress) -join ', '
IPv4 = ($ips | Where-Object { $_ -match '^\d+\.\d+\.\d+\.\d+$' }) -join ', '
IPv6 = ($ips | Where-Object { $_ -match ':' -and $_ -notmatch '^fe80' }) -join ', '
Disks = $disks.Count
DiskPaths = ($disks.Path) -join '; '
ProvisionedGB = [math]::Round((($vhds | Measure-Object Size -Sum).Sum) / 1GB, 1)
UsedGB = [math]::Round((($disks | ForEach-Object { Get-VhdChainBytes $_.Path } | Measure-Object -Sum).Sum) / 1GB, 1)
Checkpoints = @(Get-VMSnapshot -VM $vm).Count
KVP = (Get-VMIntegrationService -VM $vm | Where-Object { $_.Name -match 'Key-Value|Data Exchange' }).Enabled -join ''
}
}
$rows | Sort-Object VMName | Export-Csv -Path $outFile -NoTypeInformation -Encoding UTF8
$rows | Sort-Object VMName | Format-Table VMName, State, IPv4, ProvisionedGB, UsedGB, Checkpoints -AutoSize
"Total used: {0} GB across {1} VMs" -f ([math]::Round(($rows | Measure-Object UsedGB -Sum).Sum, 1)), @($rows).Count
"CSV written to $outFile"