Módulo 3: Infrastructure As Code For An Ai Endpoint

1. Introducción: lo que Terraform sabe declarar sin necesitar invocar nada

Descripción

ADR-001-llm-as-escalation-path.md (Módulo 1) tiene una fila con el nombre de este módulo: "M3 — IaC for an AI endpoint | Declares only the escalation path's infrastructure (bedrock.tf); process-shipment-manifest is not touched". GENAI-COST-PROFILE.md (Módulo 2) cerró con la frase exacta que abre este módulo: "el Módulo 3 abre... declarar la infraestructura completa de la carga de IA como código real, validado y planeado de verdad, sin necesitar LocalStack ni una cuenta AWS para hacerlo — el módulo con más peso ejecutado de toda esta guía". Esta lección explica por qué esa frase no es una promesa de mercadeo: es una consecuencia directa de cómo funciona Terraform, verificable en cualquier máquina, hoy mismo, sin ningún servicio corriendo detrás.

Conexión con el módulo

Todo este módulo depende de un solo hecho técnico, y esta lección lo instala antes de tocar una sola línea de HCL nueva: terraform validate y terraform plan, sobre un recurso que todavía no existe en el state, nunca necesitan una llamada de red a la API real. Las lecciones 2 a 5 construyen sobre ese hecho —el panorama del provider, el módulo bedrock-guardrail, el rol de mínimo privilegio, el plan completo—. Las lecciones 6 y 7 marcan, con la misma precisión, exactamente dónde ese hecho deja de aplicar: el momento en que apply sí necesita hablar con algo real.


El mapa de este módulo: las 8 lecciones

   M3 -- INFRAESTRUCTURA COMO CÓDIGO DE UN ENDPOINT DE IA

   1  Introducción                          por qué validate/plan no llama a la API real (esta lección)
   2  Qué recursos existen para Bedrock      aws_bedrock_guardrail y dos más, esquema real citado
   3  Manos a la obra: bedrock-guardrail     el módulo nuevo, fmt + validate aislados
   4  Manos a la obra: el rol de mínimo priv. BedrockManifestExtractorRole, InvokeModel acotado
   5  Manos a la obra: el plan completo      bedrock.tf + el módulo, N to add real
   6  El límite exacto                       por qué apply no corre contra LocalStack Hobby
   7  Manos a la obra: lo que sí aplica      la frontera exacta, dentro del mismo archivo .tf
   8  Proyecto: la infraestructura declarada bedrock.tf + módulo, el ledger honesto
#LecciónQué se ejecuta/practica
1Introducción (esta)El mecanismo que hace posible el resto del módulo, explicado y citado
2Qué recursos existen para BedrockPanorama del provider, esquema real extraído de terraform providers schema -json
3Manos a la obra: modules/bedrock-guardrail/Ejecutado: módulo nuevo, terraform fmt + terraform validate aislados
4Manos a la obra: BedrockManifestExtractorRoleEjecutado: aws_iam_role + política acotada a un ARN de modelo, terraform validate real
5Manos a la obra: terraform plan completoEjecutado: bedrock.tf + el módulo, salida real con N to add
6El límite exactoRepresentativo: por qué apply no corre contra LocalStack Hobby, cita oficial
7Manos a la obra: lo que sí aplicaLa frontera exacta entre lo real y lo representativo, dentro del mismo .tf
8Proyecto: infraestructura declaradaEjecutado: el repositorio completo, con el ledger de honestidad de este módulo

Fíjate en la forma de esta tabla: cinco de ocho lecciones están marcadas Ejecutado, sin matiz — más que cualquier otro módulo de esta guía. Eso no es casualidad de diseño; es la consecuencia directa del hecho que esta lección explica ahora.


Analogía: revisar los planos, no poner un ladrillo

Antes de que una constructora ponga un solo ladrillo, un arquitecto revisa los planos de una casa: ¿las medidas cierran?, ¿la escalera cabe en el espacio asignado?, ¿la carga de la segunda planta está soportada por las columnas correctas? Esa revisión es exhaustiva, es real, y no necesita el terreno, no necesita los materiales, no necesita al equipo de construcción presente. Se hace sobre el papel, contra las reglas de la ingeniería estructural, y produce una lista completa y confiable de "esto va a funcionar" o "esto no cierra" — antes de que exista una sola pared física a la cual comparar.

