Problema

En entornos de VDI o laboratorios de pruebas, crear cientos de máquinas Windows a partir de una plantilla implica repetir los mismos pasos: cambiar el nombre del host, asignar IP, unir al dominio y, en algunos casos, añadir discos de datos. Hacerlo manualmente o mediante scripts ad‑hoc genera inconsistencias, errores de sincronización y una carga operativa que no escala. El patrón que se repite es la necesidad de personalizar automáticamente una plantilla Windows después del clonado, de forma que cada instancia quede lista para producción sin intervención humana.

Causa

  1. Falta de integración nativa – Proxmox no incluye una herramienta equivalente a “VMware Guest Customization”. El clonado copia la VM tal cual, sin ejecutar Sysprep ni aplicar configuraciones de red.
  2. Sysprep limitado a la fase de creación – Ejecutar Sysprep sobre una VM ya en producción está deshabilitado por seguridad; la única vía segura es hacerlo justo después del clonado.
  3. Gestión de credenciales y scripts – Los scripts de configuración (unattend.xml, setup.ps1) suelen quedar dispersos, lo que dificulta su versionado y su reutilización en diferentes proyectos.
  4. Control de errores – Cuando el proceso falla (por ejemplo, la VM no responde al QEMU guest agent), la VM queda en estado intermedio y no hay registro centralizado de lo ocurrido.

Estos factores hacen que la automatización sea frágil y que los equipos de sysadmin terminen recurriendo a procesos manuales o a soluciones caseras poco mantenibles.

Solución

Implementar un sidecar Docker Compose que actúe como orquestador de la personalización. El contenedor se encarga de:

  1. Clonar la plantilla usando la API de Proxmox (pvesh o proxmoxer).
  2. Inyectar Sysprep mediante el QEMU guest agent: copiar unattend.xml y setup.ps1 al disco, lanzar guest-exec y esperar la finalización.
  3. Aplicar configuración de red – DHCP por defecto, con opción a IP estática o unión a AD mediante netsh dentro de la VM.
  4. Crear discos adicionales si la plantilla lo requiere (por ejemplo, disco de datos para servidores).
  5. Registrar el resultado en una base ligera (SQLite) con historial de jobs, errores duraderos y etiquetas de “failed”.

El sidecar se despliega como un servicio Docker independiente, lo que permite escalar el número de personalizaciones en paralelo (controlado por un límite de concurrencia) y mantener la lógica aislada del host Proxmox.

Arquitectura mínima

  • Docker host (puede ser el propio nodo Proxmox o una máquina de gestión).
  • Contenedor sidecar con:
    • Cliente API de Proxmox (proxmoxer o pvesh).
    • QEMU guest agent (qemu-ga) habilitado en la plantilla.
    • Scripts unattend.xml y setup.ps1 versionados en el repositorio.
  • Almacenamiento persistente para historial (SQLite o archivo JSON).

Flujo de trabajo

  1. El usuario envía una solicitud HTTP (REST) con los parámetros de la nueva VM (hostname, IP, dominio, discos).
  2. El sidecar clona la plantilla (qm clone).
  3. Espera a que la VM arranque y el guest agent informe “ready”.
  4. Copia los archivos de personalización y ejecuta sysprep /generalize /oobe.
  5. Cuando Sysprep termina, aplica la configuración de red y, si corresponde, une la VM al dominio.
  6. Marca el job como “success” o “failed” y devuelve el ID de la VM al solicitante.

Cuándo aplicar esta solución

  • Despliegues masivos de escritorios Windows (VDI, laboratorios, pruebas de software).
  • Entornos de desarrollo homelab donde se necesita crear y destruir VMs rápidamente.
  • Automatización de servidores Windows que requieren discos de datos personalizados o configuraciones de página de archivo distintas.
  • Escenarios donde la consistencia es crítica y el proceso manual genera errores de naming o de unión a dominio.

No es adecuada cuando:

  • Se necesita re‑personalizar una VM ya en producción (Sysprep está deshabilitado).
  • La infraestructura no permite ejecutar el QEMU guest agent (plantilla sin qemu-ga).
  • Se requiere integración profunda con herramientas de orquestación diferentes (por ejemplo, Ansible) y ya existe un flujo establecido.

Código

# docker-compose.yml básico para el sidecar
version: "3.8"
services:
  guestos-customizer:
    image: robertlukan/proxmox-guestos-customizer:2.6.7
    environment:
      - PROXMOX_HOST=proxmox.example.com
      - PROXMOX_USER=root@pam
      - PROXMOX_PASSWORD=SuperSecret
      - CONCURRENCY_LIMIT=5
    volumes:
      - ./templates/unattend.xml:/opt/customizer/unattend.xml:ro
      - ./templates/setup.ps1:/opt/customizer/setup.ps1:ro
      - ./data:/opt/customizer/data
    ports:
      - "8080:8080"
    restart: unless-stopped
# Ejemplo de petición curl para crear una VM personalizada
curl -X POST http://localhost:8080/api/v1/clone \
  -H "Content-Type: application/json" \
  -d '{
        "template_id": 101,
        "hostname": "win-dev-01",
        "ip": "192.168.10.45",
        "domain": "corp.local",
        "static_ip": true,
        "add_data_disk_gb": 50
      }'

Verificación

  1. Comprobar el historial: acceder a http://localhost:8080/api/v1/jobs y buscar el ID retornado. El estado debe ser success.
  2. Conectar a la VM: usar RDP o Enter-PSSession y validar que el hostname, la IP y la pertenencia al dominio coinciden con lo solicitado.
  3. Revisar logs del contenedor: docker logs guestos-customizer debe mostrar la secuencia clone → sysprep → network → domain.
  4. Validar discos: en Proxmox UI, la VM debe presentar el disco de datos adicional con el tamaño especificado.

Si alguno de los pasos falla, el job queda marcado como failed y el contenedor escribe un mensaje de error detallado en el archivo de historial, facilitando la depuración.

Notas adicionales

  • Habilitar QEMU guest agent en la plantilla es obligatorio; sin él, el sidecar no podrá ejecutar comandos dentro de la VM.
  • Sincronización de tiempo: asegúrate de que la VM y el host compartan la misma zona horaria; de lo contrario, Sysprep puede quedar bloqueado esperando la hora del sistema.
  • Límites de concurrencia: ajustar CONCURRENCY_LIMIT según la capacidad de CPU/RAM del nodo evita sobrecargar el host durante picos de creación.
  • Versionado de unattend.xml: mantener el archivo bajo control de versiones permite cambiar valores como la clave de producto o la configuración de OOBE sin tocar el contenedor.
  • Persistencia de historial: montar un volumen externo para /opt/customizer/data garantiza que el historial sobreviva a reinicios del contenedor.
  • Seguridad de credenciales: considera usar Docker secrets o un vault externo en lugar de variables de entorno planas para la contraseña de Proxmox.