El problema: dos mundos, dos identificadores
Escenario habitual en equipos que gestionan infraestructura Azure con IaC: el equipo de sistemas Windows (Wintel) abre un ticket solicitando cambios en discos de una VM. El problema es que hablan idiomas diferentes:
- Wintel dice: "Elimina el disco con Msft Virtual Disk ID
60022480XXXXXXXXXXXXXXXXXXXXXXXXy amplía el de 32 GB a 48 GB" - Tú ves en Terraform:
data1,data2,data3con LUNs y tamaños
El ID que proporciona Windows (60022480...) es un UniqueId del guest OS que no aparece en ningún sitio de tu código IaC ni en el portal de Azure. No puedes simplemente hacer grep en el repositorio y encontrar la respuesta.
Este artículo documenta el proceso completo para resolver este mapeo y ejecutar el cambio de forma segura con Terragrunt.
Conceptos clave
Antes de entrar en materia, aclaremos la terminología:
| Concepto | Dónde vive | Ejemplo |
|---|---|---|
| UniqueId (Guest) | Dentro de Windows | 60022480B94E649D631F68911AA694D1 |
| Managed Disk | Azure Resource Manager | vm-name-data2-1x |
| LUN | Configuración de VM en Azure | 10 |
| DiskNumber | Windows Disk Management | 2 |
| Nombre en IaC | Terragrunt/Terraform | data2 |
La clave es entender que DiskNumber en Windows ≠ LUN en Azure. Son conceptos independientes que requieren correlación cruzada.
Paso 1: Inventario desde Azure (fuente de verdad)
Lo primero es obtener qué discos tiene realmente la VM en Azure:
az account set --subscription "<subscription-id>"
az vm show -g <resource-group> -n <vm-name> \
--query "storageProfile.dataDisks[].{LUN:lun,Name:name,SizeGB:diskSizeGb}" \
-o tableResultado ejemplo:
LUN Name SizeGB
----- -------------------- --------
1 vm-name-data1-1x 128
10 vm-name-data2-1x 32
11 vm-name-data3-1x 128Ya sabemos: 3 data disks en Azure con LUN 1, 10 y 11.
Paso 2: Inventario desde el guest OS (RunCommand)
Si no tienes acceso RDP a la VM, puedes ejecutar un script PowerShell remotamente con az vm run-command invoke. Este paso suele requerir varias iteraciones porque el entorno no siempre devuelve lo esperado.
2.1 Lanzar RunCommand desde Azure CLI
az vm run-command invoke \
-g <resource-group> \
-n <vm-name> \
--command-id RunPowerShellScript \
--scripts "Get-Disk | Select-Object Number, @{N='SizeGB';E={[math]::Round(\$_.Size/1GB,0)}}, UniqueId | Format-Table -AutoSize"La respuesta viene en JSON con los campos stdout y stderr. Si el script falla, el error aparecerá en stderr.
2.2 El error del script truncado (primer intento fallido)
En el primer intento, se envió un script más complejo con Where-Object para filtrar por ID específico. El resultado fue:
You must provide a value expression following the '-eq' operator.
Missing closing '}' in statement block or type definition.¿Por qué ocurre? El script se cortó a mitad de un bloque Where-Object { $_.Index -eq ... }. RunCommand tiene un límite práctico en el tamaño del --scripts inline, y caracteres especiales como $, {, } necesitan escapado correcto dependiendo del shell desde donde se lance.
Solución: Usar el script completo sin truncar y con variables explícitamente casteadas:
Get-Disk | ForEach-Object {
$d = $_
$wmi = Get-CimInstance Win32_DiskDrive |
Where-Object { $_.Index -eq [int]$d.Number } |
Select-Object -First 1
[pscustomobject]@{
DiskNumber = $d.Number
SizeGB = [math]::Round($d.Size / 1GB, 0)
UniqueId = $d.UniqueId
LUN = $wmi.SCSITargetId
Model = $wmi.Model
}
} | Sort-Object DiskNumber | Format-Table -AutoSizeConsejo: Si el script es largo, es más fiable pasarlo como fichero con
--scripts @script.ps1en vez de inline.
2.3 El resultado confuso: LUN=0 para todos
El segundo intento se ejecutó correctamente, pero devolvió algo inesperado al consultar un disco específico por su UniqueId:
TargetId : 600224808C5B855329AF53DEFF061AF0
DiskNumber : 1
UniqueId : 600224808C5B855329AF53DEFF061AF0
SerialNumber :
LUN : 0Aquí es donde muchos se confunden: el disco aparece con LUN 0, pero en Azure los data disks de esta VM están en LUN 1, 10 y 11. ¿Cómo es posible?
Explicación: SCSITargetId (que es lo que Win32_DiskDrive expone como LUN dentro del guest) no es lo mismo que el LUN de Azure. En muchas VMs Azure con Windows:
- El controlador SCSI del guest reporta
SCSITargetId = 0para todos los discos Msft Virtual Disk - El LUN real de Azure solo es visible desde la API de Azure (el
az vm showdel Paso 1) - Intentar mapear usando este campo te llevará a decisiones incorrectas
2.4 Inventario completo final (la evidencia decisiva)
Ante la ambigüedad del LUN en guest, lo correcto es obtener el inventario completo de todos los discos con sus tamaños y UniqueIds:
DiskNumber SizeGB UniqueId LUN Model
---------- ------ -------- --- -----
0 128 IDE\DISKVIRTUAL_HD... 0 Virtual HD
1 128 600224808C5B855329AF53DEFF061AF0 0 Msft Virtual Disk
2 32 60022480B94E649D631F68911AA694D1 0 Msft Virtual Disk
3 128 60022480B16D7C3BE95C7D51B2235E9A 0 Msft Virtual DiskAhora sí tenemos lo que necesitamos: UniqueId + tamaño para cada disco. Esto, combinado con el inventario Azure, nos permite hacer la correlación.
Observaciones:
- DiskNumber 0 con ID
IDE\DISK...es el disco del SO (no se toca) - Los tres restantes son los managed data disks (todos reportan
LUN 0en guest, pero eso es irrelevante) - El de 32 GB es inequívocamente
data2 - Entre los dos de 128 GB, Wintel identificó explícitamente cuál eliminar por su UniqueId
Paso 3: Correlación cruzada (la tabla de mapeo)
Con la evidencia de ambos lados, construimos la tabla de decisión. Este es el artefacto más importante del proceso — es lo que justifica el cambio ante una auditoría:
| UniqueId Guest | Size Guest | Disco Azure | Nombre IaC | LUN Azure | Acción |
|---|---|---|---|---|---|
...1AF0 | 128 GB | vm-name-data1-1x | data1 | 1 | Eliminar |
...94D1 | 32 GB | vm-name-data2-1x | data2 | 10 | Resize → 48 GB |
...5E9A | 128 GB | vm-name-data3-1x | data3 | 11 | Mantener |
Lógica de la decisión
- Disco de 32 GB: Solo hay uno en Azure (
data2, LUN 10) y uno en el guest (UniqueId...94D1). Mapeo inequívoco. - Disco de 128 GB a eliminar: Wintel proporcionó el UniqueId explícito (
...1AF0). En Azure hay dos discos de 128 GB (data1en LUN 1 ydata3en LUN 11). Tomamosdata1como objetivo de eliminación basándonos en la evidencia combinada. - Disco de 128 GB a conservar:
data3(LUN 11, UniqueId...5E9A) no fue mencionado en la petición, se mantiene sin cambios.
Regla de oro: Cuando hay múltiples discos del mismo tamaño, nunca decidas solo por tamaño. Exige al equipo de sistemas que confirme el UniqueId, la letra de unidad, o el volumen label del disco afectado.
Paso 4: Implementación en Terragrunt
En el módulo de VM Windows, los discos de datos se definen en el bloque dynamic_data_disks del terragrunt.hcl:
Estado ANTES del cambio
dynamic_data_disks = {
"data1" = {
location = "North Europe"
storage_account_type = "StandardSSD_LRS"
create_option = "Empty"
managed_disk_size_gb = 128
public_network_access_enabled = false
os_type = "Windows"
lun = 1
caching = "ReadWrite"
}
"data2" = {
location = "North Europe"
storage_account_type = "StandardSSD_LRS"
create_option = "Empty"
managed_disk_size_gb = 32
public_network_access_enabled = false
os_type = "Windows"
lun = 10
caching = "ReadWrite"
}
"data3" = {
location = "North Europe"
storage_account_type = "StandardSSD_LRS"
create_option = "Empty"
managed_disk_size_gb = 128
public_network_access_enabled = false
os_type = "Windows"
lun = 11
caching = "ReadWrite"
}
}Estado DESPUÉS del cambio
dynamic_data_disks = {
"data2" = {
location = "North Europe"
storage_account_type = "StandardSSD_LRS"
create_option = "Empty"
managed_disk_size_gb = 48 # Antes: 32
public_network_access_enabled = false
os_type = "Windows"
lun = 10
caching = "ReadWrite"
}
"data3" = {
location = "North Europe"
storage_account_type = "StandardSSD_LRS"
create_option = "Empty"
managed_disk_size_gb = 128
public_network_access_enabled = false
os_type = "Windows"
lun = 11
caching = "ReadWrite"
}
}Cambios realizados:
- Eliminado: bloque
data1completo (el módulo se encarga del detach + delete) - Modificado:
managed_disk_size_gbdedata2de 32 → 48 - Sin cambios:
data3permanece intacto
Paso 5: Validación pre-apply
terragrunt planEl plan debe mostrar exactamente:
destroy: disco data1 (LUN 1, 128 GB)update in-place: disco data2, size 32 → 48- Sin cambios en data3
Si aparece cualquier otro cambio inesperado: STOP. Revisa si hay drift en el estado o variables heredadas que hayan cambiado.
Paso 6: Apply y verificación post-cambio
terragrunt applyVerificación Azure
az vm show -g <resource-group> -n <vm-name> \
--query "storageProfile.dataDisks[].{LUN:lun,Name:name,SizeGB:diskSizeGb}" \
-o tableEsperado:
LUN Name SizeGB
----- -------------------- --------
10 vm-name-data2-1x 48
11 vm-name-data3-1x 128Verificación guest (solicitar a Wintel)
- Rescan de discos en Disk Management
- Confirmar que el disco eliminado ya no aparece
- Confirmar que el disco ampliado muestra 48 GB
- Extender la partición a nivel de filesystem (el resize de Azure no lo hace automáticamente)
Estrategia de rollback
Si algo sale mal post-apply:
- No tocar nada más — no intentar arreglar sobre la marcha
- Reponer
data1endynamic_data_diskscon sus valores originales - Revertir
data2a 32 GB si es necesario terragrunt plan→ verificar que el plan es coherenteterragrunt apply
Nota importante: Si el disco eliminado tenía datos, el rollback creará un disco vacío nuevo. Los datos se habrán perdido. Por eso es crítico confirmar con el equipo de sistemas que el disco está libre de datos antes del apply.
Errores comunes y lecciones aprendidas
1. Confiar en SCSITargetId del guest
En muchas VMs Azure con Windows, Win32_DiskDrive.SCSITargetId devuelve 0 para todos los discos de datos. No uses ese valor como fuente de verdad para el LUN.
2. Scripts RunCommand incompletos
Si copias scripts PowerShell multilínea en RunCommand, asegúrate de que el bloque Where-Object { ... } esté completo. Un corte en medio produce errores crípticos:
You must provide a value expression following the '-eq' operator.
Missing closing '}' in statement block or type definition.3. Dos discos del mismo tamaño
Cuando hay múltiples discos con el mismo tamaño (como dos de 128 GB), no puedes mapear solo por tamaño. Necesitas el UniqueId del guest o, alternativamente, que Wintel te confirme la letra de unidad/volumen asociada.
4. Resize no extiende la partición
Azure puede redimensionar el managed disk, pero el filesystem dentro de Windows seguirá viendo el tamaño anterior hasta que alguien ejecute Extend-Volume o lo haga desde Disk Management.
Checklist operativo
Usa esta checklist para cualquier cambio de discos en VMs Azure gestionadas con IaC:
- [ ] Subscription y Resource Group confirmados
- [ ] Inventario Azure (
az vm show) guardado - [ ] Inventario guest (PowerShell) guardado
- [ ] Tabla de mapeo completada y revisada
- [ ] Cambio en IaC revisado (idealmente por segundo par de ojos)
- [ ]
terragrunt plansin cambios inesperados - [ ] Backup/snapshot verificado antes de operación destructiva
- [ ] Apply ejecutado en ventana de cambio aprobada
- [ ] Verificación post-apply en Azure
- [ ] Verificación post-apply con equipo de sistemas
- [ ] Documentación archivada
Conclusión
El mapeo de disk IDs entre Windows guest y Azure managed disks no es trivial, especialmente cuando los identificadores que maneja cada equipo son completamente diferentes. La clave está en:
- No asumir — siempre cruzar evidencia de ambos lados
- Documentar la decisión — la tabla de mapeo es tu seguro ante auditorías
- Validar antes de destruir — un
planlimpio no cuesta nada, un disco eliminado por error cuesta mucho
Con un flujo bien definido y Terragrunt/Terraform como fuente de verdad de la infraestructura, estos cambios pasan de ser operaciones de riesgo a procedimientos controlados y repetibles.
Comentarios