Hay una pregunta que Marta lleva evitando desde el módulo 3, y que un auditor, un cliente corporativo o un incidente grave harán tarde o temprano: si mañana hubiera que reconstruir toda la infraestructura de AlpinaShop en otra región, ¿cuánto se tardaría?

La respuesta honesta es que nadie lo sabe. La VPC alpinashop-vpc, las subredes sn-web-euw1 y sn-datos-euw1, el Cloud NAT, las reglas fw-*, el balanceador global con su comprobación de estado y su mapa de URL, la política de Cloud Armor, la zona de Cloud DNS, el certificado, el MIG, el clúster de GKE, las cuentas de servicio, los roles personalizados: todo eso se creó con comandos de gcloud que Marta ejecutó a lo largo de varias semanas. Algunos salieron a la primera. Otros se ajustaron después por consola. Unos cuantos se borraron y se rehicieron. El único registro de ese proceso es el historial de su terminal, que además está incompleto porque incluye comandos que se deshicieron y omite los cambios hechos con el ratón.

Y hay una segunda consecuencia, más insidiosa. alpinashop-dev se creó a partir de alpinashop-prod, pero fue divergiendo: una regla de firewall que se relajó para hacer una prueba, un tamaño de máquina distinto, una configuración de Cloud SQL que nunca se replicó. Hoy probar en desarrollo no garantiza nada sobre producción, porque no son el mismo sistema. Eso se llama deriva de configuración y es la razón por la que despliegues probados fallan en producción.

Esta lección ataca ese problema. Y lo hace con una advertencia que hay que dar pronto: la herramienta que da título a la lección no es la que debes usar. Merece la pena entender por qué, y sobre todo qué hacer cuando te la encuentres.

Contenido

  1. El problema concreto: infraestructura que solo existe en un historial
  2. Qué es la infraestructura como código
  3. Declarativo frente a imperativo, e idempotencia
  4. El estado: la idea que hace posible todo lo demás
  5. Deployment Manager: la herramienta nativa de GCP
  6. Plantillas en Jinja y en Python
  7. Un ejemplo real: la VPC y el firewall de AlpinaShop
  8. El aviso central: Deployment Manager está descontinuado
  9. La sucesión: Infrastructure Manager, Config Connector y Terraform
  10. Cómo se migra de Deployment Manager a Terraform, paso a paso
  11. Los conceptos que se conservan entre herramientas

  1. El problema concreto: infraestructura que solo existe en un historial

Vale la pena poner números a lo que cuesta la situación actual, porque el argumento a favor de la infraestructura como código no es estético:

Situación real Consecuencia hoy Con IaC
Hay que recrear el entorno en europe-west4 Semanas, con errores garantizados Cambiar una variable y aplicar
Alguien pregunta qué reglas de firewall hay Se listan por consola, sin saber por qué existen Se lee el fichero, con sus comentarios
Se quiere revisar un cambio antes de aplicarlo Imposible: se aplica y se ve qué pasa Pull request con el plan adjunto
alpinashop-dev no se parece a producción Nadie sabe en qué difieren El mismo código, distintas variables
Marta se va de vacaciones Nadie toca nada Cualquiera puede leer y proponer cambios
Un cambio rompe producción ¿Qué había antes? Nadie lo sabe git revert y aplicar
Auditoría: quién cambió el firewall y cuándo Logs de auditoría, sin contexto ni motivo Commit con autor, fecha y justificación

La fila del incidente merece énfasis. Cuando un cambio manual rompe producción, la pregunta urgente es "¿cuál era la configuración anterior?". Sin IaC, la única fuente es la memoria de quien lo cambió, bajo presión y con prisa. Con IaC, la respuesta está en Git y volver atrás es una operación conocida.

Y la deriva entre entornos es la que más caro sale a medio plazo, porque erosiona el valor de las pruebas. Todo el trabajo de 06-01 —pruebas automáticas, despliegue en alpinashop-dev, promoción a producción— descansa en la premisa de que desarrollo se parece a producción. Si no se parece, el pipeline da una confianza que no está justificada.

  1. Qué es la infraestructura como código

Infraestructura como código (IaC) es la práctica de definir la infraestructura en ficheros de texto versionados, y de crearla y modificarla exclusivamente aplicando esos ficheros con una herramienta, nunca a mano.

Esa última parte es la que cuesta y la que da todo el valor. Tener ficheros de Terraform y seguir tocando cosas por consola es lo peor de ambos mundos: la complejidad de la herramienta sin la garantía de que el código refleje la realidad.

Beneficio Qué significa en la práctica
Reproducibilidad El mismo código produce la misma infraestructura, siempre
Revisión Un cambio de firewall se discute en un pull request antes de existir
Historia git log sobre la infraestructura, con autor y motivo
Reversión Volver a la configuración de ayer es un comando
Documentación viva El código es la documentación, y no se queda obsoleta
Entornos coherentes Desarrollo y producción comparten definición
Automatización El pipeline de 06-01 puede aplicar cambios de infraestructura
Recuperación ante desastres Reconstruir es aplicar, no improvisar

La comparación directa con el trabajo por consola:

Aspecto Consola / gcloud a mano Infraestructura como código
Velocidad la primera vez Más rápida Más lenta: hay que escribir
Velocidad la décima vez Igual de lenta cada vez Instantánea
Errores humanos Frecuentes y silenciosos Detectados en la revisión
Conocimiento En la cabeza de una persona En el repositorio
Auditoría Logs sin contexto Commits con motivo
Curva de aprendizaje Ninguna Real

Y conviene ser honesto con el coste: IaC es más lento al principio. Crear una regla de firewall por consola son treinta segundos; escribirla, revisarla y aplicarla son diez minutos. La inversión se recupera la tercera o cuarta vez que hay que tocar esa regla, y se multiplica cuando hay que replicar el entorno o cuando alguien pregunta por qué existe.

  1. Declarativo frente a imperativo, e idempotencia

La diferencia entre los dos enfoques es la que hace que IaC funcione.

Imperativo es describir los pasos. Es lo que hace Marta hoy:

gcloud compute networks create alpinashop-vpc --subnet-mode=custom
gcloud compute networks subnets create sn-web-euw1 \
  --network=alpinashop-vpc --range=10.10.0.0/24 --region=europe-west1
gcloud compute firewall-rules create fw-permitir-salud \
  --network=alpinashop-vpc --allow=tcp:8080 \
  --source-ranges=35.191.0.0/16,130.211.0.0/22

Declarativo es describir el resultado deseado, y dejar que la herramienta calcule los pasos:

resources:
  - name: alpinashop-vpc
    type: compute.v1.network
    properties:
      autoCreateSubnetworks: false

