Cierras el módulo con la herramienta que más tiempo te va a ahorrar durante el resto del curso y del resto de tu carrera en Azure: la línea de comandos. En la lección anterior quedó claro que portal, CLI, PowerShell y plantillas hablan todos con la misma API de Azure Resource Manager. Lo que cambia es la velocidad, la precisión y —sobre todo— la capacidad de repetir exactamente lo mismo mañana.

Aquí aprenderás a instalar y usar Azure CLI, a consultar sus salidas con --query, a manejar varias suscripciones sin equivocarte de entorno, cuándo tiene más sentido Azure PowerShell, cómo funciona Cloud Shell (y por qué te crea una cuenta de almacenamiento). Y terminarás escribiendo un script completo, comentado, parametrizado e idempotente que despliega la base de la plataforma de Contoso Airlines y sabe borrarla después para no gastar de más.

Contenido

  1. Por qué automatizar desde la línea de comandos
  2. Instalar Azure CLI y comprobar la versión
  3. Iniciar sesión y trabajar con varias suscripciones
  4. Anatomía de un comando az y ayuda integrada
  5. Formatos de salida y consultas con --query
  6. Azure PowerShell y cuándo elegir cada herramienta
  7. Cloud Shell
  8. Script completo: la base de Contoso Airlines
  9. Limpieza: borrar sin dejar rastro de gasto
  10. Errores Comunes y Consejos
  11. Ejercicios
  12. Conclusión

  1. Por qué automatizar desde la línea de comandos

El portal es excelente para explorar. Para trabajar, tiene tres problemas:

Problema del portal Qué aporta la línea de comandos
No es repetible: 40 clics hoy, 40 clics distintos mañana Un script produce el mismo resultado siempre
No es auditable: nadie sabe qué opciones marcaste El script vive en Git, se revisa y se compara
No escala: crear 20 recursos iguales es una tarde perdida Un bucle for lo hace en un minuto
Es lento para consultas: "dame todas las VM sin etiqueta propietario" no tiene botón Una consulta --query lo resuelve en una línea

Además, la CLI es el puente natural hacia lo que viene después: las tuberías de despliegue del módulo 5 ejecutan comandos az, y los diagnósticos del módulo 7 se apoyan en consultas de línea de comandos.

Regla de oro, repetida a propósito: lo que haces una vez, hazlo en el portal; lo que harás dos veces, escríbelo.

  1. Instalar Azure CLI y comprobar la versión

Azure CLI es multiplataforma y está escrita en Python, pero se instala como un paquete nativo en cada sistema.

Windows

# Opción recomendada: gestor de paquetes de Windows.
winget install --exact --id Microsoft.AzureCLI

# Alternativa con MSI silencioso, útil en despliegues corporativos.
# Descarga el instalador oficial y ejecútalo con /quiet.

Tras instalar, cierra y vuelve a abrir la terminal para que se actualice la variable PATH.

macOS

# Con Homebrew, el gestor de paquetes más habitual en macOS.
brew update && brew install azure-cli

Linux (Debian/Ubuntu)

# Script oficial de instalación: añade el repositorio de Microsoft e instala el paquete.
# Revisa siempre un script antes de ejecutarlo con privilegios (buena práctica general).
curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash

Para RHEL, Fedora o CentOS se usa dnf; para openSUSE, zypper. La documentación oficial detalla cada caso.

Comprobar la instalación

# Muestra la versión de la CLI, de sus extensiones y de Python.
az version

Salida de ejemplo:

{
  "azure-cli": "2.64.0",
  "azure-cli-core": "2.64.0",
  "azure-cli-telemetry": "1.1.0",
  "extensions": {}
}

Mantenerla actualizada

# Actualiza la CLI a la última versión estable (funciona en la mayoría de plataformas).
az upgrade

# Desactiva la pregunta de telemetría y confirmaciones interactivas en scripts.
az config set core.collect_telemetry=false

Azure CLI se actualiza cada pocas semanas. Trabajar con una versión muy antigua provoca errores del tipo "unrecognized arguments" cuando sigues documentación reciente: si un parámetro no existe en tu instalación, lo primero que hay que probar es actualizar.

  1. Iniciar sesión y trabajar con varias suscripciones

# Abre el navegador para autenticarte de forma interactiva.
# Tras iniciar sesión, la CLI guarda un token local y ya no lo pide en cada comando.
az login

Variantes útiles:

