AZ-104

Deep Dive

D3 · Computo

ARM Templates y Bicep

Los ARM Templates y Bicep son las herramientas nativas de Azure para Infrastructure as Code. El AZ-104 evalúa la estructura de templates, modos de despliegue, funciones clave y la diferencia entre Complete e Incremental — una trampa frecuente.

Infraestructura como Código en Azure

Azure ofrece varias opciones para IaC. El AZ-104 se centra en las herramientas nativas de Azure.

HerramientaFormatoNativo Azure?Cuándo usar
ARM TemplatesJSON✅ SíEstándar histórico. Verboso pero completo. Aparece en examen AZ-104.
BicepBicep (DSL)✅ Sí (transpila a ARM)Sintaxis simplificada de ARM. Microsoft lo recomienda sobre JSON.
TerraformHCL❌ TercerosMulti-cloud. No evaluado en AZ-104 pero común en empresas.
Azure CLI / PowerShellImperativo✅ SíScripts de automatización. No declarativo — no gestiona estado.
Icon-general-9

Estructura de ARM Templates

Un ARM template es un archivo JSON que describe los recursos a desplegar. Tiene secciones obligatorias y opcionales. Solo $schema, contentVersiony resources son obligatorios.

{
  "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0",

  "parameters": {                  // Valores configurables en deploy-time
    "storageAccountName": {
      "type": "string",
      "defaultValue": "mystorage",
      "minLength": 3,
      "maxLength": 24,
      "metadata": { "description": "Storage account name" }
    },
    "location": {
      "type": "string",
      "defaultValue": "[resourceGroup().location]"
    }
  },

  "variables": {                   // Valores calculados internamente
    "storageAccountId": "[resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName'))]"
  },

  "functions": [],                 // Funciones UDF (raro en examen)

  "resources": [                   // Recursos a desplegar (obligatorio)
    {
      "type": "Microsoft.Storage/storageAccounts",
      "apiVersion": "2023-01-01",
      "name": "[parameters('storageAccountName')]",
      "location": "[parameters('location')]",
      "sku": { "name": "Standard_LRS" },
      "kind": "StorageV2",
      "properties": {
        "supportsHttpsTrafficOnly": true
      },
      "dependsOn": []              // Dependencias explícitas
    }
  ],

  "outputs": {                     // Valores retornados tras el deploy
    "storageEndpoint": {
      "type": "string",
      "value": "[reference(parameters('storageAccountName')).primaryEndpoints.blob]"
    }
  }
}
$schema✅

URL del esquema JSON que valida el template. Diferente para subscription vs RG scope.

contentVersion✅

Versión semántica del template. Convención: "1.0.0.0". No tiene funcionalidad automática.

resources✅

Array de recursos a crear. Puede estar vacío [] pero debe existir.

parameters❌

Valores configurables en deploy time. Pueden tener defaultValue, allowedValues, min/maxLength.

variables❌

Valores calculados internamente. No configurables desde fuera — solo son útiles dentro del template.

outputs❌

Valores retornados al finalizar el deploy. Útiles para pasar datos entre templates (linked templates).

Funciones de ARM

Las funciones ARM se usan dentro de expresiones [...] para construir valores dinámicamente. Son fundamentales para templates reutilizables.

FunciónUsoEjemplo
parameters('name')Accede al valor de un parámetro[parameters('location')]
variables('name')Accede al valor de una variable[variables('storageId')]
resourceGroup()Propiedades del RG actual (name, location, id)[resourceGroup().location]
subscription()Propiedades de la suscripción (id, tenantId, etc.)[subscription().subscriptionId]
resourceId(type, name)ID único de un recurso Azure[resourceId('Microsoft.Storage/storageAccounts', 'mystorage')]
reference(name)Propiedades en runtime de un recurso desplegado[reference('mystorage').primaryEndpoints.blob]
concat(a, b, c)Concatena strings o arrays[concat('storage', uniqueString(resourceGroup().id))]
uniqueString(seed)Genera string único de 13 chars basado en seed[uniqueString(resourceGroup().id)]
toLower(str)Convierte string a minúsculas[toLower(parameters('name'))]
format(template, args)Formatea un string con valores[format('{0}-storage', parameters('env'))]
if(cond, true, false)Condicional ternario[if(equals(parameters('env'), 'prod'), 'Premium_LRS', 'Standard_LRS')]
copyIndex()Índice actual en un loop de copy[copyIndex()]
Icon-general-7

