清理 Hyper-V 中无法删除的虚拟机残留
太阳作者太阳
原创内容采用 CC-4.0 协议发布,转载请注明出处

清理 Hyper-V 中无法删除的虚拟机残留

我在 Hyper-V 管理器里看到一台早已不用的虚拟机,但执行 Get-VM -NameRemove-VM 都提示找不到对象。管理员权限和 vmms 服务没有问题,虚拟交换机也能正常读取。

这说明管理器里的条目可能已经损坏,或者只剩下一条底层注册。下面是我最后使用的清理顺序。

先试正常删除

在管理员 PowerShell 中确认权限:

$principal = [Security.Principal.WindowsPrincipal]::new(
  [Security.Principal.WindowsIdentity]::GetCurrent()
)
$principal.IsInRole(
  [Security.Principal.WindowsBuiltInRole]::Administrator
)

结果应为 True。然后检查目标:

$targetName = 'TARGET_VM'

Get-VM -Name $targetName |
  Format-List Name,State,Status,Id,Path,ConfigurationLocation

Get-VMHardDiskDrive -VMName $targetName |
  Format-Table ControllerType,ControllerNumber,ControllerLocation,Path

Get-VMSnapshot -VMName $targetName |
  Format-Table Name,SnapshotType,CreationTime

如果 Get-VM 能找到它,直接使用 Hyper-V 自带命令:

Remove-VM -Name $targetName -WhatIf
Remove-VM -Name $targetName

-WhatIf 会先显示即将删除的对象。确认名称无误后再执行第二条。

Get-VM 找不到时检查底层注册

Hyper-V 的虚拟机对象位于 root/virtualization/v2。先查询普通虚拟机和计划虚拟机:

$namespace = 'root/virtualization/v2'
$targetName = 'TARGET_VM'

$targets = @(
  Get-CimInstance -Namespace $namespace -ClassName Msvm_ComputerSystem |
    Where-Object { $_.ElementName -ieq $targetName }

  Get-CimInstance -Namespace $namespace -ClassName Msvm_PlannedComputerSystem |
    Where-Object { $_.ElementName -ieq $targetName }
)

$targets |
  Format-List ElementName,Name,EnabledState,OperationalStatus

没有结果时,不要继续调用删除方法。此时管理器里显示的更可能是旧连接或界面缓存,关闭 Hyper-V 管理器再打开即可。

如果找到了唯一对象,可以调用 Hyper-V 的 DestroySystem

if ($targets.Count -ne 1) {
  throw "要求精确匹配一个对象,实际找到 $($targets.Count) 个,已停止。"
}

$service = Get-CimInstance `
  -Namespace $namespace `
  -ClassName Msvm_VirtualSystemManagementService

$result = Invoke-CimMethod `
  -InputObject $service `
  -MethodName DestroySystem `
  -Arguments @{ AffectedSystem = $targets[0] }

if ($result.ReturnValue -notin 0, 4096) {
  throw "DestroySystem 失败,返回值:$($result.ReturnValue)"
}

$result

目标虚拟机应当处于关闭或保存状态。返回 0 表示已经完成,4096 表示任务正在异步执行,需要等结果中的 Job 完成。

删除后重新查询:

Get-CimInstance -Namespace $namespace -ClassName Msvm_ComputerSystem |
  Where-Object { $_.ElementName -ieq $targetName }

Get-CimInstance -Namespace $namespace -ClassName Msvm_PlannedComputerSystem |
  Where-Object { $_.ElementName -ieq $targetName }

两条命令都没有输出,再关闭并重新打开 Hyper-V 管理器。

虚拟磁盘要单独处理

删除虚拟机注册不等于删除 .vhd.vhdx。如果还要清理磁盘文件,应当先记录 Get-VMHardDiskDrive 返回的准确路径,再确认它没有被其他虚拟机使用。

不要递归删除整个 Hyper-V 目录,也不要用 *.vhdx 通配符批量清理。

这次虚拟机残留和端口问题碰巧同时出现,但两者不能画等号。端口的处理记录在排查 Windows 动态端口与保留端口冲突