# Sin navegador (servidores, SSH): muestra un código para introducir en microsoft.com/devicelogin.
az login --use-device-code

# Especificar el inquilino cuando tu identidad pertenece a varios directorios.
az login --tenant contoso-airlines.onmicrosoft.com

Para automatización desatendida (tuberías de CI/CD) no se usa az login interactivo, sino una entidad de servicio o una identidad administrada. Se ven en las lecciones 04-02 y 05-03. Nunca metas credenciales de usuario en un script.

Varias suscripciones: el error más caro de todos

Si tu identidad tiene acceso a Contoso Airlines - Producción y a Contoso Airlines - Desarrollo, cada comando az se ejecuta contra la suscripción activa. Confundirse aquí es cómo se borran cosas en producción.

# Ver todas las suscripciones accesibles. IsDefault marca la activa.
az account list --output table
Name                              CloudName    SubscriptionId                        State    IsDefault
--------------------------------  -----------  ------------------------------------  -------  ---------
Contoso Airlines - Producción     AzureCloud   8f4c2b7a-1d3e-4a55-9c11-0a7b6e2d4f90  Enabled  True
Contoso Airlines - Desarrollo     AzureCloud   3b91e5d0-7c42-4f88-a2e6-5d9c1a8b3e77  Enabled  False
# Cambiar la suscripción activa (acepta nombre o identificador).
az account set --subscription "Contoso Airlines - Desarrollo"

# Confirmar dónde estás antes de tocar nada. Hazlo un hábito.
az account show --query "{Suscripcion:name, Id:id, Usuario:user.name}" --output table

Dos defensas prácticas:

  1. Empieza todos tus scripts fijando la suscripción explícitamente con az account set. No confíes en la que esté activa.
  2. Usa el parámetro --subscription en los comandos críticos, para no depender del estado global:
az group delete --name rg-contoso-reservas-dev \
  --subscription "Contoso Airlines - Desarrollo" --yes

  1. Anatomía de un comando az y ayuda integrada

Todos los comandos de Azure CLI siguen la misma estructura:

az <grupo> [<subgrupo>] <comando> [--parámetros]

Ejemplo desglosado:

az storage account create --name sttarjetascontosodev --resource-group rg-contoso-reservas-dev --location westeurope --sku Standard_LRS
Parte Valor Qué es
az — El programa
storage grupo La familia de servicio
account subgrupo El objeto concreto dentro de esa familia
create comando La acción (create, list, show, update, delete)
--name, --resource-group... parámetros Los datos de la operación

Los verbos se repiten en toda la CLI, y eso hace que sea predecible: si sabes az vm list, intuyes az storage account list, az webapp list y az sql db list.

Abreviaturas frecuentes: -n para --name, -g para --resource-group, -l para --location, -o para --output.

Ayuda integrada

# Ayuda de un grupo: muestra subgrupos y comandos disponibles.
az storage --help

# Ayuda de un comando concreto: lista TODOS sus parámetros, cuáles son obligatorios y ejemplos.
az storage account create --help

# Búsqueda inteligente por lenguaje natural, con ejemplos reales de uso.
az find "az storage account"
az find "crear una cuenta de almacenamiento"

Y el modo interactivo, muy útil mientras aprendes (requiere instalar la extensión):

# Autocompletado, descripciones de parámetros y ejemplos mientras escribes.
az interactive

  1. Formatos de salida y consultas con --query

Por defecto, la CLI devuelve JSON, que es completo pero incómodo de leer. El parámetro --output (o -o) cambia el formato:

Valor Resultado Cuándo usarlo
json (por defecto) JSON con formato Ver todos los campos disponibles
jsonc JSON coloreado Lectura en terminal
table Tabla legible Uso diario e informes rápidos
tsv Valores separados por tabuladores, sin cabeceras ni comillas Scripts: alimentar variables
yaml YAML Configuraciones legibles
none Nada Cuando solo importa que la operación funcione

JMESPath: el lenguaje de --query

--query usa JMESPath, un lenguaje de consulta sobre JSON. Vamos con ejemplos progresivos sobre los recursos de Contoso.

Paso 1 — seleccionar una propiedad de un objeto:

# Devuelve solo la ubicación de un grupo de recursos, sin comillas (tsv), lista para una variable.
az group show --name rg-contoso-reservas-dev --query location --output tsv

Paso 2 — recorrer una lista y quedarse con un campo:

# Nombres de todos los grupos de recursos. [] recorre la lista.
az group list --query "[].name" --output tsv

Paso 3 — construir un objeto con nombres propios:

# Renombra los campos de salida. Las claves de la izquierda son etiquetas tuyas.
az group list \
  --query "[].{Nombre:name, Region:location, Estado:properties.provisioningState}" \
  --output table
Nombre                     Region      Estado
-------------------------  ----------  ---------
rg-contoso-reservas-dev    westeurope  Succeeded
rg-contoso-reservas-pro    westeurope  Succeeded

Paso 4 — filtrar con condiciones (?):

# Solo los grupos etiquetados como producción.
az group list \
  --query "[?tags.entorno=='produccion'].{Nombre:name, Propietario:tags.propietario}" \
  --output table

# Solo las cuentas de almacenamiento de West Europe.
az storage account list \
  --query "[?location=='westeurope'].{Cuenta:name, Grupo:resourceGroup, Sku:sku.name}" \
  --output table

Paso 5 — detectar lo que falta (auditoría de gobierno):

# Recursos SIN la etiqueta obligatoria "propietario": el informe que pide Marta Ríos.
# == `null` compara con nulo; los acentos graves marcan un literal JSON.
az resource list \
  --query "[?tags.propietario == \`null\`].{Recurso:name, Tipo:type, Grupo:resourceGroup}" \
  --output table

Paso 6 — combinar con funciones y ordenación:

# Contar cuántos recursos hay en total.
az resource list --query "length(@)" --output tsv

# Los recursos ordenados por nombre, mostrando solo tipo y grupo.
az resource list \
  --query "sort_by([].{Nombre:name, Tipo:type, Grupo:resourceGroup}, &Nombre)" \
  --output table

Paso 7 — usar el resultado en un script:

# Guardar un valor en una variable de shell. --output tsv evita las comillas del JSON.
CLAVE_REGION=$(az group show --name rg-contoso-reservas-dev --query location --output tsv)
echo "El grupo está en: $CLAVE_REGION"

Truco de aprendizaje: ejecuta primero el comando sin --query y con --output json para ver la estructura completa, y solo entonces escribe la consulta. Es mucho más rápido que adivinar los nombres de los campos.

  1. Azure PowerShell y cuándo elegir cada herramienta

Azure PowerShell es el otro cliente oficial, basado en el módulo Az. Hace lo mismo que la CLI, pero con la filosofía de PowerShell: en lugar de texto, devuelve objetos con propiedades sobre los que puedes operar directamente.

Instalación y primeros comandos

# Instala el módulo Az para el usuario actual (no requiere permisos de administrador).
Install-Module -Name Az -Scope CurrentUser -Repository PSGallery -Force

# Inicia sesión (abre el navegador).
Connect-AzAccount

# Ver las suscripciones disponibles y seleccionar una.
Get-AzSubscription
Set-AzContext -Subscription "Contoso Airlines - Desarrollo"

# Crear el grupo de recursos de Contoso con sus etiquetas.
New-AzResourceGroup -Name "rg-contoso-reservas-dev" -Location "westeurope" -Tag @{
    entorno       = "desarrollo"
    proyecto      = "contoso-reservas"
    "centro-coste" = "CC-1042"
    propietario   = "[email protected]"
}

# Listar recursos y filtrar por etiqueta, aprovechando los objetos de PowerShell.
Get-AzResource -TagName "proyecto" -TagValue "contoso-reservas" |
    Select-Object Name, ResourceType, ResourceGroupName |
    Format-Table

Fíjate en la diferencia de filosofía: en la CLI filtras con --query (JMESPath sobre JSON); en PowerShell filtras con Where-Object y seleccionas con Select-Object, porque ya tienes objetos.

Comparativa

Aspecto Azure CLI (az) Azure PowerShell (Az)
Sintaxis az grupo subgrupo comando Verbo-AzSustantivo (New-AzResourceGroup)
Salida JSON (texto) Objetos de .NET
Filtrado --query con JMESPath Where-Object, Select-Object
Shell nativo Bash, Zsh; funciona en cualquier shell PowerShell (Windows, Linux y macOS)
Curva de entrada Más suave si vienes de Linux Más suave si vienes de Windows
Integración Excelente en tuberías multiplataforma y contenedores Excelente con Windows Server, Microsoft 365 y Exchange
Documentación de Microsoft Ejemplos en ambas Ejemplos en ambas