Modos de Despliegue — Complete vs Incremental

El modo de despliegue determina cómo Azure maneja los recursos existentes en el RG que NO están en el template. Esta es la trampa más frecuente del AZ-104 sobre ARM Templates.

Incremental (por defecto)

  • ✅Recursos en el template → creados o actualizados
  • ✅Recursos en el RG pero NO en el template → se mantienen intactos
  • ⚠️No garantiza estado final idéntico al template
  • ✅Más seguro: no elimina nada inesperadamente

--mode Incremental (default)

Complete

  • ✅Recursos en el template → creados o actualizados
  • 🚫Recursos en el RG pero NO en el template → ELIMINADOS
  • ✅Garantiza que el RG = lo que dice el template (estado deseado)
  • ⚠️Peligroso si el template no está completo

--mode Complete

Ejemplo de trampa de examen

Tienes un RG con VM-A, VM-B y StorageAccount-1. Despliegas un template en modo Completeque solo define VM-A. Resultado: VM-B y StorageAccount-1 son ELIMINADOS automáticamente.

Templates Anidados y Vinculados

Nested Templates

El template del recurso hijo está embebido inline dentro del template padre bajo properties.template. No requiere URL accesible. Útil para templates pequeños o cuando no tienes almacenamiento externo.

Linked Templates

El template hijo está en una URL accesible (Storage Account, GitHub). Se referencia via properties.templateLink.uri. Permite templates modulares y reutilizables. Requiere que la URL sea accesible durante el deploy.

// Referencia a linked template

{
  "type": "Microsoft.Resources/deployments",
  "apiVersion": "2022-09-01",
  "name": "storageDeployment",
  "properties": {
    "mode": "Incremental",
    "templateLink": {
      "uri": "https://mystorageaccount.blob.core.windows.net/templates/storage.json",
      "contentVersion": "1.0.0.0"
    },
    "parameters": {
      "storageAccountName": { "value": "[parameters('storageName')]" }
    }
  }
}

Bicep — Sintaxis y Equivalencias

Bicep es un DSL (Domain Specific Language) que compila a ARM JSON. Misma API, mismos recursos — solo sintaxis más simple. Azure CLI y Portal pueden desplegar Bicep directamente sin pre-compilar.

ConceptoARM JSONBicep
Parámetro"parameters": { "name": { "type": "string" } }param name string
Variable"variables": { "id": "valor" }var id = 'valor'
Recurso"type": "Microsoft.Storage/...", "name": "mystorage"resource mystorage 'Microsoft.Storage/storageAccounts@2023-01-01'
Output"outputs": { "ep": { "type": "string", "value": "..." } }output ep string = mystorage.properties.primaryEndpoints.blob
Referencia[reference('name').property]existingResource.properties.property
Módulo (=linked)Microsoft.Resources/deployments + templateLinkmodule storage './storage.bicep' = { ... }
Loop"copy": { "name": "loop", "count": 3 }resource vm 'Microsoft.Compute/virtualMachines@...' = [for i in range(0,3): {...}]
Condicional"condition": "[equals(param, 'prod')]"resource vm '...' = if (env == 'prod') { ... }

// Bicep equivalente al ARM JSON del ejemplo anterior

param storageAccountName string = 'mystorage'
param location string = resourceGroup().location