terraform validate es exactamente esa revisión de planos: confirma que el HCL que escribiste es sintácticamente correcto y que cada argumento que le diste a cada recurso coincide con lo que el schema del provider espera —tipos correctos, campos obligatorios presentes, nada mal anidado—. terraform plan, un paso más, es como si el arquitecto además calculara, papel en mano, exactamente qué materiales habría que pedir y en qué orden colocarlos, sin haber llamado todavía al proveedor de materiales: para una casa que no existe todavía, todo lo que hace falta pedir se deduce del plano mismo, no de una visita al terreno. Nada de esto reemplaza la construcción real —eso es apply, la lección 6 marca exactamente dónde ese paso se detiene en este laboratorio—, pero confirma, con una certeza real y verificable, que el plano en sí no tiene errores.


El mecanismo exacto, explicado sin atajos

terraform validate y terraform plan hacen trabajos distintos, y vale la pena separarlos con precisión antes de seguir:

   QUÉ HACE CADA COMANDO, Y QUÉ NECESITA PARA HACERLO

   terraform validate
        │
        │  Lee el HCL. Lo compara contra el *schema* del provider ya
        │  instalado localmente (el mismo que la lección 2 extrae con
        │  `terraform providers schema -json`). Confirma tipos, campos
        │  obligatorios, bloques bien anidados.
        │
        │  NECESITA: el HCL en disco + el provider ya descargado
        │  (`terraform init`). NO necesita red hacia ningún endpoint
        │  de AWS, ni credenciales válidas, ni un `state` previo.
        ▼
   Success! -- o una lista de errores de sintaxis/tipo, sin red

   terraform plan
        │
        │  Compara el HCL declarado contra el `state` actual --
        │  el archivo (o backend) que registra qué recursos existen
        │  YA, y con qué atributos, según la última vez que Terraform
        │  los leyó o los creó.
        │
        │  Para un recurso NUEVO -- uno que el `state` no conoce
        │  todavía -- no hay nada que leer de la realidad: el diff
        │  completo ("esto se va a crear, con estos atributos") se
        │  construye 100% a partir del HCL que escribiste. Cero
        │  llamadas de red hacia el recurso en sí.
        │
        │  Para un recurso que YA EXISTE en el `state`, plan SÍ
        │  necesita refrescarlo contra la API real -- para detectar
        │  drift, un cambio hecho fuera de Terraform -- y ahí sí
        │  hace falta conectividad real.
        ▼
   Plan: N to add, 0 to change, 0 to destroy -- sin red, si N
   recursos son TODOS nuevos (el caso exacto de este módulo)

Este módulo vive enteramente en la mitad izquierda de ese segundo bloque: todo lo que bedrock.tf y modules/bedrock-guardrail/ declaran, en este momento de la guía, es nuevo — nada de eso existe todavía en ningún state, en ningún laboratorio, en ninguna cuenta. Por eso terraform plan sobre esta infraestructura puede correr, de verdad, sin LocalStack corriendo y sin una cuenta AWS real detrás — no porque Terraform "simule" algo, sino porque, literalmente, no hay nada que necesite leer de la realidad todavía.

La evidencia de que esto ya se comprobó, no se está prometiendo por primera vez aquí. El Módulo 2, lección 5 de esta misma guía ya corrió, de verdad, exactamente esta secuencia sobre el primer borrador de bedrock.tf:

terraform init -input=false
terraform validate
terraform plan -out=tfplan -input=false

Qué esperar (literal — ya ejecutado y documentado en Módulo 2, lección 5 de esta guía):

Terraform has been successfully initialized!
Success! The configuration is valid.
Plan: 15 to add, 0 to change, 0 to destroy.

Quince recursos —todo andes-cargo-infra/ hasta ese punto, incluido el primer aws_bedrock_guardrail en borrador—, planeados de punta a punta, sin que ningún puerto 4566 estuviera escuchando. Este módulo repite exactamente esa secuencia (lección 5), ahora con la infraestructura completa —el módulo y el rol IAM que las lecciones 3 y 4 construyen—, y con un número de recursos más alto.