Cómo elegir, sin dogmatismos:

  • Si tu equipo es de administradores de sistemas Windows y ya automatiza con PowerShell, usa PowerShell.
  • Si tu equipo viene de Linux, Docker o desarrollo multiplataforma, usa la CLI.
  • Si escribes scripts para tuberías de CI/CD en contenedores Linux, la CLI suele ser más ligera y directa.
  • Lo importante es ser coherente dentro de un mismo proyecto. En Contoso, Diego y Marta acuerdan usar Azure CLI, y ese es el criterio del curso a partir de aquí.

  1. Cloud Shell

Azure Cloud Shell es un terminal alojado en Azure, accesible desde el icono >_ de la barra superior del portal (o en shell.azure.com). Ventajas inmediatas:

  • Ya está autenticado con tu identidad: no hay que hacer az login.
  • Trae las herramientas instaladas y actualizadas: Azure CLI, Azure PowerShell, Bicep, Terraform, kubectl, Git, Python, Node.js, y editores como vim, nano y el editor gráfico integrado (code .).
  • Funciona desde cualquier navegador, incluido el móvil.

Bash o PowerShell

Puedes elegir el intérprete en el desplegable superior y cambiar en cualquier momento. Los comandos az funcionan en ambos; los Az* de PowerShell, solo en el modo PowerShell.

La cuenta de almacenamiento que crea

La primera vez que abres Cloud Shell, te pide crear (o asociar) una cuenta de almacenamiento. Esto no es un capricho:

  • Cloud Shell corre en un contenedor efímero: cuando termina la sesión, el contenedor se destruye.
  • Para que tus ficheros sobrevivan, monta un recurso compartido de Azure Files en tu directorio $HOME, dentro de una imagen de disco llamada normalmente acc_<usuario>.img.
  • Lo que guardes en $HOME (~/clouddrive) persiste entre sesiones; lo que instales fuera de ahí, no.

Aviso de coste: esa cuenta de almacenamiento es un recurso tuyo y se factura, aunque el importe sea muy pequeño (unos céntimos al mes por unos pocos GB). Cloud Shell en sí no cuesta nada; su almacenamiento, sí. Si dejas de usarlo, puedes borrar el grupo de recursos que se crea automáticamente (suele llamarse cloud-shell-storage-<región>).

Otras características y límites

Aspecto Detalle
Tiempo de inactividad La sesión se cierra tras unos 20 minutos sin interacción
Persistencia Solo $HOME / clouddrive; el resto del sistema de ficheros se pierde
Carga y descarga de ficheros Botón de Cargar/Descargar en la barra de Cloud Shell
Editor integrado code . abre un editor gráfico dentro del navegador
Sesiones simultáneas Limitadas; una sesión Bash y una PowerShell a la vez
Región del almacenamiento Se elige al crearlo; conviene ponerlo cerca de ti

Cuándo usar Cloud Shell: para aprender (no instalas nada), para tareas puntuales desde un equipo ajeno, para emergencias desde el móvil. Cuándo no: para trabajo diario intensivo o scripts largos, donde una CLI local con tu editor y tu repositorio Git es más cómoda.

  1. Script completo: la base de Contoso Airlines

Ahora el ejercicio central del módulo: un script que crea toda la base de la plataforma de Contoso Airlines. Es parametrizado (todo en variables al principio), idempotente (se puede ejecutar varias veces sin romper nada ni duplicar recursos) y está comentado línea a línea.

Guárdalo como desplegar-base-contoso.sh.

#!/usr/bin/env bash
# =============================================================================
# desplegar-base-contoso.sh
# Crea la base de la plataforma de Contoso Airlines:
#   - Grupo de recursos con las etiquetas obligatorias
#   - Cuenta de almacenamiento para las tarjetas de embarque en PDF
#   - Contenedor privado "tarjetas-embarque"
#
# Es IDEMPOTENTE: ejecutarlo dos veces deja el mismo resultado que ejecutarlo una.
# Uso:   ./desplegar-base-contoso.sh [dev|pro]
# =============================================================================

# 'set -e' aborta el script ante el primer error, en lugar de seguir a ciegas.
# 'set -u' aborta si se usa una variable no definida (evita erratas peligrosas).
# 'set -o pipefail' propaga el error de cualquier comando dentro de una tubería.
set -euo pipefail