resource storageAccount 'Microsoft.Storage/storageAccounts@2023-01-01' = {
  name: storageAccountName
  location: location
  sku: { name: 'Standard_LRS' }
  kind: 'StorageV2'
  properties: {
    supportsHttpsTrafficOnly: true
  }
}

output storageEndpoint string = storageAccount.properties.primaryEndpoints.blob

Comandos de Despliegue

// Azure CLI — despliegues en distintos scopes

# Resource Group scope (más común en AZ-104)
az deployment group create \
  --resource-group myRG \
  --template-file main.bicep \
  --parameters storageAccountName=mystorage \
  --mode Incremental

# Subscription scope
az deployment sub create \
  --location westeurope \
  --template-file main.bicep

# Management Group scope
az deployment mg create \
  --management-group-id myMG \
  --location westeurope \
  --template-file main.bicep

# Tenant scope
az deployment tenant create \
  --location westeurope \
  --template-file main.bicep

// PowerShell equivalente

# Resource Group scope
New-AzResourceGroupDeployment `
  -ResourceGroupName myRG `
  -TemplateFile main.bicep `
  -storageAccountName mystorage `
  -Mode Incremental

# Subscription scope
New-AzDeployment `
  -Location westeurope `
  -TemplateFile main.bicep

What-if y Deployment Stacks

What-if — previsualizar cambios

Muestra qué recursos serían creados, modificados o eliminados SIN ejecutar el deploy. Esencial para validar antes de un deploy en modo Complete.

az deployment group what-if \

--resource-group myRG \

--template-file main.bicep

+ Create: VM-new (nuevos)

~ Modify: StorageAccount-1 (cambios)

- Delete: OldVM (en modo Complete)

= NoChange: VNet-1 (sin cambios)

Deployment Stacks

Gestión de ciclo de vida de recursos desplegados como grupo. Permite crear, actualizar y eliminar el stack completo como unidad. Evita recursos huérfanos cuando un template se borra.

  • •Define qué pasa con recursos no gestionados: detach, delete, deleteAll
  • •Aplica deny assignments automáticamente para proteger recursos del stack
  • •Soporta scope: RG, sub, MG

Trampas de Examen

Complete mode elimina recursos que NO están en el template

Esta es la trampa más frecuente sobre ARM. Si despliegas en modo Complete un template que no incluye todos los recursos del RG, los recursos ausentes son ELIMINADOS. Por defecto el modo es Incremental (no elimina nada que no esté en el template).

Bicep transpila a ARM — misma API, mismos recursos

Bicep NO es un servicio separado ni tiene su propio motor de despliegue. Se compila a ARM JSON y Azure despliega el ARM JSON resultante. La diferencia es solo sintaxis. El AZ-104 puede preguntar si Bicep soporta cierta característica de ARM — sí, porque es el mismo resultado.

dependsOn es necesario para dependencias implícitas no detectables

ARM detecta automáticamente dependencias cuando usas reference() o resourceId() en propiedades. Sin embargo, si la dependencia es externa (ej: script que espera a que esté lista una DB), debes usar dependsOn explícito con el nombre del recurso.

parameters() NO funciona en variables de la sección "variables"

Falso — sí funciona. Las variables pueden referenciar parámetros. Lo que NO puede hacer una variable es referenciar otra variable que ya depende de ella (referencia circular). parameters() en variables es un patrón legítimo y común.

Linked templates requieren URL accesible durante el deploy

El ARM engine descarga el template vinculado durante la ejecución del deploy. Si el archivo está en un Storage Account privado, el deploy fallará. Solución: usar una SAS URL temporal o alojar en GitHub público / Template Specs.

¿Entendiste este tema?

Pon a prueba lo que acabas de aprender

Un administrador despliega un ARM template en modo Complete sobre un Resource Group que contiene: VM-A, VM-B, VNet-1 y StorageAccount-1. El template solo define VM-A y VNet-1. ¿Cuál es el resultado?