El hueco que esto NO llena, dicho por adelantado

El propio Módulo 1, lección 7 de esta guía ya anticipó, en su sección de errores comunes, la pregunta que esta lección podría generar sin querer: "si Terraform puede validate/plan un recurso Bedrock sin LocalStack ni cuenta AWS, ¿por qué awslocal bedrock list-foundation-models no pudo hacer lo mismo?" La respuesta, ya dada ahí y repetida aquí porque es la base de todo este módulo: son operaciones fundamentalmente distintas. terraform plan sobre un recurso que no existe todavía nunca necesita leer nada real. awslocal bedrock list-foundation-models es lo opuesto — una llamada que necesita una API real, corriendo, respondiendo, con un plan de licencia que la incluya. Este módulo entero vive del primer tipo de operación. La lección 6 confirma, con la misma honestidad, que apply —crear el recurso de verdad— pertenece al segundo tipo, y por eso se detiene exactamente donde el Módulo 1 ya predijo que se detendría.


Errores comunes

Pensar que terraform plan "simula" la infraestructura, como si fuera una especie de LocalStack interno (de confusión de mecanismo). Qué pasa: alguien concluye que Terraform tiene su propia emulación de AWS incorporada, y que por eso el plan "funciona sin LocalStack". Cómo detectarlo: si tu explicación de por qué plan corre sin red menciona alguna forma de simulación de AWS por parte de Terraform. Cómo corregirlo: Terraform no simula nada — el plan de un recurso nuevo es, literalmente, la traducción directa del HCL que escribiste a "esto es lo que voy a pedirle a la API cuando corras apply", sin haber hecho esa petición todavía. No hay AWS falso corriendo en ningún lado durante plan; hay, simplemente, nada que necesite consultarse todavía.

Asumir que esto aplica a CUALQUIER terraform plan, no solo a recursos nuevos (de sobre-generalización). Qué pasa: alguien concluye que, como el plan de este módulo corrió sin red, entonces ningún plan de Terraform necesita nunca conectividad real. Cómo detectarlo: si tu regla mental es "terraform plan nunca necesita red", sin la condición "para un recurso nuevo". Cómo corregirlo: un recurso que ya existe en el state sí necesita refrescarse contra la API real durante plan —para detectar si alguien lo cambió manualmente fuera de Terraform, el problema de drift que terraform-and-iac-guide ya cubrió—. El caso de este módulo es el caso favorable, no el caso general: todo lo nuevo aquí es, precisamente, nuevo.

Esperar que esta lección, por sí sola, ya resuelva si apply corre o no en este laboratorio (de expectativa hacia adelante). Qué pasa: alguien termina esta lección y da por hecho que, como validate/plan corren sin red, apply también va a correr igual de limpio. Cómo detectarlo: si tu plan es saltarte directo a un terraform apply sobre bedrock.tf antes de la lección 6. Cómo corregirlo: apply es una operación completamente distinta —sí necesita hablar con algo real, sea LocalStack o AWS—, y esta guía ya confirmó, en el Módulo 1, lección 7, que Bedrock específicamente no está disponible en el plan gratuito de LocalStack. La lección 6 de este módulo documenta ese límite exacto, con la misma honestidad que el resto de la guía.


Ejercicios

Ejercicio 1 — Explica, en tus propias palabras, la diferencia entre lo que necesita terraform validate y lo que necesita terraform plan para un recurso nuevo. Un compañero que nunca usó Terraform te pregunta por qué un comando ("validate") nunca necesita red, mientras que el otro ("plan") a veces sí, a veces no. Explícale la diferencia con el ejemplo de esta lección.

Ver solución