# ------------------------------------------------------------------
# 1. PARÁMETROS. Todo lo configurable, en un solo sitio y arriba.
# ------------------------------------------------------------------
ENTORNO="${1:-dev}"                       # Primer argumento; "dev" si no se indica
SUSCRIPCION="Contoso Airlines - Desarrollo"
REGION="westeurope"                       # Región principal decidida en la lección 01-02
PROYECTO="contoso-reservas"
CENTRO_COSTE="CC-1042"
PROPIETARIO="[email protected]"

# Nombres derivados, siguiendo la convención de nomenclatura de Contoso.
GRUPO="rg-${PROYECTO}-${ENTORNO}"          # p. ej. rg-contoso-reservas-dev
# Las cuentas de almacenamiento solo admiten minúsculas y números (3-24 caracteres)
# y su nombre es ÚNICO EN TODO AZURE: añadimos un sufijo aleatorio estable por si acaso.
SUFIJO="$(echo -n "${GRUPO}" | cksum | cut -c1-4)"
CUENTA_ALM="sttarjetascontoso${ENTORNO}${SUFIJO}"
CONTENEDOR="tarjetas-embarque"

# Traducción del entorno corto al valor de etiqueta que exige el esquema de Contoso.
if [ "${ENTORNO}" = "pro" ]; then
  ENTORNO_TAG="produccion"
else
  ENTORNO_TAG="desarrollo"
fi

echo "=== Despliegue de la base de Contoso Airlines (${ENTORNO_TAG}) ==="

# ------------------------------------------------------------------
# 2. CONTEXTO. Nunca confíes en la suscripción que estuviera activa.
# ------------------------------------------------------------------
az account set --subscription "${SUSCRIPCION}"
echo "Suscripción activa: $(az account show --query name --output tsv)"

# ------------------------------------------------------------------
# 3. PROVEEDORES. Necesarios en suscripciones nuevas; es inocuo repetirlo.
# ------------------------------------------------------------------
az provider register --namespace Microsoft.Storage --wait

# ------------------------------------------------------------------
# 4. GRUPO DE RECURSOS.
#    'az group create' ya es idempotente: si existe, actualiza sus etiquetas.
# ------------------------------------------------------------------
echo "--> Grupo de recursos: ${GRUPO}"
az group create \
  --name "${GRUPO}" \
  --location "${REGION}" \
  --tags entorno="${ENTORNO_TAG}" \
         proyecto="${PROYECTO}" \
         centro-coste="${CENTRO_COSTE}" \
         propietario="${PROPIETARIO}" \
  --output none

# ------------------------------------------------------------------
# 5. CUENTA DE ALMACENAMIENTO.
#    Comprobamos primero si existe, para que el script sea idempotente
#    y para no fallar si el nombre global ya está ocupado por otra persona.
# ------------------------------------------------------------------
echo "--> Cuenta de almacenamiento: ${CUENTA_ALM}"
if az storage account show --name "${CUENTA_ALM}" --resource-group "${GRUPO}" --output none 2>/dev/null; then
  echo "    Ya existe: no se vuelve a crear."
else
  # --sku Standard_LRS  : la opción más económica (redundancia local). Módulo 2.
  # --kind StorageV2    : tipo de cuenta actual, admite blobs, colas, tablas y archivos.
  # --min-tls-version   : fuerza TLS 1.2 como mínimo.
  # --allow-blob-public-access false : NADIE puede leer los PDF de forma anónima.
  # --https-only true   : rechaza el tráfico sin cifrar.
  az storage account create \
    --name "${CUENTA_ALM}" \
    --resource-group "${GRUPO}" \
    --location "${REGION}" \
    --sku Standard_LRS \
    --kind StorageV2 \
    --min-tls-version TLS1_2 \
    --https-only true \
    --allow-blob-public-access false \
    --tags entorno="${ENTORNO_TAG}" \
           proyecto="${PROYECTO}" \
           centro-coste="${CENTRO_COSTE}" \
           propietario="${PROPIETARIO}" \
    --output none
  echo "    Creada."
fi

# ------------------------------------------------------------------
# 6. CONTENEDOR DE BLOBS.
#    --auth-mode login usa TU identidad de Entra ID en lugar de la clave de la
#    cuenta: es la forma recomendada y no expone secretos en el script.
#    (Requiere tener un rol de datos, p. ej. "Colaborador de datos de Blob".)
# ------------------------------------------------------------------
echo "--> Contenedor: ${CONTENEDOR}"
az storage container create \
  --name "${CONTENEDOR}" \
  --account-name "${CUENTA_ALM}" \
  --auth-mode login \
  --public-access off \
  --output none

