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
- El problema concreto: infraestructura que solo existe en un historial
- Qué es la infraestructura como código
- Declarativo frente a imperativo, e idempotencia
- El estado: la idea que hace posible todo lo demás
- Deployment Manager: la herramienta nativa de GCP
- Plantillas en Jinja y en Python
- Un ejemplo real: la VPC y el firewall de AlpinaShop
- El aviso central: Deployment Manager está descontinuado
- La sucesión: Infrastructure Manager, Config Connector y Terraform
- Cómo se migra de Deployment Manager a Terraform, paso a paso
- Los conceptos que se conservan entre herramientas
- 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.
- 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.
- 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/22Declarativo es describir el resultado deseado, y dejar que la herramienta calcule los pasos:
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.
- 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.
- 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: trueLa 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-devDos advertencias operativas que conviene tener presentes:
--previewes equivalente alplande Terraform y no debe saltarse nunca en un entorno serio. Muestra qué se creará, se modificará o se destruirá antes de tocar nada.deployments deleteborra 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-devABANDON 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.
- 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/24Plantilla 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.
- 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-prodY 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.
- 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:
- 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
.yamly ejecutardeployments describees una habilidad práctica. - Los conceptos son transferibles. Declarativo, dependencias, plantillas, salidas, previsualización, estado: son los mismos en cualquier herramienta. Aprenderlos aquí sirve para 06-07.
- 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.
- 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=prodLo 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: REGIONALAplicas 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 |
- 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-prodUna 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.txtEl 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/redGenera 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-saludY 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:
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.
- 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:
- Describe el resultado, no los pasos. La herramienta calcula el camino.
- Deja que las dependencias se infieran de las referencias. Declarar el orden a mano es fuente de errores;
depends_ones el último recurso. - Previsualiza siempre antes de aplicar.
--previewoplan, sin excepciones en producción. - 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-legacyEl 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 planpegado 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
- ¿Qué es Google Cloud Platform?
- Configuración de tu cuenta de GCP
- Descripción general de la consola de GCP
- Proyectos, jerarquía de recursos y facturación
- Regiones, zonas y modelo de responsabilidad compartida
- Cloud Shell y la CLI de gcloud
Módulo 2: Servicios principales de GCP
- Compute Engine: máquinas virtuales en Google Cloud
- Cloud Storage: almacenamiento de objetos
- Cloud SQL: bases de datos relacionales gestionadas
- App Engine: plataforma como servicio
- Google Kubernetes Engine (GKE)
- Bases de datos NoSQL: Firestore, Bigtable y Spanner
- Cómo elegir el servicio de cómputo adecuado
Módulo 3: Redes y seguridad
- Redes VPC
- Balanceo de carga en la nube
- Cloud CDN
- Gestión de identidad y acceso (IAM)
- Cloud Armor
- Secretos y cifrado: Secret Manager y Cloud KMS
- Cloud DNS, certificados TLS y publicación segura de servicios
Módulo 4: Datos y análisis
- BigQuery: el almacén de datos analítico
- Cloud Dataflow: procesamiento de datos por lotes y en streaming
- Cloud Dataproc: Spark y Hadoop gestionados
- Cloud Pub/Sub: mensajería asíncrona
- Cloud Data Fusion: integración de datos sin código
- Orquestación de pipelines con Cloud Composer y Workflows
- Gobierno del dato y cuadros de mando con Dataplex y Looker Studio
Módulo 5: Aprendizaje automático e IA
- Vertex AI: la plataforma de machine learning de GCP
- AutoML: modelos a medida sin escribir código
- TensorFlow en GCP: entrenamiento y servicio de modelos
- API de lenguaje natural
- API de visión
- IA generativa en Vertex AI: modelos Gemini y embeddings
- MLOps: del modelo al producto con Vertex AI Pipelines
Módulo 6: DevOps y monitoreo
- Cloud Build: integración continua en GCP
- Cloud Source Repositories y gestión del código fuente
- Cloud Functions: funciones sin servidor
- Cloud Monitoring (antes Stackdriver): métricas, paneles y alertas
- Cloud Deployment Manager e infraestructura como código nativa
- Cloud Logging y Cloud Trace: logs, trazas y diagnóstico
- Terraform en GCP: infraestructura como código en la práctica
Módulo 7: Temas avanzados de GCP
- Híbrido y multinube con Anthos
- Computación sin servidor con Cloud Run
- Redes avanzadas: VPC compartida, peering y conectividad híbrida
- Mejores prácticas de seguridad
- Gestión y optimización de costos
- Fiabilidad: SLO, alta disponibilidad y recuperación ante desastres
- Gobierno a escala: organización, políticas y auditoría
