¿Alguna vez te has encontrado en esa situación donde hay una herramienta que te resuelve un problema pero en tu empresa no está permitida?
Ya sea porque usan otra alternativa, por políticas de seguridad o simplemente por decisiones corporativas, te ves obligado a adaptarte.
Aunque al principio esto puede parecer frustrante (sobre todo cuando crees que tu herramienta habitual te haría el trabajo más rápido y cómodo), en realidad es una oportunidad de oro. Te obliga a aprender cómo funciona el nuevo entorno y da la excusa perfecta para programar tus propios scripts desde cero para automatizar y simplificar el trabajo diario.
En mi caso, me enfrenté a la clásica tarea de infraestructura: clonar imágenes de discos y realizar despliegues a partir de preinstalaciones de Windows.
En este entorno, la herramienta permitida era DISM, que viene de forma nativa en la suite de Windows y no requiere instalación extra.
Si quieres ir directo al grano, puedes consultar y descargar los scripts completos en el repositorio del proyecto en GitHub.
Aquí es donde empieza esta historia.
Despliegue Unificado: Menú Dinámico y Formateo en un solo Script
La primera vez que me tocó hacer un deploy en el trabajo, el punto de partida fue un archivo de texto con una línea de comando extensa que los técnicos copiábamos y pegábamos directamente en la consola de WinPE. Además, debíamos modificar manualmente el nombre de la imagen .wim que queríamos aplicar.
Para preparar el disco antes del despliegue, también contábamos con un bloque de instrucciones de diskpart (limpiar volumen, crear tabla GPT, definir particiones EFI y Windows), que estaba almacenado en un archivo de texto independiente.
Tener comandos dispersos y archivos de configuración sueltos por el disco de herramientas me generaba desconfianza.
Si un archivo se borra u otro se desactualiza, el proceso se interrumpe y falla resultando en una pérdida de tiempo en tareas que suelen demorar bastante.
Pensé: "Esto tiene que poder hacerse mejor".
La solución fue crear deploy.cmd para concentrar todo el despliegue en un único script autoportante que realiza tres tareas clave de forma secuencial:
1. Escaneo dinámico y menú interactivo
Para evitar errores de tipeo al introducir el nombre de la imagen .wim, el script barre el directorio donde se almacenan y las lista en un menú.
La decición de hacerlo así fue para que sea escalable y dinámico permitiendo agregar imágenes al directorio sin necesidad de modificar el script.
:: Escaneo dinámico de imágenes
set COUNT=0
for %%A in ("%IMG_DIR%\*.wim") do (
set /a COUNT+=1
set "WIM[!COUNT!]=%%~fA"
set "WIM_NAME[!COUNT!]=%%~nA"
)
:: ... generación del menú interactivo ...
Este simple bucle for detecta los archivos de un directorio y almacena las rutas en "arrays" simulados de Batch, generando opciones numeradas para que solo deba seleccionar una y el script se encargará del resto.
Nota de sintaxis: El uso de signos de exclamación (
!) en lugar del porcentaje (%) tradicional de Batch se debe a la necesidad de habilitar la expansión retardada (setlocal enabledelayedexpansion). Sin esto, Batch evaluaría las variables del menú al cargar el bucle en memoria y no en tiempo de ejecución, lo que rompería la lógica del índice dinámico.
2. Integración de DiskPart al vuelo
En lugar de depender de un archivo de texto externo para particionar el disco, el propio script genera las instrucciones de diskpart en el directorio temporal y las ejecuta en segundo plano:
set "TEMP_SCRIPT=%temp%\dp_script.txt"
(
echo select disk %TARGET_DISK%
echo clean
echo convert gpt
echo create partition efi size=200
echo format quick fs=fat32 label="EFI"
echo assign letter=S
:: ... creación de particiones MSR y Windows ...
) > "%TEMP_SCRIPT%"
diskpart /s "%TEMP_SCRIPT%" >> "%LOG_FILE%" 2>&1
del "%TEMP_SCRIPT%"
Lo ejecutamos silenciosamente con diskpart /s y registramos su salida en un archivo de log (%LOG_FILE%) para diagnosticar posibles fallos.
3. Configuración automática de arranque UEFI
Una vez que DISM vuelca la imagen seleccionada, el script ejecuta el paso final para que el sistema sea arrancable de forma automática sin intervención manual:
bcdboot Z:\Windows /s S: /f UEFI >> "%LOG_FILE%" 2>&1
Este comando escribe los archivos de arranque en la partición EFI (S:) apuntando a la instalación de Windows que acabamos de aplicar en la partición primaria (Z:).
Al concentrar todo en un único archivo de script, logramos un proceso autocontenido: el mismo archivo prepara el hardware, automatiza la selección de la imagen y configura el arranque UEFI.
El Siguiente Desafío: La Migración Masiva y el Script de Captura
Tener un script de despliegue sólido fue un gran avance, pero el verdadero desafío apareció con una actualización masiva de discos HDD a SSD.
Normalmente, un proceso de captura se hace una sola vez: se prepara un sistema de referencia, se captura la imagen base y se despliega tantas veces como haga falta. Sin embargo, en una migración de hardware uno a uno no se puede hacer eso. Cada equipo debe conservar intactos los datos, perfiles y controladores específicos de su usuario. Esto significaba realizar una captura y un despliegue individual por cada ordenador del parque.
Al multiplicar el proceso por el número de máquinas a actualizar, cualquier fricción manual se vuelve crítica.
Fue en este escenario donde nació capture.cmd y donde me topé con una limitación de DISM al procesar perfiles: el tratamiento de los archivos de OneDrive almacenados en la nube.
DISM no procesa correctamente los punteros o enlaces de archivos que solo residen en la nube de OneDrive. Al capturar la imagen, el comando finalizaba sin indicar ningún error. Sin embargo, al desplegar esa misma imagen en un nuevo ordenador, el proceso fallaba a mitad de camino y se suspendía.
Esto me obligaba a montar la imagen capturada en otro ordenador para limpiarla a mano, o a repetir la captura borrando previamente los directorios de OneDrive de los perfiles afectados.
La solución que encontré era usar el parámetro /ConfigFile de DISM para excluir los ficheros dentro de las carpetas de OneDrive pero los comodines de exclusión estándar de DISM resultaron ser muy erráticos. Por ejemplo, una regla genérica como Users/*/OneDrive* no solo excluía los archivos sincronizados en la nube, sino que también coincidía con ejecutables internos como Users/usuario/AppData/Microsoft/OneDrive/OneDrive.exe, provocando fallos en el perfil al iniciar sesión tras el despliegue.
Para resolverlo de forma limpia, la solución fue crear de manera dinámica el archivo de configuración exclude.ini personalizado antes de iniciar la captura.
El código en acción: Generación de exclusiones al vuelo
Este es el bloque que resuelve el problema en capture.cmd:
(
echo [ExclusionList]
:: ... exclusiones estándar del sistema ...
for /d %%U in ("%WINDOWS_PARTITION%\Users\*") do (
set "USER_NAME=%%~nxU"
if /I not "!USER_NAME!"=="All Users" (
for /d %%O in ("%%U\OneDrive*") do (
echo \Users\!USER_NAME!\%%~nxO\*
)
)
)
) > "%TEMP%\exclude.ini"
¿Cómo funciona este filtro dinámico?
- Exclusiones del sistema estándar: Primero escribimos en el archivo temporal las exclusiones clásicas de Windows (archivos de paginación, papelera, etc.) que no deben incluirse en una clonación limpia.
-
Iteración de perfiles de usuario: Mediante
for /d %%Ubarremos el directorioUsers. Guardamos el nombre de cada carpeta enUSER_NAME. -
Filtro de cuentas y búsqueda de OneDrive: Omitimos cuentas del sistema como "All Users". Luego, buscamos directorios que comiencen con
OneDrivedentro del perfil de cada usuario (%%U\OneDrive*). -
Rutas absolutas sin comodines destructivos: Si se encuentra una carpeta de OneDrive para ese usuario específico, escribimos su ruta absoluta exacta (por ejemplo,
\Users\usuario\OneDrive - Empresa\*) en la sección de exclusiones.
⚠️ El peligro oculto de
/ConfigFileen DISMUn comportamiento crítico de DISM es que, al pasarle un archivo
/ConfigFilepersonalizado, se desactivan por completo sus exclusiones nativas por defecto. Si no se declaran de nuevo a mano, archivos masivos de sistema comopagefile.sys,hiberfil.syso carpetas de reciclaje se incluirán en la captura. Por ello, el script escribe primero estas exclusiones estándar al inicio del archivo temporal.
Una vez generado el archivo en %TEMP%\exclude.ini, llamamos a DISM pasándole este archivo como parámetro:
Dism /Capture-Image /ImageFile:"%DESTINATION%\%IMAGE_NAME%" /CaptureDir:%WINDOWS_PARTITION%\ /Name:"My Windows partition" /ConfigFile:"%TEMP%\exclude.ini" /CheckIntegrity
Al terminar, el script elimina el archivo .ini temporal. Esto garantizó capturas exitosas sin intervención manual en los perfiles de usuario y sin falsos positivos en las exclusiones.
Conclusiones y Trade-offs
A veces, las soluciones de infraestructura más robustas no requieren suites de software de terceros complejas ni dependencias pesadas. La combinación de scripts de consola de Batch y herramientas nativas como DISM y DiskPart nos permitió construir un flujo de clonación confiable, dinámico y robusto.
Como en cualquier decisión de diseño técnico, hubo compromisos:
- Ventajas: Es universal, ligero y se ejecuta en cualquier entorno WinPE sin requerir red ni dependencias. Aunque PowerShell suele ser la primera opción para automatizar en Windows, muchas imágenes de arranque WinPE minimalistas omiten el soporte de PowerShell para ahorrar tamaño. Batch garantiza que el script funcione de forma nativa en cualquier consola de recuperación por básica que sea.
-
Trade-offs: La sintaxis de Batch es rígida, carece de un manejo de errores sofisticado y la manipulación de variables en bucles requiere habilitar la expansión retardada (
setlocal ENABLEDELAYEDEXPANSION), lo cual añade cierta complejidad al código.
Esta automatización transformó un proceso manual propenso a fallos tipográficos en una tarea de selección de opciones en un menú, asegurando que cada clonación e instalación se realizara sobre un estado limpio y predecible.
El código fuente completo de los scripts y las instrucciones de uso están disponibles en el repositorio del proyecto en GitHub. ¡Las sugerencias, reportes de fallos y contribuciones son más que bienvenidos!
Top comments (0)