# ------------------------------------------------------------------
# 7. RESUMEN. Comprobación final legible de lo desplegado.
# ------------------------------------------------------------------
echo ""
echo "=== Despliegue completado ==="
az resource list \
  --resource-group "${GRUPO}" \
  --query "[].{Recurso:name, Tipo:type, Region:location}" \
  --output table

echo ""
echo "Para eliminar TODO lo creado y dejar de pagar, ejecuta:"
echo "  az group delete --name ${GRUPO} --yes --no-wait"

Cómo ejecutarlo

# Dar permisos de ejecución (solo la primera vez).
chmod +x desplegar-base-contoso.sh

# Desplegar el entorno de desarrollo.
./desplegar-base-contoso.sh dev

Qué hace que este script sea idempotente

Técnica usada Por qué importa
az group create sobre un grupo existente No falla: actualiza sus etiquetas
Comprobación previa con az storage account show Evita el error de "nombre ya en uso" al reejecutar
Nombres derivados de variables, no escritos a mano El mismo script sirve para dev y para pro
set -euo pipefail Detiene el script ante el primer fallo en lugar de dejar un despliegue a medias
--output none Salida limpia; solo se imprime lo que decidimos

Si te fijas, este script hace algo muy parecido a una plantilla de infraestructura como código, pero de forma imperativa (paso a paso). El siguiente nivel es hacerlo declarativo con Bicep, y eso es exactamente la lección 05-06.

  1. Limpieza: borrar sin dejar rastro de gasto

Este apartado es obligatorio, no opcional. El coste de lo que has creado en este módulo es de céntimos, pero el hábito vale dinero real cuando trabajes con máquinas virtuales y bases de datos.

# Borra el grupo y ABSOLUTAMENTE TODO lo que contiene. Es irreversible.
# --yes     : no pide confirmación (úsalo solo cuando estés seguro).
# --no-wait : devuelve el control inmediatamente; el borrado sigue en segundo plano.
az group delete --name rg-contoso-reservas-dev --yes --no-wait

Comprobaciones antes y después:

# Antes: ver exactamente qué se va a destruir.
az resource list --resource-group rg-contoso-reservas-dev --output table

# Después: confirmar que el grupo ya no existe (devuelve "false").
az group exists --name rg-contoso-reservas-dev

Y una comprobación de higiene que conviene hacer periódicamente en toda la suscripción:

# Lista TODOS los grupos de recursos con sus etiquetas: buscando huérfanos y olvidos.
az group list --query "[].{Grupo:name, Region:location, Entorno:tags.entorno, Propietario:tags.propietario}" --output table

# Busca discos que ya no están conectados a ninguna máquina virtual: gasto puro.
az disk list --query "[?diskState=='Unattached'].{Disco:name, Grupo:resourceGroup, GB:diskSizeGb}" --output table

# Busca direcciones IP públicas sin asociar: también se facturan.
az network public-ip list --query "[?ipConfiguration==null].{IP:name, Grupo:resourceGroup}" --output table

Esos tres últimos comandos son, literalmente, tres de las fuentes de gasto inútil más habituales en cuentas de Azure reales. Guárdalos.

Recuerda además: si creaste un bloqueo CanNotDelete en la lección anterior, el borrado fallará hasta que lo elimines con az lock delete. Está diseñado para que sea así.

Errores Comunes y Consejos

  • Ejecutar comandos en la suscripción equivocada. Empieza siempre con az account set y confirma con az account show. Es el error que más daño hace.
  • Trabajar con una CLI desactualizada. Si un parámetro "no existe", ejecuta az upgrade antes de buscar en foros.
  • Poner mayúsculas o guiones en el nombre de la cuenta de almacenamiento. Solo minúsculas y números, 3-24 caracteres, y único en todo Azure.
  • Usar --output json dentro de scripts. Para alimentar variables usa --output tsv: evita comillas y saltos de línea inesperados.
  • Escribir claves de acceso o contraseñas dentro del script. Usa --auth-mode login, identidades administradas (lección 04-02) o Key Vault (lección 04-03).
  • Olvidar que Cloud Shell se cierra a los 20 minutos de inactividad y que solo persiste $HOME. Guarda tu trabajo en ~/clouddrive o, mejor, en un repositorio Git.
  • Olvidar que el almacenamiento de Cloud Shell se factura. Es poco dinero, pero si dejas de usarlo, bórralo.
  • Escribir consultas --query a ciegas. Ejecuta primero el comando en JSON, mira la estructura y luego consulta.
  • Borrar con --yes sin mirar antes. Ejecuta az resource list --resource-group ... para ver qué desaparecerá.
  • Consejo final de coste: adopta la costumbre de terminar cada sesión de prácticas con az group delete. Un grupo de recursos por práctica, borrado al final: es la disciplina más rentable que puedes adquirir en este curso.

