Problema

Muchas organizaciones están dejando atrás Hyper‑V para adoptar una solución basada en KVM como Proxmox. El reto no es mover una o dos máquinas, sino cientos de VMs con diferentes sistemas operativos, tamaños de disco y configuraciones de red. En un entorno de producción, la migración debe minimizar el tiempo de inactividad, preservar backups existentes y evitar la reconfiguración manual de cada VM. La falta de una herramienta “one‑click” que convierta VHDX a los formatos nativos de Proxmox obliga a combinar varias utilidades y a orquestar procesos en lote.

Causa

  1. Formato de disco incompatible – Hyper‑V utiliza VHD/VHDX, mientras que Proxmox (KVM/QEMU) prefiere RAW o QCOW2. La conversión no es trivial porque los metadatos de la VM (CPU, NIC, controladores) también difieren.
  2. Ausencia de exportación nativa – Hyper‑V no ofrece una exportación directa a OVF que Proxmox pueda importar sin ajustes. Cada VM queda como un conjunto de VHDX + archivo de configuración XML.
  3. Dependencia de backups – Muchas empresas confían en Veeam para la protección de datos. Veeam guarda los discos en su propio formato y, aunque permite restaurar a Hyper‑V o VMware, no incluye un destino Proxmox.
  4. Drivers y agentes – Los Integration Services de Hyper‑V no funcionan bajo KVM; es necesario instalar qemu‑guest‑agent y, a veces, cambiar el tipo de controlador de disco o NIC.
  5. Escalado – Lo que funciona para una VM rara vez escala a cientos sin automatización. La falta de scripts reutilizables genera errores de nomenclatura, IDs duplicados y configuraciones inconsistentes.

Solución

Una estrategia robusta combina tres bloques: extracción de los discos, conversión al formato KVM y creación automática de las VMs en Proxmox. Se pueden usar dos caminos principales; la elección depende de si ya se dispone de backups Veeam.

1. Migración usando Veeam como punto de partida

  1. Crear backup Veeam de cada VM – Si ya se está respaldando con Veeam, no hay que generar exportaciones adicionales.
  2. Restaurar el backup a un host Windows temporal – Utiliza la opción “Instant VM Recovery” o “Restore to a new location” para obtener el VHDX original.
  3. Exportar el VHDX – Copia el archivo VHDX a un storage accesible desde el nodo Proxmox (NFS, SMB o iSCSI).
  4. Convertir a QCOW2qemu-img convert -f vhdx -O qcow2 source.vhdx dest.qcow2.
  5. Importar en Proxmoxqm importdisk <VMID> dest.qcow2 local-lvm --format qcow2.
  6. Crear la VM – Usa qm create con los parámetros de CPU, RAM y NIC que correspondan.
  7. Instalar qemu‑guest‑agent – Después del primer arranque, instala el agente para mejorar la gestión de la VM.

Este flujo mantiene la cadena de backup intacta: si algo falla, el backup Veeam sigue disponible y la conversión puede reintentarse sin tocar la VM original.

2. Conversión directa con virt‑v2v

virt-v2v es una herramienta de libguestfs diseñada para migrar VMs entre hipervisores. Soporta VHDX como entrada y genera discos QCOW2 o RAW listos para Proxmox.

virt-v2v -i disk /path/to/source.vhdx -o local -os /var/lib/vz/template/qemu \
        -of qcow2 -osid 100 --machine q35
  • -i disk indica que el origen es un disco bruto.
  • -o local y -os especifican el directorio de destino en el nodo Proxmox.
  • -of qcow2 elige el formato de salida.
  • --machine q35 fuerza la emulación de chipset compatible con Windows 10/Server 2019.

virt-v2v también adapta la configuración de NIC, elimina los drivers de Hyper‑V y agrega un controlador virtio por defecto. Para cientos de VMs basta con envolver el comando en un bucle Bash.

3. Orquestación en lote

A continuación se muestra un script que cubre la mayor parte del proceso: exporta VHDX desde un share, los convierte, crea la VM y la registra en Proxmox. El script asume que los IDs de VM se generan secuencialmente a partir de 9000; ajústelo a su rango.

#!/usr/bin/env bash
SRC="/mnt/hyperv_exports"          # Share con los VHDX exportados
DST="/var/lib/vz/template/qemu"     # Directorio de plantillas QCOW2 en Proxmox
START_ID=9000
NEXT_ID=$START_ID

for vhdx in "$SRC"/*.vhdx; do
  name=$(basename "$vhdx" .vhdx)
  qcow="${DST}/${name}.qcow2"

  echo "Convirtiendo $vhdx$qcow"
  qemu-img convert -f vhdx -O qcow2 "$vhdx" "$qcow"

  vmid=$NEXT_ID
  ((NEXT_ID++))

  echo "Creando VM $vmid ($name)"
  qm create "$vmid" --name "$name" --memory 4096 --net0 virtio,bridge=vmbr0
  qm importdisk "$vmid" "$qcow" local-lvm --format qcow2
  qm set "$vmid" --scsihw virtio-scsi-pci --ide2 local:cloudinit
  qm set "$vmid" --boot c --bootdisk scsi0
done

El script cubre los pasos críticos y permite añadir opciones de CPU, RAM o discos adicionales mediante variables de entorno. Al ejecutarse en el nodo Proxmox, la VM queda lista para arrancar; solo falta instalar el qemu-guest-agent y, en caso de Windows, cambiar el controlador de disco a virtio (se hace automáticamente en la mayoría de versiones modernas).

Cuándo aplicar esta solución

  • Migraciones masivas – Cuando el número de VMs supera decenas y la intervención manual es inviable.
  • Entornos con backups Veeam – Si ya se cuenta con Veeam, el primer camino evita duplicar trabajo de exportación.
  • Versiones recientes de Windows/Linuxvirt-v2v gestiona bien los controladores modernos; para sistemas muy antiguos (Windows Server 2003) puede requerir ajustes manuales.
  • Redes estáticas o DHCP – La solución asume que la configuración de red se puede volver a aplicar en Proxmox; si la VM depende de VLANs específicas, añada la opción --bridge adecuada en qm create.

No es recomendable cuando:

  • Se necesita migración en tiempo real – Ningún método aquí ofrece “live migration” entre Hyper‑V y KVM.
  • Licencias de Windows están atadas al hardware Hyper‑V – La reactivación será necesaria y podría romper la automatización.
  • Los discos están cifrados con BitLocker sin clave de recuperación – La conversión fallará sin la clave.

Código

#!/usr/bin/env bash
SRC="/mnt/hyperv_exports"
DST="/var/lib/vz/template/qemu"
START_ID=9000
NEXT_ID=$START_ID

for vhdx in "$SRC"/*.vhdx; do
  name=$(basename "$vhdx" .vhdx)
  qcow="${DST}/${name}.qcow2"

  qemu-img convert -f vhdx -O qcow2 "$vhdx" "$qcow"

  vmid=$NEXT_ID
  ((NEXT_ID++))

  qm create "$vmid" --name "$name" --memory 4096 --net0 virtio,bridge=vmbr0
  qm importdisk "$vmid" "$qcow" local-lvm --format qcow2
  qm set "$vmid" --scsihw virtio-scsi-pci --ide2 local:cloudinit
  qm set "$vmid" --boot c --bootdisk sc