La diferencia práctica está en qué pasa al ejecutarlo dos veces:

Imperativo Declarativo
Primera ejecución Crea los recursos Crea los recursos
Segunda ejecución Falla: "ya existe" No hace nada: ya está como debe
Si se modificó a mano No se entera Detecta la diferencia y la corrige
Si se elimina algo del fichero No se entera Lo borra

Esa propiedad —que aplicar N veces produce el mismo resultado que aplicar una— es la idempotencia, y es lo que permite ejecutar el proceso con confianza. Puedes aplicar la configuración cada mañana: si nada ha cambiado, no pasa nada; si alguien tocó algo a mano, se corrige.

La tercera fila esconde el beneficio más valioso: el declarativo detecta y corrige la deriva. Si alguien abre el puerto 22 a todo internet en una prueba y se olvida de cerrarlo, la próxima aplicación de la configuración lo detecta y lo revierte. La infraestructura converge hacia lo que dice el código.

Y la cuarta fila esconde el peligro: si borras un recurso del fichero, la herramienta lo destruye. No lo ignora: lo elimina, porque el fichero declara el estado deseado completo. Borrar por descuido treinta líneas de un .yaml puede significar borrar una base de datos.

  1. El estado: la idea que hace posible todo lo demás

Para que una herramienta declarativa sepa que debe modificar un recurso en lugar de crearlo, necesita saber qué recursos gestiona. Ese registro es el estado.

flowchart LR
    A[Código:<br/>estado DESEADO] --> C{Comparación}
    B[Estado:<br/>lo que la herramienta<br/>GESTIONA] --> C
    D[Realidad en GCP:<br/>lo que EXISTE] --> C
    C --> E[Plan de cambios:<br/>crear, modificar, destruir]

El estado responde a tres preguntas que la herramienta no puede contestar de otro modo:

  • ¿Qué recursos gestiono? Distingue lo que creó ella de lo que existía ya. Sin esa distinción, aplicar una configuración podría destruir recursos creados por otro equipo.
  • ¿Qué identificador tiene cada uno? El nombre lógico del fichero (red_principal) no es el identificador real en GCP.
  • ¿Cómo estaban la última vez? Para calcular la diferencia sin consultar toda la API en cada ejecución.

La gran diferencia entre las dos herramientas de esta lección está precisamente aquí:

Herramienta Dónde vive el estado Consecuencia
Deployment Manager Gestionado por Google, en el propio servicio No hay que gestionarlo, pero no se ve ni se manipula
Terraform Un fichero que tú gestionas (.tfstate) Total control, y total responsabilidad

Deployment Manager te ahorra el problema del estado. Terraform te lo entrega, con sus consecuencias: hay que guardarlo en un sitio compartido, con bloqueo para que dos personas no apliquen a la vez, con versionado por si se corrompe, y nunca en Git, porque contiene valores sensibles. Todo eso se resuelve en 06-07.

  1. Deployment Manager: la herramienta nativa de GCP

Cloud Deployment Manager es la herramienta de infraestructura como código nativa de Google Cloud, disponible desde 2015. Su modelo:

  • Una configuración en YAML declara recursos.
  • Cada recurso tiene un tipo derivado directamente de las APIs de GCP (compute.v1.network, sqladmin.v1beta4.instance).
  • Un despliegue (deployment) es un conjunto de recursos gestionados como una unidad, con su estado guardado por el servicio.
  • Las plantillas, en Jinja o Python, permiten parametrizar y reutilizar.

La configuración mínima:

# red.yaml
resources:
  - name: alpinashop-vpc
    type: compute.v1.network
    properties:
      autoCreateSubnetworks: false
      description: "VPC principal de AlpinaShop"

  - name: sn-web-euw1
    type: compute.v1.subnetwork
    properties:
      # $(ref....) crea una DEPENDENCIA IMPLÍCITA: la subred espera a la red
      network: $(ref.alpinashop-vpc.selfLink)
      region: europe-west1
      ipCidrRange: 10.10.0.0/24
      privateIpGoogleAccess: true

La sintaxis $(ref.recurso.propiedad) es el mecanismo central: además de obtener un valor que no se conoce hasta que el recurso existe, declara una dependencia. Deployment Manager construye un grafo y crea los recursos en el orden correcto, paralelizando lo que puede. Es exactamente la misma idea que en Terraform, con otra sintaxis.

Los comandos del ciclo de vida:

# Previsualizar: calcula qué haría, sin tocar nada
gcloud deployment-manager deployments create red-alpinashop \
  --config=red.yaml --preview --project=alpinashop-dev

# Ver el plan calculado
gcloud deployment-manager deployments describe red-alpinashop \
  --project=alpinashop-dev

# Confirmar y ejecutar
gcloud deployment-manager deployments update red-alpinashop \
  --project=alpinashop-dev

# Actualizar tras cambiar el fichero
gcloud deployment-manager deployments update red-alpinashop \
  --config=red.yaml --project=alpinashop-dev

# Ver los recursos gestionados
gcloud deployment-manager resources list \
  --deployment=red-alpinashop --project=alpinashop-dev

# Destruir TODO el despliegue
gcloud deployment-manager deployments delete red-alpinashop \
  --project=alpinashop-dev

Dos advertencias operativas que conviene tener presentes:

  • --preview es equivalente al plan de Terraform y no debe saltarse nunca en un entorno serio. Muestra qué se creará, se modificará o se destruirá antes de tocar nada.
  • deployments delete borra todos los recursos del despliegue. No es "olvidar la configuración": es destruir la VPC, las subredes y todo lo que contenga. Ejecutado sobre el despliegue equivocado en producción, es catastrófico.

También existen las políticas de actualización, que controlan qué hacer cuando un cambio exige recrear un recurso:

gcloud deployment-manager deployments update red-alpinashop \
  --config=red.yaml \
  --delete-policy=ABANDON \
  --create-policy=CREATE_OR_ACQUIRE \
  --project=alpinashop-dev

ABANDON deja de gestionar el recurso en lugar de borrarlo —útil para sacar algo del despliegue sin destruirlo— y CREATE_OR_ACQUIRE adopta un recurso que ya existe con ese nombre en lugar de fallar. Este último es el mecanismo de importación de Deployment Manager, y es mucho más limitado que el de Terraform.

  1. Plantillas en Jinja y en Python

Una configuración plana no escala: si hay que crear cinco reglas de firewall casi idénticas, copiar y pegar es exactamente el problema que IaC viene a resolver. Las plantillas parametrizan.

Plantilla en Jinja, para lo sencillo:

{# subred.jinja #}
resources:
  - name: {{ properties["nombre"] }}
    type: compute.v1.subnetwork
    properties:
      network: {{ properties["redSelfLink"] }}
      region: {{ properties["region"] }}
      ipCidrRange: {{ properties["rango"] }}
      privateIpGoogleAccess: {{ properties.get("accesoPrivadoGoogle", true) }}

outputs:
  - name: selfLink
    value: $(ref.{{ properties["nombre"] }}.selfLink)

Y su uso:

# red.yaml
imports:
  - path: subred.jinja

resources:
  - name: alpinashop-vpc
    type: compute.v1.network
    properties:
      autoCreateSubnetworks: false

  - name: subred-web
    type: subred.jinja
    properties:
      nombre: sn-web-euw1
      redSelfLink: $(ref.alpinashop-vpc.selfLink)
      region: europe-west1
      rango: 10.10.0.0/24

  - name: subred-datos
    type: subred.jinja
    properties:
      nombre: sn-datos-euw1
      redSelfLink: $(ref.alpinashop-vpc.selfLink)
      region: europe-west1
      rango: 10.20.0.0/24

Plantilla en Python, cuando hace falta lógica de verdad. Una plantilla Python es una función GenerateConfig(context) que devuelve un diccionario con los recursos:

# firewall.py
"""Genera las reglas de firewall de AlpinaShop a partir de una lista."""

REGLAS_BASE = [
    {"nombre": "fw-permitir-salud", "puertos": ["tcp:8080"],
     "origenes": ["35.191.0.0/16", "130.211.0.0/22"],  # sondas de Google (03-02)
     "etiquetas": ["catalogo-web"]},
    {"nombre": "fw-permitir-web-interno", "puertos": ["tcp:8080"],
     "origenes": ["10.10.0.0/24"], "etiquetas": ["catalogo-web"]},
    {"nombre": "fw-permitir-sql", "puertos": ["tcp:5432"],
     "origenes": ["10.10.0.0/24"], "etiquetas": ["base-datos"]},
]


def GenerateConfig(context):
    red = context.properties["redSelfLink"]
    entorno = context.properties["entorno"]
    recursos = []

    for regla in REGLAS_BASE:
        recursos.append({
            "name": f"{regla['nombre']}-{entorno}",
            "type": "compute.v1.firewall",
            "properties": {
                "network": red,
                "sourceRanges": regla["origenes"],
                "targetTags": regla["etiquetas"],
                "allowed": [
                    {"IPProtocol": p.split(":")[0], "ports": [p.split(":")[1]]}
                    for p in regla["puertos"]
                ],
                "description": f"Generada por IaC - entorno {entorno}",
            },
        })

    # Regla EXCLUSIVA de desarrollo: SSH desde la VPN de la oficina.
    # En producción no existe, y esa es exactamente la ventaja de usar código.
    if entorno == "dev":
        recursos.append({
            "name": "fw-permitir-ssh-oficina-dev",
            "type": "compute.v1.firewall",
            "properties": {
                "network": red,
                "sourceRanges": [context.properties["cidrOficina"]],
                "allowed": [{"IPProtocol": "tcp", "ports": ["22"]}],
            },
        })

    return {"resources": recursos}

El bloque condicional del final ilustra el beneficio principal: la diferencia entre entornos deja de ser accidental y pasa a estar escrita. Hoy nadie sabe en qué difiere alpinashop-dev de alpinashop-prod; con esto, la diferencia son cinco líneas de código con un if explícito, revisadas en un pull request.

Deployment Manager admite además esquemas (.schema) que validan los parámetros de una plantilla —tipos, valores obligatorios, expresiones regulares— y producen errores claros antes de tocar nada. Es una buena idea que Terraform recoge después con la validación de variables.

  1. Un ejemplo real: la VPC y el firewall de AlpinaShop

Juntándolo todo, así quedaría la red de AlpinaShop en Deployment Manager:

# alpinashop-red.yaml
imports:
  - path: subred.jinja
  - path: firewall.py

resources:
  - name: alpinashop-vpc
    type: compute.v1.network
    properties:
      autoCreateSubnetworks: false
      routingConfig:
        routingMode: REGIONAL
      description: "VPC principal de AlpinaShop - gestionada por IaC"

  - name: subred-web
    type: subred.jinja
    properties:
      nombre: sn-web-euw1
      redSelfLink: $(ref.alpinashop-vpc.selfLink)
      region: europe-west1
      rango: 10.10.0.0/24

  - name: subred-datos
    type: subred.jinja
    properties:
      nombre: sn-datos-euw1
      redSelfLink: $(ref.alpinashop-vpc.selfLink)
      region: europe-west1
      rango: 10.20.0.0/24

  - name: reglas-firewall
    type: firewall.py
    properties:
      redSelfLink: $(ref.alpinashop-vpc.selfLink)
      entorno: prod
      cidrOficina: 192.0.2.0/24

  - name: alpinashop-router-euw1
    type: compute.v1.router
    properties:
      network: $(ref.alpinashop-vpc.selfLink)
      region: europe-west1

  - name: alpinashop-nat-euw1
    type: compute.v1.router
    properties:
      network: $(ref.alpinashop-vpc.selfLink)
      region: europe-west1
      nats:
        - name: alpinashop-nat-euw1
          natIpAllocateOption: AUTO_ONLY
          sourceSubnetworkIpRangesToNat: ALL_SUBNETWORKS_ALL_IP_RANGES

outputs:
  - name: redSelfLink
    value: $(ref.alpinashop-vpc.selfLink)
  - name: subredWeb
    value: $(ref.subred-web.selfLink)
gcloud deployment-manager deployments create alpinashop-red \
  --config=alpinashop-red.yaml --preview --project=alpinashop-prod

Y aquí está el punto de la lección: este fichero, revisado en un pull request y aplicado desde el pipeline, sustituye a quince comandos sueltos en el historial de Marta. La red pasa a ser un artefacto que se lee, se discute y se reproduce.

Pero antes de que nadie se ponga a escribirlo, hay que decir lo siguiente.

  1. El aviso central: Deployment Manager está descontinuado

Cloud Deployment Manager está descontinuado. Google anunció su retirada, no recibe funcionalidad nueva desde hace años y su fin de soporte está fijado. No debe usarse para ningún proyecto nuevo.

Todo lo del apartado anterior es correcto y funciona hoy. Y sería un error empezar a escribirlo.

Las cuatro razones por las que Deployment Manager no prosperó, que son instructivas por sí mismas:

Razón Detalle
Solo GCP Terraform gestiona GCP, AWS, Azure, GitHub, Cloudflare, Datadog… con un mismo lenguaje
Ecosistema inexistente Terraform tiene miles de módulos públicos reutilizables; Deployment Manager, casi ninguno
Cobertura de recursos incompleta Servicios nuevos tardaban en tener tipo, o nunca lo tenían
Adopción baja La comunidad eligió Terraform, y con ella los ejemplos, los libros y las ofertas de empleo

La tercera es la que más se sufría en la práctica: cuando aparecía un servicio nuevo, había que recurrir a los tipos genéricos basados en la API REST, con una experiencia bastante ingrata.

Entonces, ¿por qué está esta lección en el curso? Por tres motivos concretos:

  1. Te lo vas a encontrar. Hay mucha infraestructura desplegada con Deployment Manager en organizaciones que llevan años en GCP, incluidas plantillas del Marketplace. Saber leer un .yaml y ejecutar deployments describe es una habilidad práctica.
  2. Los conceptos son transferibles. Declarativo, dependencias, plantillas, salidas, previsualización, estado: son los mismos en cualquier herramienta. Aprenderlos aquí sirve para 06-07.
  3. Porque lo importante es saber migrar. Si te encuentras Deployment Manager, tu trabajo será sacarlo de ahí. Eso es el apartado 10, y es lo verdaderamente útil de esta lección.

Y hay una lección de fondo que trasciende a la herramienta: elegir tecnología no es solo elegir capacidades, es elegir un ecosistema. Deployment Manager era técnicamente razonable. Perdió porque la comunidad, los ejemplos, los módulos, los libros y los profesionales formados estaban en el otro lado. Al evaluar una herramienta, pregunta también cuánta gente la usa y qué pasa si el proveedor deja de invertir en ella.

  1. La sucesión: Infrastructure Manager, Config Connector y Terraform

Google no dejó un hueco: propuso sucesores, y cada uno responde a un perfil distinto.

Infrastructure Manager (a menudo abreviado Infra Manager) es el sucesor gestionado y oficial. Y lo interesante es qué ejecuta por dentro: Terraform. Google reconoció que Terraform había ganado y, en lugar de competir, montó un servicio gestionado que lo ejecuta por ti.

gcloud infra-manager deployments apply proyectos/alpinashop-prod/locations/europe-west1/deployments/red \
  --service-account=projects/alpinashop-prod/serviceAccounts/[email protected] \
  --local-source=./terraform/red \
  --input-values=entorno=prod

Lo que aporta sobre ejecutar Terraform tú mismo: gestiona el estado por ti —adiós al bucket y al bloqueo—, se autentica con una cuenta de servicio de GCP sin claves, integra con Cloud Build y con los logs de auditoría, y mantiene un historial de despliegues consultable. Lo que cuesta: solo GCP, y menos flexibilidad que ejecutar Terraform en tu propio pipeline.

Config Connector es una propuesta distinta: un complemento de GKE que permite gestionar recursos de GCP como objetos de Kubernetes.

apiVersion: compute.cnrm.cloud.google.com/v1beta1
kind: ComputeNetwork
metadata:
  name: alpinashop-vpc
  namespace: tienda
spec:
  autoCreateSubnetworks: false
  routingMode: REGIONAL

Aplicas eso con kubectl apply y el controlador crea la VPC de verdad. La ventaja es la reconciliación continua: el controlador vigila permanentemente y corrige la deriva sola, sin esperar a que alguien ejecute nada. Tiene sentido para equipos que ya viven en Kubernetes y usan GitOps; para AlpinaShop, que tiene un clúster pero no una cultura de GitOps, sería añadir una dependencia grande para resolver un problema que Terraform resuelve más simple.

Terraform es el estándar de facto, y por eso tiene su propia lección.

Herramienta Modelo Estado Multiproveedor ¿Para AlpinaShop?
Deployment Manager YAML + plantillas Gestionado No No: descontinuado
Infrastructure Manager Terraform gestionado Gestionado No Buena opción, se menciona en 06-07
Config Connector CRD de Kubernetes En el clúster No No: complejidad desproporcionada
Terraform HCL Tuyo Sí Sí: la elección

  1. Cómo se migra de Deployment Manager a Terraform, paso a paso

Este es el apartado útil de la lección, y sirve para dos escenarios a la vez: migrar desde Deployment Manager y —el caso real de AlpinaShop— traer a código una infraestructura creada a mano. El procedimiento es prácticamente el mismo, porque en ambos casos el objetivo es que Terraform tome el control de recursos que ya existen sin destruirlos ni recrearlos.

flowchart TD
    A[1. Inventariar<br/>qué existe realmente] --> B[2. Exportar la configuración<br/>bulk-export]
    B --> C[3. Revisar y limpiar<br/>el HCL generado]
    C --> D[4. Importar al estado<br/>terraform import]
    D --> E[5. Verificar:<br/>terraform plan VACÍO]
    E --> F{¿Plan vacío?}
    F -->|No| C
    F -->|Sí| G[6. Abandonar el despliegue<br/>de Deployment Manager]
    G --> H[7. Refactorizar<br/>con calma]

Paso 1: inventariar

No se puede importar lo que no se conoce. Y la fuente de verdad no es la memoria de nadie, ni un documento: es GCP.

# Si hay despliegues de Deployment Manager, listar sus recursos
gcloud deployment-manager deployments list --project=alpinashop-prod
gcloud deployment-manager resources list --deployment=alpinashop-red \
  --project=alpinashop-prod --format='table(name, type, id)'

# Y en cualquier caso, inventariar la realidad recurso por recurso
gcloud compute networks list --project=alpinashop-prod
gcloud compute networks subnets list --project=alpinashop-prod
gcloud compute firewall-rules list --project=alpinashop-prod
gcloud compute forwarding-rules list --project=alpinashop-prod
gcloud sql instances list --project=alpinashop-prod
gcloud storage buckets list --project=alpinashop-prod

Una alternativa mucho más completa es Cloud Asset Inventory, que enumera todo lo que existe en un proyecto de una sola vez:

gcloud asset search-all-resources \
  --scope=projects/alpinashop-prod \
  --format='table(assetType, displayName, name)' > inventario-prod.txt

El resultado de este paso es una lista escrita de qué hay. Y suele deparar sorpresas: recursos de pruebas que nadie borró, una IP estática reservada y sin usar que se está pagando, reglas de firewall duplicadas. Ese hallazgo ya justifica el ejercicio, incluso antes de escribir una línea de Terraform.

Paso 2: exportar la configuración actual

La herramienta que ahorra la mayor parte del trabajo:

# Exportar TODOS los recursos soportados del proyecto a ficheros .tf
gcloud beta resource-config bulk-export \
  --project=alpinashop-prod \
  --resource-format=terraform \
  --path=./terraform-exportado

# O acotado a un tipo concreto, que es más manejable
gcloud beta resource-config bulk-export \
  --project=alpinashop-prod \
  --resource-format=terraform \
  --resource-types=ComputeNetwork,ComputeSubnetwork,ComputeFirewall \
  --path=./terraform-exportado/red

Genera ficheros .tf con la configuración real de los recursos existentes. No es código listo para producción, y hay que asumirlo desde el principio: nombres feos y generados automáticamente, todos los valores literales sin variables, campos calculados por el servidor que no deberían estar, sin módulos y sin estructura. Es un punto de partida, y como punto de partida ahorra días.

También existe la exportación a formato Deployment Manager (--resource-format=krm para Config Connector), pero para nuestro destino queremos terraform.

Paso 3: revisar y limpiar

El trabajo manual, y no hay atajo. Lo que hay que hacer con lo exportado:

Tarea Por qué
Renombrar los recursos google_compute_network.tfer--alpinashop-vpc → red_principal
Quitar los campos calculados self_link, id, creation_timestamp, fingerprint: los pone GCP
Sustituir literales por referencias network = "https://..." → network = google_compute_network.red_principal.id
Extraer variables El proyecto, la región y los CIDR deben ser variables
Organizar en ficheros red.tf, firewall.tf, sql.tf, iam.tf
Añadir comentarios Por qué existe cada regla: eso no lo exporta ninguna herramienta
Fijar la versión del proveedor Reproducibilidad

La tercera fila es la más importante técnicamente. Un fichero exportado tiene valores literales que no expresan relaciones; sustituirlos por referencias es lo que hace que Terraform entienda el orden de creación y que el código sea reutilizable.

Y la sexta es la más importante en el plano humano. La exportación reproduce qué hay, nunca por qué. La regla fw-permitir-salud con los rangos 35.191.0.0/16 y 130.211.0.0/22 es incomprensible sin un comentario que diga que son las sondas de comprobación de estado de Google. Este momento de la migración es la única ocasión en que alguien va a mirar cada recurso uno por uno; es cuando hay que escribir esos comentarios, porque después no se hará.

Paso 4: importar al estado

Terraform tiene el código, pero su estado está vacío: cree que no gestiona nada. Si aplicaras ahora, intentaría crear todo de nuevo y fallaría con errores de "ya existe" —o peor, crearía duplicados—.

Importar asocia cada recurso real con su bloque de código. La forma clásica, recurso a recurso:

terraform import google_compute_network.red_principal \
  projects/alpinashop-prod/global/networks/alpinashop-vpc

terraform import google_compute_subnetwork.web \
  projects/alpinashop-prod/regions/europe-west1/subnetworks/sn-web-euw1

terraform import google_compute_firewall.permitir_salud \
  projects/alpinashop-prod/global/firewalls/fw-permitir-salud

Y la forma moderna, disponible desde Terraform 1.5 y claramente preferible, con bloques import declarativos:

# importaciones.tf — un fichero temporal que se borra al terminar
import {
  to = google_compute_network.red_principal
  id = "projects/alpinashop-prod/global/networks/alpinashop-vpc"
}

import {
  to = google_compute_subnetwork.web
  id = "projects/alpinashop-prod/regions/europe-west1/subnetworks/sn-web-euw1"
}

import {
  to = google_compute_firewall.permitir_salud
  id = "projects/alpinashop-prod/global/firewalls/fw-permitir-salud"
}

La ventaja de los bloques import es enorme para una migración grande: son código versionable y revisable, aparecen en terraform plan antes de ejecutarse, y no dependen de que alguien recuerde el orden de cincuenta comandos. Además, terraform plan -generate-config-out=generado.tf puede generar el HCL correspondiente a los recursos importados, lo que reduce todavía más el trabajo del paso 3.

El formato del identificador varía por tipo de recurso y está documentado al final de la página de cada recurso en la documentación del proveedor. Es el detalle más tedioso de todo el procedimiento.

Paso 5: verificar que el plan sale vacío

Este es el paso que valida toda la migración, y es el criterio de éxito:

terraform plan
No changes. Your infrastructure matches the configuration.

Un plan vacío significa que el código describe exactamente la realidad, que el estado la refleja y que Terraform ha tomado el control sin cambiar nada. Es el objetivo.

Si el plan no sale vacío, léelo con atención porque cada tipo de diferencia significa algo distinto:

Lo que muestra el plan Causa habitual Qué hacer
~ update de un campo trivial Un valor por defecto que no escribiste Añadirlo al código con el valor real
~ update de un campo calculado Copiaste self_link o fingerprint Quitarlo del código
-/+ replace Un campo inmutable no coincide PARAR: aplicarlo destruiría el recurso
+ create de algo que existe Falta importarlo Importarlo
- destroy de algo que existe Está en el estado pero no en el código Añadirlo al código

La tercera fila merece una advertencia en mayúsculas. Un -/+ replace sobre google_sql_database_instance destruiría la base de datos de pedidos de AlpinaShop. Nunca se aplica un plan de migración que contenga un reemplazo sin haber entendido exactamente por qué aparece. Casi siempre es un campo inmutable mal transcrito y se corrige en el código.

La recomendación operativa: haz toda la migración primero en alpinashop-dev. Si algo sale mal, se pierde un entorno de pruebas, no la tienda.

Paso 6: abandonar el despliegue antiguo

Cuando se venía de Deployment Manager, queda un detalle crítico. Ahora hay dos herramientas que creen gestionar los mismos recursos, y eso es peligroso: un deployments delete los borraría por debajo de Terraform.

# ABANDON deja de gestionarlos SIN BORRARLOS. Nunca uses delete aquí.
gcloud deployment-manager deployments delete alpinashop-red \
  --delete-policy=ABANDON \
  --project=alpinashop-prod

--delete-policy=ABANDON es la diferencia entre una migración limpia y un desastre. Sin ese parámetro, el comando destruye la infraestructura que acabas de importar.

Paso 7: refactorizar con calma

Solo ahora, con el plan vacío y el control en Terraform, empieza la mejora: extraer módulos, parametrizar entornos, añadir prevent_destroy a lo crítico, integrar en el pipeline. Y la regla de oro: cada cambio de refactorización debe terminar también con un plan vacío. Si al mover código a un módulo el plan propone destruir y recrear algo, has cambiado el significado, no la forma.

Un resumen del esfuerzo real, para que nadie se lleve una sorpresa:

Fase Esfuerzo Se puede automatizar
Inventariar Horas Bastante
Exportar Minutos Sí
Limpiar y comentar Días Poco
Importar Horas Sí, con bloques import
Verificar el plan vacío Días de iteración No
Refactorizar Continuo No

Es un trabajo tedioso. Y es de una sola vez: cuando está hecho, está hecho para siempre.

  1. Los conceptos que se conservan entre herramientas

Cierro con lo que hace que aprender Deployment Manager no sea tiempo perdido. Los conceptos son los mismos en todas las herramientas de IaC, y solo cambia la sintaxis:

Concepto Deployment Manager Terraform Config Connector
Unidad declarativa Recurso en YAML Bloque resource Objeto CRD
Referencia entre recursos $(ref.x.selfLink) google_x.y.id Ref de Kubernetes
Dependencia implícita Por la referencia Por la referencia Por la referencia
Dependencia explícita metadata.dependsOn depends_on Anotaciones
Parametrización Propiedades de plantilla variable Kustomize / Helm
Reutilización Plantillas Jinja/Python Módulos Charts
Valores de salida outputs output Estado del objeto
Previsualización --preview plan --dry-run
Estado Gestionado por Google Fichero tuyo En el clúster (etcd)
Validación .schema validation en variables Esquema del CRD
Entornos Despliegues distintos Workspaces o carpetas Namespaces

Los cuatro principios que valen en todas ellas:

  1. Describe el resultado, no los pasos. La herramienta calcula el camino.
  2. Deja que las dependencias se infieran de las referencias. Declarar el orden a mano es fuente de errores; depends_on es el último recurso.
  3. Previsualiza siempre antes de aplicar. --preview o plan, sin excepciones en producción.
  4. El código es la fuente de verdad. En cuanto alguien toca algo a mano, el sistema deja de ser fiable y vuelves al punto de partida.

Errores Comunes y Consejos

Empezar un proyecto nuevo con Deployment Manager. Está descontinuado. Todo lo de esta lección sirve para entender y migrar lo existente, no para escribir nada nuevo.

Ejecutar deployments delete sin --delete-policy=ABANDON durante una migración. Destruye la infraestructura en lugar de dejar de gestionarla. Es el error más caro posible en este procedimiento.

Aplicar un plan de migración con un -/+ replace sin entenderlo. Un reemplazo sobre Cloud SQL destruye la base de datos. Si el plan propone recrear algo, para y averigua qué campo inmutable no coincide.

Dar por bueno el código exportado por bulk-export. Es un punto de partida, no un resultado. Sin limpiar, tendrás campos calculados que provocan diferencias eternas y literales que impiden reutilizar el código.

No comentar por qué existe cada recurso durante la migración. Es la única vez que alguien mirará cada recurso individualmente. Si no se escribe el motivo entonces, se pierde para siempre.

Seguir tocando cosas a mano después de adoptar IaC. Es lo peor de ambos mundos. En cuanto el código y la realidad divergen, la confianza en la herramienta desaparece.

Migrar directamente en producción. Haz todo el procedimiento primero en alpinashop-dev. Los errores de una migración se pagan caros y allí no cuestan nada.

Borrar recursos del fichero pensando que "dejarán de gestionarse". En una herramienta declarativa, quitar un recurso del código significa destruirlo. Para dejar de gestionarlo sin borrarlo existe terraform state rm o la política ABANDON.

Importar sin verificar el plan vacío. Importar no valida que el código sea correcto: solo asocia un identificador. El plan vacío es la única prueba de que la migración está bien hecha.

Consejo final: empieza por lo menos peligroso. Migra primero las reglas de firewall y los buckets, después la red, y deja para el final Cloud SQL y todo lo que contenga datos. Cuando llegues a lo delicado tendrás el procedimiento dominado y sabrás leer un plan con soltura.

Ejercicios

Ejercicio 1: leer y traducir una configuración heredada

Te incorporas a un proyecto y encuentras un despliegue de Deployment Manager llamado plataforma-legacy con un fichero que define una VPC, dos subredes, una regla de firewall que permite tcp:22 desde 0.0.0.0/0 y una instancia de Cloud SQL. Describe qué comandos ejecutarías para entender qué gestiona ese despliegue, qué harías con la regla de firewall y en qué orden migrarías los cuatro tipos de recurso a Terraform, justificando el orden.

Ejercicio 2: diagnosticar un plan que no sale vacío

Durante la migración de AlpinaShop, tras importar la red y el firewall, terraform plan muestra: un ~ update sobre google_compute_network.red_principal que cambia description de "VPC principal" a ""; un ~ update sobre google_compute_subnetwork.web que cambia private_ip_google_access de true a false; y un -/+ replace sobre google_compute_subnetwork.datos porque ip_cidr_range pasaría de 10.20.0.0/24 a 10.20.0.0/22. Explica la causa de cada uno, cuál es más peligroso y cómo corregirlos.

Ejercicio 3: planificar la migración completa de AlpinaShop

Marta te pide un plan realista para llevar a Terraform toda la infraestructura de alpinashop-prod: VPC con dos subredes y Cloud NAT, unas ocho reglas de firewall, el balanceador global completo, la política de Cloud Armor, la zona de Cloud DNS con el certificado, el MIG con su plantilla, el clúster de GKE Autopilot, la instancia de Cloud SQL, tres buckets, cuatro cuentas de servicio con sus vinculaciones IAM y un rol personalizado. Propón un plan por fases con criterios de agrupación, indica qué recursos NO importarías y por qué, y define el criterio de finalización de cada fase.

Soluciones

Solución 1

Comandos para entender el despliegue:

# 1. Qué recursos gestiona, con sus tipos e identificadores
gcloud deployment-manager resources list --deployment=plataforma-legacy \
  --format='table(name, type, id, update.state)'

# 2. El manifiesto: la configuración expandida REAL que se aplicó
gcloud deployment-manager manifests list --deployment=plataforma-legacy
gcloud deployment-manager manifests describe MANIFEST_ID \
  --deployment=plataforma-legacy --format='value(expandedConfig)'

# 3. Quién y cuándo lo cambió por última vez
gcloud deployment-manager deployments describe plataforma-legacy

El manifiesto es la pieza clave y la que la gente no conoce: contiene la configuración expandida, es decir, con todas las plantillas resueltas y todos los valores por defecto rellenados. El .yaml original puede tener plantillas parametrizadas difíciles de leer; el manifiesto muestra exactamente qué se creó.

La regla de firewall tcp:22 desde 0.0.0.0/0 es un hallazgo de seguridad, y hay que tratarlo como tal. Deja SSH abierto a todo internet, contra todo lo establecido en 03-01 y 03-04.

Y la decisión correcta es migrarla tal cual está, y arreglarla después, aunque duela. Las razones:

  • Cambiarla durante la migración rompe el criterio del plan vacío, que es la única forma de verificar que la migración es correcta. Si mezclas migración y corrección, cuando algo falle no sabrás cuál de las dos cosas lo causó.
  • Puede que algo dependa de ella —un proceso operativo, un acceso de un proveedor—, y averiguarlo requiere investigación que no debe bloquear la migración.
  • Una vez en Terraform, corregirla es un pull request revisable, con historia y con vuelta atrás. Es decir, se corrige mejor después.

Lo que sí hay que hacer inmediatamente: documentar el hallazgo, comunicarlo y ponerle fecha. Y si la exposición se considera inaceptable a corto plazo, restringir el origen a los rangos de la oficina como medida provisional, antes de empezar la migración, para que la migración parta de un estado ya aceptable.

Orden de migración, de menos a más peligroso:

Orden Recurso Por qué ahí
1 Reglas de firewall Independientes, fáciles de importar, un error no destruye datos
2 VPC Base de todo; hay que tenerla antes que las subredes
3 Subredes Dependen de la VPC; ojo con ip_cidr_range, es inmutable
4 Cloud SQL La última siempre: contiene datos y un replace es irreversible

El criterio general: empieza por lo que se puede recrear sin consecuencias y termina por lo que contiene datos. Cuando llegues a Cloud SQL habrás importado varios recursos, sabrás leer un plan con soltura y reconocerás un -/+ replace al instante. Y para esa importación en concreto, añade lifecycle { prevent_destroy = true } antes de ejecutar terraform apply por primera vez: es una red de seguridad de dos líneas que puede salvar la base de datos.

Solución 2

Diferencia 1 — description pasaría de "VPC principal" a "".

Causa: el recurso real tiene descripción, pero el código HCL no incluye el atributo description. Terraform interpreta la ausencia como "debe estar vacío" e intenta borrarla.

Gravedad: baja. Cambiar una descripción no afecta al funcionamiento.

Corrección: añadir el atributo al código con el valor real.

resource "google_compute_network" "red_principal" {
  name        = "alpinashop-vpc"
  description = "VPC principal de AlpinaShop"   # faltaba
  auto_create_subnetworks = false
}

Lección: es el síntoma más común tras importar. Aparece siempre que un recurso tiene atributos opcionales configurados que el código no menciona.

Diferencia 2 — private_ip_google_access pasaría de true a false.

Causa: la misma —el atributo falta en el código— pero las consecuencias son completamente distintas. Ese ajuste es lo que permite que las instancias sin IP pública alcancen las APIs de Google. Aplicarlo dejaría a las VM de sn-web-euw1 sin acceso a Cloud Storage, a Secret Manager ni a Cloud Logging.

Gravedad: alta. Es una interrupción funcional real, y además difícil de diagnosticar: no se cae nada de golpe, simplemente empiezan a fallar llamadas a APIs de forma aparentemente aleatoria.

Corrección:

resource "google_compute_subnetwork" "web" {
  name                     = "sn-web-euw1"
  ip_cidr_range            = "10.10.0.0/24"
  region                   = "europe-west1"
  network                  = google_compute_network.red_principal.id
  private_ip_google_access = true      # CRÍTICO: faltaba
}

Lección importante: un ~ update no es automáticamente inofensivo. Dos diferencias sintácticamente idénticas —un atributo ausente— tienen impactos radicalmente distintos. Hay que leer qué campo cambia, no solo el símbolo que lo precede.

Diferencia 3 — -/+ replace de la subred de datos por cambio de CIDR.

Causa: el código dice /22 y la realidad es /24. Es un error de transcripción, muy probablemente al copiar del inventario o al escribir de memoria. Y ip_cidr_range no se puede modificar en caliente en una subred existente con recursos dentro, así que Terraform solo puede destruirla y crearla de nuevo.

Gravedad: crítica. Destruir sn-datos-euw1 implicaría desconectar todo lo que vive en ella —la instancia de Cloud SQL entre otras cosas— y la operación fallaría a mitad, dejando la infraestructura en un estado incoherente. Y si por algún motivo consiguiera completarse, las IP privadas cambiarían y todo lo que las referencia dejaría de funcionar.

Corrección: poner en el código el valor real, 10.20.0.0/24.

Cuál es más peligroso y por qué. El -/+ replace es el más peligroso, y no solo por el impacto: es el único que Terraform no puede deshacer. Los dos ~ update son reversibles —se corrige el código y se vuelve a aplicar—; un recurso destruido no vuelve. Por eso la regla del apartado 10 es categórica: un replace inesperado en una migración debe pararte siempre.

Y una recomendación de procedimiento que este ejercicio ilustra bien: en una migración, ejecuta terraform plan después de cada importación, no al final de todas. Con cincuenta recursos importados de golpe, el plan tiene cientos de líneas y las tres diferencias importantes se pierden entre el ruido. Importar de tres en tres y verificar es más lento y muchísimo más seguro.

Solución 3

Criterio de agrupación por fases: de menos a más peligroso, y respetando las dependencias.

Fase 0 — Preparación (medio día). Bucket alpinashop-terraform-estado con versionado, backend configurado, proveedor google con versión fijada, inventario completo con Cloud Asset Inventory y exportación con bulk-export. Repositorio alpinashop-infra de 06-02 con protección de ramas y CODEOWNERS exigiendo revisión de seguridad.

Criterio de finalización: terraform init funciona y el estado remoto está vacío pero accesible.

Fase 1 — Recursos independientes y sin datos (1-2 días). Buckets —salvo su contenido—, cuentas de servicio, rol personalizado analistaCatalogo y vinculaciones IAM.

Por qué primero: no dependen de nada, son fáciles de importar y un error no destruye nada irrecuperable. Es donde el equipo aprende a leer planes.

Criterio: plan vacío y una prueba de que las cuentas de servicio siguen funcionando.

Fase 2 — Red base (2-3 días). VPC, dos subredes, router, Cloud NAT alpinashop-nat-euw1 y las ocho reglas de firewall.

Por qué aquí: es la base de todo lo que viene después, y todavía no toca datos. Máxima atención a ip_cidr_range y a private_ip_google_access, por lo visto en el ejercicio 2.

Criterio: plan vacío y verificación funcional real: la tienda sigue respondiendo, las VM siguen saliendo por el NAT y siguen alcanzando las APIs de Google.

Fase 3 — Publicación (2-3 días). Balanceador completo —IP alpinashop-lb-ip, hc-catalogo, bs-catalogo-web, bb-catalogo-imagenes, alpinashop-url-map, proxy y regla de reenvío—, política de Cloud Armor pol-catalogo-web, zona de Cloud DNS alpinashop-publica y certificado alpinashop-cert.

Por qué juntas: están fuertemente acopladas y separarlas dejaría estados intermedios raros. Es la fase con más recursos interdependientes y donde el orden de importación importa más.

Criterio: plan vacío, la tienda responde por HTTPS con el certificado correcto y la CDN sigue sirviendo aciertos.

Fase 4 — Cómputo (2 días). Plantilla de instancia, MIG alpinashop-web-mig y clúster GKE Autopilot alpinashop-cluster.

Precaución específica: las plantillas de instancia son inmutables por diseño; cualquier diferencia produce un replace, y ahí un replace es aceptable siempre que el MIG haga una actualización progresiva y no un recreado simultáneo. Hay que verificarlo en el plan antes de aplicar.

Criterio: plan vacío y comprobación de que el MIG no ha recreado instancias inesperadamente.

Fase 5 — Datos (2 días, con máxima cautela). Instancia de Cloud SQL alpinashop-pedidos y su base de datos tienda.

Precauciones obligatorias: copia de seguridad verificada antes de empezar; lifecycle { prevent_destroy = true } escrito antes del primer apply; y aplicación en una ventana de mantenimiento con alguien mirando.

Criterio: plan vacío y la aplicación conectando con normalidad.

Recurso ¿Importar? Motivo
Contenido de los buckets No Terraform gestiona el contenedor, no los datos
Filas y esquema de Cloud SQL No Las migraciones de esquema son de la aplicación (06-01)
Valores de Secret Manager No El secreto sí, su valor jamás: acabaría en el estado
Datasets y tablas de BigQuery Depende El dataset sí; las tablas creadas por pipelines, no
Objetos de Kubernetes del namespace tienda No Van en alpinashop-catalogo con sus manifiestos
Instancias individuales del MIG No Las gestiona el MIG, no Terraform
Certificados gestionados por Google Sí, el recurso La renovación la hace Google

La regla que unifica la columna de exclusiones: Terraform gestiona la forma, no el contenido. El bucket sí, los objetos no. La base de datos sí, las filas no. El secreto sí, su valor nunca —porque cualquier valor que Terraform gestione acaba escrito en el fichero de estado, y eso convertiría el estado en un almacén de credenciales, contradiciendo todo lo de 03-06—.

Estimación total: entre dos y tres semanas de trabajo no continuo, compaginado con la operación normal. Y las tres recomendaciones finales:

  • Todo primero en alpinashop-dev. El coste de un error allí es cero, y la fase 5 en particular no debería tocarse en producción sin haberla ensayado.
  • Una fase por pull request, con el terraform plan pegado en la descripción. Es lo que convierte la migración en un trabajo revisable y no en una operación heroica de una persona.
  • Cada fase termina con plan vacío y verificación funcional, no solo con plan vacío. Un plan vacío dice que el código coincide con el estado; que la tienda funcione lo dice la tienda.

Conclusión

AlpinaShop tiene ahora un plan para dejar de depender del historial de un terminal.

Sabes cuál era el problema concreto y cuánto costaba: infraestructura irreproducible, cambios sin revisar, alpinashop-dev derivando de alpinashop-prod hasta que probar dejó de significar nada, y ninguna respuesta a la pregunta de qué configuración había antes del cambio que rompió algo.

Sabes qué es la infraestructura como código y por qué su valor no está en escribir ficheros sino en no volver a tocar nada a mano. Entiendes la diferencia entre declarativo e imperativo y su consecuencia práctica: la idempotencia, que permite aplicar la configuración cuantas veces quieras, y la detección de deriva, que corrige lo que alguien cambió por su cuenta. Con el peligro asociado: en una herramienta declarativa, borrar un recurso del fichero significa destruirlo.

Comprendes qué es el estado y por qué es imprescindible —saber qué se gestiona, con qué identificador y cómo estaba—, y la diferencia fundamental entre las dos herramientas: Deployment Manager lo gestiona por ti, Terraform te lo entrega con toda la responsabilidad que eso implica.

Conoces Deployment Manager de verdad: configuraciones YAML con tipos derivados de las APIs, $(ref....) creando dependencias implícitas, plantillas en Jinja para lo simple y en Python cuando hace falta lógica, esquemas de validación, el ciclo create --preview / update / delete, y las políticas ABANDON y CREATE_OR_ACQUIRE. Sabes escribir la red completa de AlpinaShop con ello.

Y sabes que no debes usarlo. Está descontinuado, perdió por ser solo de GCP, por no tener ecosistema, por la cobertura incompleta de recursos y porque la comunidad eligió otra cosa. Con la lección de fondo que va más allá de la herramienta: elegir tecnología es elegir un ecosistema, y una herramienta técnicamente correcta sin comunidad detrás es una apuesta perdida.

Conoces la sucesión: Infrastructure Manager, que es Terraform gestionado por Google —el reconocimiento explícito de quién ganó—; Config Connector, con su reconciliación continua, para quien ya vive en Kubernetes; y Terraform como estándar de facto.

Y tienes lo verdaderamente útil de esta lección: el procedimiento de migración completo, que sirve tanto para salir de Deployment Manager como para traer a código una infraestructura creada a mano. Inventariar con Cloud Asset Inventory, exportar con gcloud beta resource-config bulk-export, limpiar el HCL generado —quitando campos calculados, sustituyendo literales por referencias y, sobre todo, escribiendo por qué existe cada recurso, porque es la única vez que alguien los mirará uno a uno—, importar con terraform import o con los bloques import declarativos, y verificar que el plan sale vacío, que es el único criterio válido de éxito. Con la tabla para diagnosticar un plan que no sale vacío y la regla categórica: un -/+ replace inesperado debe pararte siempre. Y el --delete-policy=ABANDON que separa una migración limpia de un desastre.

Por último, tienes los conceptos que se conservan entre herramientas —recursos, referencias, dependencias, parametrización, reutilización, salidas, previsualización, estado— y los cuatro principios que valen en cualquiera de ellas: describe el resultado, deja que las dependencias se infieran, previsualiza siempre y trata el código como la única fuente de verdad.

Con esto, AlpinaShop sabe qué quiere hacer y cómo llegar hasta ahí. Le falta la herramienta con la que hacerlo.

Antes de eso, sin embargo, queda pendiente el resto de la observabilidad. Las métricas de 06-04 avisan de que algo pasa, pero no dicen qué. En 06-06 llegan Cloud Logging y Cloud Trace: los logs que responden qué ocurrió exactamente en cada caso, las trazas que dicen dónde se fue el tiempo, y el recorrido completo de un incidente de AlpinaShop desde la alerta hasta la línea de código. Después, en 06-07, Terraform cierra el módulo poniendo en práctica todo lo que aquí has aprendido a planificar.

Curso de Google Cloud Platform (GCP)

Módulo 1: Introducción a Google Cloud Platform

Módulo 2: Servicios principales de GCP

Módulo 3: Redes y seguridad

Módulo 4: Datos y análisis

Módulo 5: Aprendizaje automático e IA

Módulo 6: DevOps y monitoreo

Módulo 7: Temas avanzados de GCP

Módulo 8: Proyecto final

© Copyright 2026. Todos los derechos reservados