Ejercicios

Ejercicio 1: Consultas de gobierno

Escribe los comandos de Azure CLI que resuelvan estas peticiones de Marta Ríos:

  1. Mostrar en una tabla el nombre y la región de todos los grupos de recursos de la suscripción activa.
  2. Listar todos los recursos etiquetados con proyecto=contoso-reservas, mostrando nombre, tipo y grupo.
  3. Detectar los recursos que no tengan la etiqueta centro-coste.
  4. Guardar en una variable de shell llamada REGION_PRO la región del grupo rg-contoso-reservas-pro.

Ejercicio 2: Adaptar el script

Partiendo del script desplegar-base-contoso.sh del apartado 8:

  1. Añade una variable CRITICIDAD y aplícala como etiqueta al grupo y a la cuenta de almacenamiento, con valor alta si el entorno es pro y baja en caso contrario.
  2. Añade un paso que, solo si el entorno es pro, aplique un bloqueo CanNotDelete sobre el grupo.
  3. Explica por qué el paso 3 del script (az provider register) es seguro aunque se ejecute cada vez.

Ejercicio 3: Comparar herramientas y limpiar

  1. Escribe el equivalente en Azure PowerShell de estos dos comandos de la CLI:
az group create --name rg-contoso-millas-dev --location westeurope --tags entorno=desarrollo proyecto=contoso-millas
az group delete --name rg-contoso-millas-dev --yes
  1. Justifica en tres líneas qué herramienta elegirías para una tubería de CI/CD que se ejecuta en un contenedor Linux, y por qué.
  2. Escribe los comandos que auditan una suscripción buscando discos sin conectar y direcciones IP públicas sin asociar, y explica por qué esos dos recursos son fuentes clásicas de gasto inútil.

Soluciones

Solución 1:

# 1. Grupos de recursos con nombre y región.
az group list --query "[].{Grupo:name, Region:location}" --output table

# 2. Recursos del proyecto.
az resource list --tag proyecto=contoso-reservas \
  --query "[].{Recurso:name, Tipo:type, Grupo:resourceGroup}" --output table

# 3. Recursos SIN la etiqueta centro-coste.
az resource list \
  --query "[?tags.\"centro-coste\" == \`null\`].{Recurso:name, Tipo:type, Grupo:resourceGroup}" \
  --output table

# 4. Región del grupo de producción en una variable.
REGION_PRO=$(az group show --name rg-contoso-reservas-pro --query location --output tsv)
echo "$REGION_PRO"

Nota sobre el punto 3: como el nombre de la etiqueta lleva un guion, hay que entrecomillarlo dentro de la expresión JMESPath.

Solución 2:

  1. Variable y etiqueta de criticidad:
# Junto al resto de parámetros:
if [ "${ENTORNO}" = "pro" ]; then
  CRITICIDAD="alta"
else
  CRITICIDAD="baja"
fi

# Y se añade al bloque de --tags de az group create y de az storage account create:
#   criticidad="${CRITICIDAD}"
  1. Bloqueo condicional en producción:
if [ "${ENTORNO}" = "pro" ]; then
  echo "--> Aplicando bloqueo CanNotDelete al grupo de producción"
  # 'az lock create' falla si ya existe un bloqueo con ese nombre;
  # comprobamos primero para mantener la idempotencia del script.
  if ! az lock show --name "no-borrar-produccion" --resource-group "${GRUPO}" --output none 2>/dev/null; then
    az lock create \
      --name "no-borrar-produccion" \
      --lock-type CanNotDelete \
      --resource-group "${GRUPO}" \
      --notes "Plataforma de venta en produccion. Retirar solo con autorizacion." \
      --output none
  fi