terraform validate solo compara tu HCL contra el schema del provider —qué argumentos existen, de qué tipo, cuáles son obligatorios— ya descargado localmente por terraform init; nunca necesita red, para ningún recurso, en ningún caso, porque nunca mira más allá de tu propio archivo y del schema instalado. terraform plan, en cambio, sí necesita comparar contra la realidad —pero solo para los recursos que ya existen en el state. Para un recurso completamente nuevo, no hay ninguna realidad previa que consultar: el plan completo se construye leyendo tu HCL, exactamente igual que validate. Por eso, en este módulo, donde todo es nuevo, plan se comporta —en cuanto a necesitar red o no— igual que validate.

Ejercicio 2 — Predice qué pasaría si, después de este módulo, alguien corriera terraform plan de nuevo sobre bedrock.tf, DESPUÉS de haber aplicado la infraestructura contra una cuenta AWS real. Basándote en el diagrama de "QUÉ HACE CADA COMANDO" de esta lección, ¿ese segundo plan seguiría corriendo sin red?

Ver solución

No. Una vez que aws_bedrock_guardrail.this (dentro del módulo) existe de verdad en el state —porque alguien corrió apply contra una cuenta real—, cualquier plan posterior sobre ese mismo recurso deja de estar en el caso "recurso nuevo" y entra al caso "recurso existente": Terraform necesita refrescarlo contra la API real de Bedrock antes de calcular el diff, para detectar si algo cambió fuera de Terraform desde la última vez. La condición exacta de esta lección —cero red necesaria— aplica solo mientras el recurso siga sin existir en ningún state, que es, precisamente, el estado en el que este módulo entero trabaja.

Ejercicio 3 — Conecta esta lección con el error que el Módulo 1, lección 7 ya anticipó. Sin volver a leer esa lección completa, explica por qué "Terraform sí puede planear Bedrock sin LocalStack" y "awslocal no pudo listar modelos de Bedrock sin LocalStack" no son una contradicción.

Ver solución

No es una contradicción porque son dos tipos de operación completamente distintos, no dos resultados distintos de la misma pregunta. terraform plan sobre un recurso nuevo nunca necesita leer nada de la realidad —el diff se calcula solo con el HCL declarado—. awslocal bedrock list-foundation-models es, por naturaleza, una consulta que necesita una API real respondiendo con datos reales —no hay ningún HCL de por medio del cual derivar la respuesta—. Que uno funcione sin red y el otro no, no es una inconsistencia de esta guía: es la diferencia exacta entre "declarar una intención" (lo que Terraform hace) y "consultar un estado real" (lo que awslocal siempre hace).


Resumen y siguiente paso

Esta lección explicó el mecanismo exacto que hace posible que este módulo sea el más ejecutado de toda la guía: terraform validate nunca necesita red, para ningún recurso; terraform plan, para un recurso que todavía no existe en ningún state, tampoco la necesita, porque el diff completo se construye a partir del HCL declarado, sin nada real que consultar todavía. Confirmaste esto con la evidencia ya real del Módulo 2, lección 5 (Success! y Plan: 15 to add, sin LocalStack corriendo), y viste, por adelantado, dónde ese mismo mecanismo deja de aplicar —apply, la lección 6— sin necesitar releer todo el Módulo 1.

Antes de avanzar deberías poder: explicar sin ayuda por qué validate nunca necesita red; explicar bajo qué condición exacta plan tampoco la necesita; y anticipar, sin que nadie te lo diga, que apply es una operación de un tipo distinto, para la que el mismo argumento no aplica.

La lección 2 pone este mecanismo a trabajar sobre el panorama completo de recursos Bedrock que el provider hashicorp/aws expone — extraído del schema real, no memorizado de una versión anterior de la documentación.

Recursos

  1. Terraform Docs — Command: validate — referencia oficial, incluida la confirmación de que no requiere acceso a proveedores remotos de datos.
  2. Terraform Docs — Command: plan — referencia oficial del ciclo de vida refreshdiffplan.
  3. Módulo 1, lección 7 de esta guía (07-hands-on-the-honest-attempt-against-bedrock-on-localstack.md) — la fuente de la pregunta que esta lección contesta directamente en su sección de errores comunes.
  4. Módulo 2, lección 5 de esta guía (05-hands-on-the-honest-attempt-of-infracost-scan-on-bedrock-tf.md) — la fuente de la evidencia real (Success!, Plan: 15 to add) citada en esta lección.