fi
  1. az provider register es idempotente: si el proveedor ya está registrado, la operación no cambia nada y termina correctamente. Registrarlo no cuesta dinero ni tiene efectos secundarios, por lo que incluirlo garantiza que el script funcione también en una suscripción recién creada.

Solución 3:

  1. Equivalentes en Azure PowerShell:
New-AzResourceGroup -Name "rg-contoso-millas-dev" -Location "westeurope" -Tag @{
    entorno  = "desarrollo"
    proyecto = "contoso-millas"
}

Remove-AzResourceGroup -Name "rg-contoso-millas-dev" -Force
  1. Para una tubería en un contenedor Linux elegiría Azure CLI: la imagen es más ligera que instalar el módulo Az de PowerShell, la sintaxis encaja de forma natural con Bash, la mayoría de tareas de tubería son de una línea, y prácticamente todos los ejemplos de tuberías de Azure DevOps y GitHub Actions están escritos con az.

  2. Auditoría de gasto inútil:

az disk list --query "[?diskState=='Unattached'].{Disco:name, Grupo:resourceGroup, GB:diskSizeGb}" --output table
az network public-ip list --query "[?ipConfiguration==null].{IP:name, Grupo:resourceGroup}" --output table

Son fuentes clásicas de gasto porque se facturan por existir, no por usarse. Al borrar una máquina virtual, sus discos gestionados y su IP pública no se eliminan automáticamente si no se marcó la opción correspondiente: quedan huérfanos, invisibles en el día a día y cobrando cada mes. Una revisión mensual con estos dos comandos suele ser la optimización más rentable y más rápida de aplicar.

Conclusión

Con esta lección cierras el módulo 1 y ya tienes la caja de herramientas completa. Sabes por qué automatizar (repetible, auditable, escalable), has instalado Azure CLI y comprobado su versión, sabes iniciar sesión y —muy importante— cambiar y confirmar la suscripción activa antes de tocar nada. Conoces la anatomía de un comando az y su ayuda integrada (--help, az find), y manejas --output table para leer y --query con JMESPath para filtrar, renombrar campos, detectar recursos sin etiquetar y alimentar variables en scripts. Sabes qué aporta Azure PowerShell y con qué criterio elegir entre uno y otro, y cómo funciona Cloud Shell: sus dos intérpretes, la cuenta de almacenamiento que crea y factura, la persistencia limitada a $HOME y el cierre por inactividad. Y has escrito un script real, parametrizado, comentado e idempotente que crea el grupo rg-contoso-reservas-dev, la cuenta de almacenamiento de tarjetas de embarque y su contenedor privado, con las etiquetas obligatorias de Contoso Airlines, además de la rutina de limpieza que evita facturas sorpresa.

Recapitulando el módulo completo: has entendido qué es la nube y qué es Azure y conocido el caso de Contoso Airlines; has distinguido IaaS, PaaS, SaaS y serverless, el modelo de responsabilidad compartida y la geografía de Azure, fijando West Europe como región principal y North Europe como pareja; has creado y protegido tu cuenta con MFA y un presupuesto con alertas; te mueves con soltura por el portal; dominas Azure Resource Manager, la jerarquía de suscripción, grupo de recursos y recurso, los bloqueos y el esquema de etiquetado de Contoso; y ahora automatizas desde la línea de comandos.

Tienes los cimientos: la cuenta, el gobierno, la nomenclatura, los grupos de recursos y la herramienta para desplegar. Lo que falta es la plataforma en sí. En el módulo 2, Servicios principales de Azure, empezamos a construirla de verdad: desplegaremos las máquinas virtuales del motor de disponibilidad heredado, aprenderemos a escalar y dar alta disponibilidad al cómputo, publicaremos Contoso Reservas y la API de Disponibilidad en App Service, guardaremos las tarjetas de embarque en Azure Storage con la redundancia adecuada, y construiremos la red virtual con sus subredes y grupos de seguridad, además de la conectividad híbrida con las oficinas de Barcelona y Palma. Nos vemos allí.

Curso de Azure

Módulo 1: Introducción a Azure

Módulo 2: Servicios principales de Azure

Módulo 3: Bases de datos de Azure

Módulo 4: Seguridad en Azure

Módulo 5: Azure DevOps

Módulo 6: Servicios avanzados de Azure

Módulo 7: Monitoreo y gestión

Módulo 8: Gestión y optimización de costos

Módulo 9: Estudios de caso y mejores prácticas

© Copyright 2026. Todos los derechos reservados