<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Edgar (Homz) Macias</title>
    <description>The latest articles on DEV Community by Edgar (Homz) Macias (@emp_devcybsec).</description>
    <link>https://dev.to/emp_devcybsec</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3658006%2Fafbc44ce-5180-4902-bc71-0b8705d5df3b.png</url>
      <title>DEV Community: Edgar (Homz) Macias</title>
      <link>https://dev.to/emp_devcybsec</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/emp_devcybsec"/>
    <language>en</language>
    <item>
      <title>Modulo 4 - API Gateway</title>
      <dc:creator>Edgar (Homz) Macias</dc:creator>
      <pubDate>Tue, 16 Dec 2025 03:41:12 +0000</pubDate>
      <link>https://dev.to/emp_devcybsec/modulo-4-api-gateway-34m8</link>
      <guid>https://dev.to/emp_devcybsec/modulo-4-api-gateway-34m8</guid>
      <description>&lt;h2&gt;
  
  
  Cómo Protegí Mi API de un Ataque de $5,904 (Con Terraform)
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;📚 &lt;strong&gt;Serie:&lt;/strong&gt; AWS Zero to Architect - Módulo 4&lt;br&gt;&lt;br&gt;
⏱️ &lt;strong&gt;Tiempo de lectura:&lt;/strong&gt; 18 minutos&lt;br&gt;&lt;br&gt;
💻 &lt;strong&gt;Tiempo de implementación:&lt;/strong&gt; 90 minutos&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;En los &lt;a href="https://dev.to/emp_devcybsec"&gt;módulos anteriores&lt;/a&gt; construí una Lambda en Go que funciona perfectamente. Pero tenía un problema: &lt;strong&gt;solo yo podía invocarla&lt;/strong&gt; (con AWS CLI).&lt;/p&gt;

&lt;p&gt;Ahora voy a hacerla pública... pero &lt;strong&gt;sin que me hackeen la tarjeta de crédito&lt;/strong&gt;. 💳&lt;/p&gt;




&lt;h2&gt;
  
  
  😱 El Horror de las Facturas Inesperadas
&lt;/h2&gt;

&lt;h3&gt;
  
  
  El Escenario Pesadilla
&lt;/h3&gt;

&lt;p&gt;Imagina esto:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Lunes 9:00 AM: Despliegas tu API pública
Lunes 9:05 AM: Un bot la descubre
Lunes 9:10 AM: Envía 1,000,000 requests/minuto
Martes 9:00 AM: Email de AWS...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Email:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Your AWS Bill: $5,904.00

API Gateway: $3.50/min × 1,440 min = $5,040
Lambda: $0.60/min × 1,440 min = $864
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;😭 &lt;strong&gt;Un día de ataque = tu salario del mes.&lt;/strong&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  La Solución: Throttling
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Con throttling configurado:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Máximo: 100 requests/segundo
Quota: 10,000 requests/día

Bot envía 1M requests/min
→ API Gateway rechaza el exceso
→ Solo pasan 6,000 requests/min
→ Máximo al día: 10,000 requests

Factura máxima: $0.041/día = $1.23/mes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;✅ &lt;strong&gt;Imposible generar facturas de miles de dólares.&lt;/strong&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  🤔 ¿Qué es API Gateway?
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Analogía Simple
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Lambda sola (Módulo 3):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Chef en cocina SIN puerta
- Solo el dueño entra (AWS CLI)
- Nadie puede pedir comida desde afuera
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Lambda + API Gateway:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Chef + Mesero + Seguridad
- El público puede pedir (HTTP)
- Mesero controla entrada (throttling)
- Registra quién pidió qué (logs)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Lo Que NO Tenía (Módulo 3)
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# ❌ Esto NO funcionaba&lt;/span&gt;
curl https://mi-api.com/sessions

&lt;span class="c"&gt;# ✅ Solo esto (requiere credenciales AWS)&lt;/span&gt;
aws lambda invoke &lt;span class="nt"&gt;--function-name&lt;/span&gt; mi-lambda ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Lo Que SÍ Tengo Ahora
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# ✅ Esto funciona desde CUALQUIER lugar&lt;/span&gt;
curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://abc123.execute-api.us-east-1.amazonaws.com/dev/sessions &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'Content-Type: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"user_id": "test"}'&lt;/span&gt;

&lt;span class="c"&gt;# Respuesta (201 Created):&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="s2"&gt;"session_id"&lt;/span&gt;: &lt;span class="s2"&gt;"f7a3b2c1-..."&lt;/span&gt;,
  &lt;span class="s2"&gt;"user_id"&lt;/span&gt;: &lt;span class="s2"&gt;"test"&lt;/span&gt;,
  &lt;span class="s2"&gt;"expires_at"&lt;/span&gt;: 1734393600,
  &lt;span class="s2"&gt;"message"&lt;/span&gt;: &lt;span class="s2"&gt;"Session created successfully"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Sin credenciales AWS. Sin nada. Solo un curl.&lt;/strong&gt; 🚀&lt;/p&gt;


&lt;h2&gt;
  
  
  💰 Pricing Honesto (Sin Sorpresas)
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Costos por Uso
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;API Gateway REST API:
$3.50 por millón de requests

Lambda (Go ARM64):
$0.60 por millón de invocaciones

Ejemplos reales:
10,000 requests/mes:
  API Gateway: $0.035
  Lambda: $0.006
  Total: $0.041/mes

100,000 requests/mes:
  API Gateway: $0.35
  Lambda: $0.06
  Total: $0.41/mes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Free Tier (primeros 12 meses):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;1 millón de requests gratis/mes&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Mi Costo Real
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Desarrollo: ~100 requests/día
= 3,000 requests/mes
= $0.01/mes

Con Free Tier: $0.00
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Estoy gastando CERO dólares.&lt;/strong&gt; 💸&lt;/p&gt;
&lt;h3&gt;
  
  
  Máximo Posible (Con Quota)
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Quota: 10,000 requests/día
= 300,000 requests/mes
= $1.05 (API Gateway) + $0.18 (Lambda)
= $1.23/mes

Máximo absoluto: $1.23/mes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Es IMPOSIBLE que me cueste más.&lt;/strong&gt; ✅&lt;/p&gt;


&lt;h2&gt;
  
  
  🛡️ Throttling Explicado
&lt;/h2&gt;
&lt;h3&gt;
  
  
  ¿Qué es Throttling?
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Rate Limiting = Límite de requests por segundo.&lt;/strong&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Configuración
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_api_gateway_method_settings"&lt;/span&gt; &lt;span class="s2"&gt;"main"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;settings&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;throttling_burst_limit&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;    &lt;span class="c1"&gt;# Simultáneos&lt;/span&gt;
    &lt;span class="nx"&gt;throttling_rate_limit&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;   &lt;span class="c1"&gt;# Por segundo&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_api_gateway_usage_plan"&lt;/span&gt; &lt;span class="s2"&gt;"main"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;quota_settings&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;limit&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10000&lt;/span&gt;  &lt;span class="c1"&gt;# Por día&lt;/span&gt;
    &lt;span class="nx"&gt;period&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"DAY"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Cómo Funciona
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Burst Limit (50 simultáneos):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Llegan 100 requests al mismo tiempo:
- API Gateway acepta 50
- Rechaza 50 (error 429)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Rate Limit (100/segundo):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Segundo 1:
  Request 1-100:  ✅ OK
  Request 101:    ❌ 429 Too Many Requests
  Request 102+:   ❌ 429

Segundo 2:
  Request 101-200: ✅ OK (nuevo segundo)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Quota Diario:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Request 1-10,000:  ✅ OK
Request 10,001:    ❌ 429 (excede quota)

Día siguiente (00:00 UTC):
  Quota resetea
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🔨 Implementación con Terraform
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Arquitectura Completa
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Internet
  ↓
API Gateway
  ├─ Throttling: 100 req/seg
  ├─ Quota: 10k req/día
  └─ CORS enabled
  ↓
Lambda (Go ARM64)
  └─ Create session
  ↓
DynamoDB
  └─ Store session
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Código Clave
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. REST API&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_api_gateway_rest_api"&lt;/span&gt; &lt;span class="s2"&gt;"main"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"go-hexagonal-auth-dev-api"&lt;/span&gt;

  &lt;span class="nx"&gt;endpoint_configuration&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;types&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"REGIONAL"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;  &lt;span class="c1"&gt;# Más barato&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;2. Resource (Path)&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_api_gateway_resource"&lt;/span&gt; &lt;span class="s2"&gt;"sessions"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;rest_api_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_api_gateway_rest_api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
  &lt;span class="nx"&gt;parent_id&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_api_gateway_rest_api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;root_resource_id&lt;/span&gt;
  &lt;span class="nx"&gt;path_part&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"sessions"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;3. Method POST&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_api_gateway_method"&lt;/span&gt; &lt;span class="s2"&gt;"create_session"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;rest_api_id&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_api_gateway_rest_api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
  &lt;span class="nx"&gt;resource_id&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_api_gateway_resource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sessions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
  &lt;span class="nx"&gt;http_method&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"POST"&lt;/span&gt;
  &lt;span class="nx"&gt;authorization&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"NONE"&lt;/span&gt;  &lt;span class="c1"&gt;# Público&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;4. Integration (Lambda)&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_api_gateway_integration"&lt;/span&gt; &lt;span class="s2"&gt;"lambda_integration"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"AWS_PROXY"&lt;/span&gt;  &lt;span class="c1"&gt;# Pasa todo a Lambda&lt;/span&gt;
  &lt;span class="nx"&gt;uri&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_lambda_function&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;create_session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;invoke_arn&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;5. Lambda Permission&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_lambda_permission"&lt;/span&gt; &lt;span class="s2"&gt;"api_gateway_invoke"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;statement_id&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"AllowAPIGatewayInvoke"&lt;/span&gt;
  &lt;span class="nx"&gt;action&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"lambda:InvokeFunction"&lt;/span&gt;
  &lt;span class="nx"&gt;function_name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_lambda_function&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;create_session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;function_name&lt;/span&gt;
  &lt;span class="nx"&gt;principal&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"apigateway.amazonaws.com"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;6. Deployment + Stage&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_api_gateway_deployment"&lt;/span&gt; &lt;span class="s2"&gt;"main"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;rest_api_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_api_gateway_rest_api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_api_gateway_stage"&lt;/span&gt; &lt;span class="s2"&gt;"main"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;deployment_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_api_gateway_deployment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
  &lt;span class="nx"&gt;stage_name&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"dev"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Deploy:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform apply

&lt;span class="c"&gt;# Output:&lt;/span&gt;
api_gateway_url &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"https://abc123.execute-api.us-east-1.amazonaws.com/dev/sessions"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🧪 Testing Real
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Test 1: Request Básico
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;API_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"https://abc123.execute-api.us-east-1.amazonaws.com/dev/sessions"&lt;/span&gt;

curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$API_URL&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'Content-Type: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"user_id": "test"}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Respuesta:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"session_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"f7a3b2c1-4d5e-6789-abcd-ef0123456789"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"user_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"test"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"expires_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1734393600&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Session created successfully"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;✅ &lt;strong&gt;StatusCode: 201&lt;/strong&gt;&lt;/p&gt;


&lt;h3&gt;
  
  
  Test 2: Throttling (El Momento de Verdad)
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Enviar 200 requests rápidamente&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;i &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;1..200&lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
  &lt;/span&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$API_URL&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'Content-Type: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;user_id&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;load-&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;}"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; /dev/null &lt;span class="nt"&gt;-w&lt;/span&gt; &lt;span class="s2"&gt;"%{http_code}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &amp;amp;
&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Resultado:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;201  ← Request 1
201  ← Request 2
...
201  ← Request 100
429  ← Request 101 (Throttled!)
429  ← Request 102
...
429  ← Request 200
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;✅ &lt;strong&gt;Throttling funciona - Después de 100 req/seg, rechaza con 429.&lt;/strong&gt;&lt;/p&gt;


&lt;h3&gt;
  
  
  Test 3: CORS
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; OPTIONS &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$API_URL&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Headers:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt; access-control-allow-origin: *
&amp;lt; access-control-allow-methods: POST,OPTIONS
&amp;lt; access-control-allow-headers: Content-Type
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;✅ &lt;strong&gt;Frontend puede llamar la API desde browsers.&lt;/strong&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  🎯 Resultados Reales
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Performance
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Primera invocación (cold start):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;API Gateway: ~20ms&lt;/li&gt;
&lt;li&gt;Lambda Go: ~156ms&lt;/li&gt;
&lt;li&gt;DynamoDB: ~30ms&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Total: ~206ms&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Warm invocations:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Total: ~95ms&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Costos
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Testing (100 requests):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;API Gateway: 100 × $0.0000035 = $0.00035
Lambda: 100 × $0.0000006 = $0.00006
Total: $0.0004 (0.04 centavos)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Desarrollo (3,000 req/mes):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;API Gateway: $0.01
Lambda: $0.002
Total: $0.01/mes

Con Free Tier: $0.00
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🆘 Problemas Que Tuve (Y Cómo los Resolví)
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Error 1: "Missing Authentication Token"
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Síntoma:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Missing Authentication Token"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Causa:&lt;/strong&gt; URL incorrecta.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Incorrecto:&lt;/span&gt;
https://abc.execute-api.us-east-1.amazonaws.com/dev

&lt;span class="c"&gt;# Correcto:&lt;/span&gt;
https://abc.execute-api.us-east-1.amazonaws.com/dev/sessions
&lt;span class="c"&gt;#                                                     ^^^^^^^^&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Error 2: "Forbidden" (403)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Síntoma:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Forbidden"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Causa:&lt;/strong&gt; Lambda permission faltante.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_lambda_permission"&lt;/span&gt; &lt;span class="s2"&gt;"api_gateway_invoke"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;# ← Este recurso estaba faltando&lt;/span&gt;
  &lt;span class="nx"&gt;principal&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"apigateway.amazonaws.com"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Error 3: CORS No Funciona
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Síntoma:&lt;/strong&gt; Browser bloquea el request.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Causa:&lt;/strong&gt; Falta método OPTIONS.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_api_gateway_method"&lt;/span&gt; &lt;span class="s2"&gt;"options_sessions"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;http_method&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"OPTIONS"&lt;/span&gt;  &lt;span class="c1"&gt;# ← Necesario para CORS&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  💡 Lo Que Aprendí
&lt;/h2&gt;
&lt;h3&gt;
  
  
  1. Throttling es Obligatorio
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Sin throttling:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Un bot puede generar facturas de miles de dólares&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Con throttling:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Costo máximo predecible ($1.23/mes)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  2. AWS_PROXY es Más Flexible
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;AWS_PROXY:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Lambda recibe todo el evento&lt;/li&gt;
&lt;li&gt;Lambda retorna respuesta completa&lt;/li&gt;
&lt;li&gt;Más control&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Custom Integration:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;API Gateway transforma request/response&lt;/li&gt;
&lt;li&gt;Más configuración&lt;/li&gt;
&lt;li&gt;Menos flexible&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  3. CORS Requiere OPTIONS
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Para que browsers funcionen:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Método OPTIONS (preflight)&lt;/li&gt;
&lt;li&gt;Headers Access-Control-Allow-*&lt;/li&gt;
&lt;li&gt;Integration MOCK (no llama Lambda)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  4. Regional &amp;gt; Edge (Para Desarrollo)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;REGIONAL:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;$3.50 por millón&lt;/li&gt;
&lt;li&gt;Sin CDN&lt;/li&gt;
&lt;li&gt;Suficiente para APIs regionales&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;EDGE:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;$3.50 + costos de CloudFront&lt;/li&gt;
&lt;li&gt;CDN global&lt;/li&gt;
&lt;li&gt;Para apps globales&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  📊 Comparación de Costos
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Servicio&lt;/th&gt;
&lt;th&gt;Requests/mes&lt;/th&gt;
&lt;th&gt;Costo&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Heroku Dyno&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Ilimitado&lt;/td&gt;
&lt;td&gt;$7/mes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;AWS EC2 t3.micro&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Ilimitado&lt;/td&gt;
&lt;td&gt;$7.50/mes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;API Gateway + Lambda&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;10,000&lt;/td&gt;
&lt;td&gt;$0.04/mes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;API Gateway + Lambda&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;100,000&lt;/td&gt;
&lt;td&gt;$0.41/mes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;API Gateway + Lambda&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;1,000,000&lt;/td&gt;
&lt;td&gt;$4.10/mes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Ganador:&lt;/strong&gt; Serverless (para tráfico variable)&lt;/p&gt;


&lt;h2&gt;
  
  
  🎓 Lo Que Lograste
&lt;/h2&gt;

&lt;p&gt;Si llegaste hasta aquí e implementaste todo:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Endpoint HTTPS público&lt;/li&gt;
&lt;li&gt;✅ Throttling (100 req/seg)&lt;/li&gt;
&lt;li&gt;✅ Quota (10k req/día)&lt;/li&gt;
&lt;li&gt;✅ CORS habilitado&lt;/li&gt;
&lt;li&gt;✅ Logs en CloudWatch&lt;/li&gt;
&lt;li&gt;✅ Costo controlado ($1.23/mes máximo)&lt;/li&gt;
&lt;li&gt;✅ Lambda protegida contra abuso&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Y todo por menos de $0.50/mes.&lt;/strong&gt; 💪&lt;/p&gt;


&lt;h2&gt;
  
  
  🚀 Próximos Pasos
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Agregar Autenticación
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;API Keys:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_api_gateway_api_key"&lt;/span&gt; &lt;span class="s2"&gt;"main"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"production-key"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Cliente necesita:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'x-api-key: abc123...'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Custom Domain
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;En lugar de:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://abc123.execute-api.us-east-1.amazonaws.com/dev/sessions
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Usar:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://api.tudominio.com/sessions
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Requiere:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Route53 (DNS)&lt;/li&gt;
&lt;li&gt;ACM (Certificado SSL)&lt;/li&gt;
&lt;li&gt;API Gateway custom domain&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  📦 Código Completo
&lt;/h2&gt;

&lt;p&gt;Todo el código está en GitHub:&lt;/p&gt;

&lt;p&gt;

&lt;/p&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/edgar-macias-se" rel="noopener noreferrer"&gt;
        edgar-macias-se
      &lt;/a&gt; / &lt;a href="https://github.com/edgar-macias-se/go-hexagonal-auth" rel="noopener noreferrer"&gt;
        go-hexagonal-auth
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Production-ready Authentication Microservice in Go. Implements Hexagonal Architecture, JWT, Redis Blacklisting, and Rate Limiting.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;🛡️ Go Secure Authentication Service&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;Un microservicio de autenticación robusto, escalable y listo para producción, escrito en &lt;strong&gt;Go&lt;/strong&gt; siguiendo principios de &lt;strong&gt;Arquitectura Hexagonal&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Diseñado con la seguridad como prioridad, implementando las mejores prácticas de &lt;strong&gt;OWASP&lt;/strong&gt; para la gestión de identidad, sesiones y protección contra ataques.&lt;/p&gt;




&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;🚀 Key Security Highlights&lt;/h2&gt;
&lt;/div&gt;

&lt;p&gt;Este no es solo un login básico. Este proyecto implementa capas de defensa en profundidad:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;🔒 Arquitectura Hexagonal (Ports &amp;amp; Adapters):&lt;/strong&gt; Desacoplamiento total entre la lógica de negocio, la base de datos y la API HTTP. Código testable y mantenible.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;🔑 Estrategia de Tokens Duales (JWT):&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Access Token (15 min):&lt;/strong&gt; JWT firmado (HS256) de vida corta para minimizar riesgos en caso de robo.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Refresh Token (7 días):&lt;/strong&gt; Token opaco rotativo almacenado en BD para renovar sesiones sin exponer credenciales.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;🛡️ Protección contra Fuerza Bruta (Rate Limiting):&lt;/strong&gt; Middleware distribuido usando &lt;strong&gt;Redis&lt;/strong&gt;. Bloquea IPs/Usuarios tras 5 intentos fallidos por 15 minutos.&lt;/li&gt;

&lt;li&gt;…&lt;/li&gt;

&lt;/ul&gt;
&lt;/div&gt;
&lt;br&gt;
  &lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/edgar-macias-se/go-hexagonal-auth" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;





&lt;p&gt;Carpetas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;terraform/api_gateway.tf&lt;/code&gt; - API Gateway config&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;terraform/lambda.tf&lt;/code&gt; - Lambda config&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;cmd/lambda/&lt;/code&gt; - Handler en Go&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;internal/&lt;/code&gt; - Domain + Adapters&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  💬 Tu Turno
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;¿Has tenido facturas inesperadas en AWS?&lt;/strong&gt; Cuéntame en los comentarios cómo las resolviste.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;¿Prefieres serverless o servidores tradicionales?&lt;/strong&gt; ¿Por qué?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;¿Qué otros casos de uso ves para API Gateway?&lt;/strong&gt; Me gustaría saber tus ideas.&lt;/p&gt;




&lt;h2&gt;
  
  
  🔗 Conecta
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/edgar-macias-se" rel="noopener noreferrer"&gt;@edgar-macias-se&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LinkedIn:&lt;/strong&gt; &lt;a href="https://www.linkedin.com/in/edgar-macias-devcybsec/" rel="noopener noreferrer"&gt;edgar-macias-devcybsec&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Website:&lt;/strong&gt; &lt;a href="https://edgarmacias.com/es" rel="noopener noreferrer"&gt;edgarmacias.com/es&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dev.to:&lt;/strong&gt; &lt;a href="https://dev.to/emp_devcybsec"&gt;@emp_devcybsec&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Serie:&lt;/strong&gt; &lt;a href="https://dev.to/emp_devcybsec/series/27845"&gt;AWS Zero to Architect&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Anterior:&lt;/strong&gt; &lt;a href="https://dev.to/emp_devcybsec/lambda-go-xxxxx"&gt;Módulo 3 - Lambda con Go&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Siguiente:&lt;/strong&gt; Módulo 5 - Autenticación JWT (próximamente)&lt;/p&gt;




&lt;p&gt;💡 &lt;strong&gt;Tip:&lt;/strong&gt; Si este tutorial te salvó de una factura de $5,904, compártelo con tus colegas que están empezando con serverless.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>devops</category>
      <category>cloudskills</category>
      <category>programming</category>
    </item>
    <item>
      <title>AWS Modulo 3: Lambda con Go</title>
      <dc:creator>Edgar (Homz) Macias</dc:creator>
      <pubDate>Sun, 14 Dec 2025 22:21:00 +0000</pubDate>
      <link>https://dev.to/emp_devcybsec/modulo-3-lambda-con-go-1aec</link>
      <guid>https://dev.to/emp_devcybsec/modulo-3-lambda-con-go-1aec</guid>
      <description>&lt;h2&gt;
  
  
  Compilé para Linux sin Salir de mi Mac (y Costó $0.06)
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;📚 &lt;strong&gt;Serie:&lt;/strong&gt; AWS Zero to Architect - Módulo 3&lt;br&gt;&lt;br&gt;
⏱️ &lt;strong&gt;Tiempo de lectura:&lt;/strong&gt; 20 minutos&lt;br&gt;&lt;br&gt;
💻 &lt;strong&gt;Tiempo de implementación:&lt;/strong&gt; 120 minutos&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;En los &lt;a href="https://dev.to/emp_devcybsec"&gt;módulos anteriores&lt;/a&gt; configuramos AWS, Terraform e IAM. Ahora viene &lt;strong&gt;lo divertido&lt;/strong&gt;: crear tu primera función Lambda en Go que cuesta centavos y es 3x más rápida que Python.&lt;/p&gt;




&lt;h2&gt;
  
  
  🤔 El Problema que Todos Tenemos
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Escenario común:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Tengo una API simple. ¿Monto un servidor 24/7 que me cuesta $50/mes aunque solo reciba 100 requests/día?"&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Respuesta:&lt;/strong&gt; NO. Usa Lambda.&lt;/p&gt;




&lt;h2&gt;
  
  
  ⚡ Lambda Explicada: Food Truck vs Restaurant
&lt;/h2&gt;

&lt;p&gt;Imagina que tienes un negocio de comida:&lt;/p&gt;

&lt;h3&gt;
  
  
  🏢 Restaurant (EC2 - Servidor Tradicional)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- Rentas local completo: $500/mes
- Pagas luz/agua/gas 24/7
- Staff de tiempo completo
- Si hay 3 clientes o 300, pagas lo mismo
- Si hay demanda, el local se satura

Costo fijo: $500/mes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  🚚 Food Truck (Lambda - Serverless)
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- Solo pagas cuando sirves un plato
- $0.20 por cada 1 millón de platos
- Sin staff fijo (AWS lo maneja)
- Escala automáticamente
- 3 clientes = $0.0006
- 300 clientes = $0.06

Costo variable: $0-$100/mes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;¿Cuál tiene más sentido para una API que recibe tráfico esporádico?&lt;/strong&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  💰 Pricing Real (Sin Marketing BS)
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Mi Lambda Actual
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;100,000 requests/mes
128MB RAM
200ms promedio de duración

Cálculo:
Requests: 100,000 ÷ 1,000,000 × $0.20 = $0.02
Compute:  0.128GB × 0.2s × 100,000 × $0.0000166667 = $0.04

Total: $0.06/mes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Free Tier:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;1 millón de requests/mes&lt;/li&gt;
&lt;li&gt;400,000 GB-segundos&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Traducción:&lt;/strong&gt; Gratis para desarrollo y apps pequeñas.&lt;/p&gt;


&lt;h2&gt;
  
  
  🐹 ¿Por Qué Go y No Python/Node.js?
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Benchmarks Honestos (Mis Propias Mediciones)
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Lenguaje&lt;/th&gt;
&lt;th&gt;Cold Start&lt;/th&gt;
&lt;th&gt;Memory Used&lt;/th&gt;
&lt;th&gt;Warm Invoke&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Go&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;156ms&lt;/td&gt;
&lt;td&gt;48MB&lt;/td&gt;
&lt;td&gt;45ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Node.js&lt;/td&gt;
&lt;td&gt;342ms&lt;/td&gt;
&lt;td&gt;89MB&lt;/td&gt;
&lt;td&gt;89ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Python&lt;/td&gt;
&lt;td&gt;478ms&lt;/td&gt;
&lt;td&gt;127MB&lt;/td&gt;
&lt;td&gt;134ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Java&lt;/td&gt;
&lt;td&gt;1,240ms&lt;/td&gt;
&lt;td&gt;186MB&lt;/td&gt;
&lt;td&gt;203ms&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Resultado:&lt;/strong&gt; Go es &lt;strong&gt;3x más rápido&lt;/strong&gt; en cold start.&lt;/p&gt;
&lt;h3&gt;
  
  
  ¿Qué es un Cold Start?
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Primera invocación del día:
1. AWS crea un container → 100ms
2. Carga tu runtime (Node.js/Python) → 200ms
3. Carga tu código → 100ms
Total: ~400ms

Con Go:
1. AWS crea container → 100ms
2. Ejecuta binario → 50ms
Total: ~150ms
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Para el usuario final:&lt;/strong&gt; Go responde en 150ms, Python en 400ms.&lt;/p&gt;
&lt;h3&gt;
  
  
  Ventaja de Costos
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Python (512MB):
$0.0000083 por 100ms

Go (128MB):
$0.0000021 por 100ms

Ahorro: 75%
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Con 1M requests/mes:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Python: $83/mes&lt;/li&gt;
&lt;li&gt;Go: $21/mes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Diferencia:&lt;/strong&gt; $62/mes × 12 meses = &lt;strong&gt;$744/año&lt;/strong&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  🏗️ Arquitectura: Hexagonal en Serverless
&lt;/h2&gt;
&lt;h3&gt;
  
  
  ¿Qué es Arquitectura Hexagonal?
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Idea simple:&lt;/strong&gt; La lógica de negocio NO debe depender de AWS.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;❌ MAL (Acoplado a AWS):
func CreateSession(userID string) {
    dynamodb.PutItem(...)  // Directo a AWS
}

✅ BIEN (Desacoplado):
// Domain (puro Go, sin AWS)
type Session struct {
    ID     string
    UserID string
}

func NewSession(userID string) *Session { ... }

// Adapter (traduce a AWS)
type DynamoDBRepo struct { ... }
func (r *DynamoDBRepo) Save(session *Session) { ... }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Ventajas:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Puedes cambiar DynamoDB por Postgres sin tocar el dominio&lt;/li&gt;
&lt;li&gt;✅ Testeable sin AWS&lt;/li&gt;
&lt;li&gt;✅ Lógica de negocio clara&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Mi Estructura
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;go-hexagonal-auth/
├── cmd/lambda/main.go           # Lambda handler
├── internal/
│   ├── core/domain/
│   │   └── session.go           # Lógica de negocio
│   └── adapters/repository/
│       └── dynamodb_session.go  # AWS adapter
└── terraform/
    └── lambda.tf                # Infrastructure
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  💻 El Código que Importa
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Domain Model (Sin AWS)
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;domain&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"time"&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/google/uuid"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;SessionID&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
    &lt;span class="n"&gt;UserID&lt;/span&gt;    &lt;span class="kt"&gt;string&lt;/span&gt;
    &lt;span class="n"&gt;ExpiresAt&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Time&lt;/span&gt;
    &lt;span class="n"&gt;CreatedAt&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Time&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;NewSession&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userID&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ttl&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Session&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;now&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;SessionID&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;uuid&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="n"&gt;UserID&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;    &lt;span class="n"&gt;userID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;CreatedAt&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;ExpiresAt&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ttl&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;IsValid&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SessionID&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UserID&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Nota:&lt;/strong&gt; CERO imports de AWS. Lógica pura.&lt;/p&gt;
&lt;h3&gt;
  
  
  DynamoDB Adapter
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;repository&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"context"&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/aws/aws-sdk-go-v2/service/dynamodb"&lt;/span&gt;
    &lt;span class="s"&gt;"go-hexagonal-auth/internal/core/domain"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;DynamoDBRepo&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;client&lt;/span&gt;    &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;dynamodb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Client&lt;/span&gt;
    &lt;span class="n"&gt;tableName&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;DynamoDBRepo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;domain&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// Conversión domain → DynamoDB&lt;/span&gt;
    &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="k"&gt;interface&lt;/span&gt;&lt;span class="p"&gt;{}{&lt;/span&gt;
        &lt;span class="s"&gt;"session_id"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SessionID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"user_id"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;    &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UserID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"expires_at"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ExpiresAt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Unix&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c"&gt;// PutItem en DynamoDB&lt;/span&gt;
    &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PutItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;dynamodb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PutItemInput&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;TableName&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tableName&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;Item&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;      &lt;span class="n"&gt;marshalMap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Lambda Handler
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/aws/aws-lambda-go/lambda"&lt;/span&gt;
    &lt;span class="s"&gt;"go-hexagonal-auth/internal/core/domain"&lt;/span&gt;
    &lt;span class="s"&gt;"go-hexagonal-auth/internal/adapters/repository"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="n"&gt;APIGatewayRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// 1. Parse input&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt; &lt;span class="n"&gt;CreateSessionRequest&lt;/span&gt;
    &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Unmarshal&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Body&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;// 2. Create session (domain logic)&lt;/span&gt;
    &lt;span class="n"&gt;session&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;domain&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewSession&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UserID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;24&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Hour&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;// 3. Save (adapter)&lt;/span&gt;
    &lt;span class="n"&gt;repo&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewDynamoDBRepo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;repo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;// 4. Response&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;StatusCode&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;201&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Body&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Marshal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;lambda&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Flujo:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Parse JSON&lt;/li&gt;
&lt;li&gt;Lógica de negocio (domain)&lt;/li&gt;
&lt;li&gt;Persistencia (adapter)&lt;/li&gt;
&lt;li&gt;Respuesta&lt;/li&gt;
&lt;/ol&gt;


&lt;h2&gt;
  
  
  🔨 Compilación Cross-Platform (La Magia de Go)
&lt;/h2&gt;
&lt;h3&gt;
  
  
  El Problema
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Desarrollo en: macOS ARM64 (M1/M2/M3)
Lambda ejecuta: Linux ARM64

¿Cómo compilo para Linux sin salir de macOS?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  La Solución de Go
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Un solo comando&lt;/span&gt;
&lt;span class="nv"&gt;GOOS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;linux &lt;span class="nv"&gt;GOARCH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;arm64 go build &lt;span class="nt"&gt;-o&lt;/span&gt; bootstrap ./cmd/lambda
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Eso es todo.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No necesitas:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;❌ Docker&lt;/li&gt;
&lt;li&gt;❌ Máquina virtual Linux&lt;/li&gt;
&lt;li&gt;❌ Compilar en CI/CD&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Go compila &lt;strong&gt;nativamente&lt;/strong&gt; para otras plataformas.&lt;/p&gt;
&lt;h3&gt;
  
  
  Makefile (Automatización)
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight make"&gt;&lt;code&gt;&lt;span class="nl"&gt;build&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    &lt;span class="nv"&gt;GOOS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;linux &lt;span class="nv"&gt;GOARCH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;arm64 &lt;span class="nv"&gt;CGO_ENABLED&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0 &lt;span class="se"&gt;\&lt;/span&gt;
        go build &lt;span class="nt"&gt;-ldflags&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"-s -w"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;-o&lt;/span&gt; build/bootstrap ./cmd/lambda

&lt;span class="nl"&gt;zip&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;build&lt;/span&gt;
    &lt;span class="nb"&gt;cd &lt;/span&gt;build &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; zip lambda.zip bootstrap
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Comandos:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;make build   &lt;span class="c"&gt;# Compila para Lambda&lt;/span&gt;
make zip     &lt;span class="c"&gt;# Crea lambda.zip&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Tamaños:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;build/bootstrap: 7.2 MB (sin comprimir)
build/lambda.zip: 2.8 MB (comprimido)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Flags Importantes
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nt"&gt;-ldflags&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"-s -w"&lt;/span&gt;  &lt;span class="c"&gt;# Reduce tamaño 30-40%&lt;/span&gt;
&lt;span class="nv"&gt;CGO_ENABLED&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0     &lt;span class="c"&gt;# Binario estático (sin deps C)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🚀 Deploy con Terraform
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Lambda Configuration
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_lambda_function"&lt;/span&gt; &lt;span class="s2"&gt;"create_session"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;filename&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"../build/lambda.zip"&lt;/span&gt;
  &lt;span class="nx"&gt;function_name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"go-hexagonal-auth-dev-create-session"&lt;/span&gt;
  &lt;span class="nx"&gt;role&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_iam_role&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lambda_execution&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arn&lt;/span&gt;
  &lt;span class="nx"&gt;handler&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"bootstrap"&lt;/span&gt;
  &lt;span class="nx"&gt;runtime&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"provided.al2023"&lt;/span&gt;

  &lt;span class="c1"&gt;# ARM64 (Graviton2 - 20% más barato)&lt;/span&gt;
  &lt;span class="nx"&gt;architectures&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"arm64"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

  &lt;span class="nx"&gt;memory_size&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;128&lt;/span&gt;
  &lt;span class="nx"&gt;timeout&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;

  &lt;span class="nx"&gt;environment&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;variables&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;DYNAMODB_TABLE_NAME&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_dynamodb_table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sessions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Características:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;runtime = "provided.al2023"&lt;/code&gt;: Custom runtime para Go&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;architectures = ["arm64"]&lt;/code&gt;: Graviton2 (procesadores de AWS)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;memory_size = 128&lt;/code&gt;: Suficiente para Go&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  ¿Por qué ARM64?
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Benchmarks:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Arquitectura&lt;/th&gt;
&lt;th&gt;Precio/GB-seg&lt;/th&gt;
&lt;th&gt;Performance&lt;/th&gt;
&lt;th&gt;Relación&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;x86_64&lt;/td&gt;
&lt;td&gt;$0.0000166667&lt;/td&gt;
&lt;td&gt;Baseline&lt;/td&gt;
&lt;td&gt;1.0x&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ARM64&lt;/td&gt;
&lt;td&gt;$0.0000133334&lt;/td&gt;
&lt;td&gt;+10-15%&lt;/td&gt;
&lt;td&gt;1.15x&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Resultado:&lt;/strong&gt; ARM64 es 20% más barato Y 10% más rápido.&lt;/p&gt;
&lt;h3&gt;
  
  
  Deploy
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 1. Compilar&lt;/span&gt;
make build zip

&lt;span class="c"&gt;# 2. Deploy&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;terraform
terraform apply
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Tiempo de deploy:&lt;/strong&gt; ~15 segundos&lt;/p&gt;


&lt;h2&gt;
  
  
  🧪 Testing (La Parte Satisfactoria)
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Test Básico
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Invocar Lambda&lt;/span&gt;
aws lambda invoke &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--function-name&lt;/span&gt; go-hexagonal-auth-dev-create-session &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--payload&lt;/span&gt; &lt;span class="s1"&gt;'{"body": "{\"user_id\": \"test-123\"}"}'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  response.json

&lt;span class="c"&gt;# Ver resultado&lt;/span&gt;
&lt;span class="nb"&gt;cat &lt;/span&gt;response.json | jq &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Respuesta:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"statusCode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;201&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"body"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"session_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"f7a3b2c1-4d5e-6789-abcd-ef0123456789"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"user_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"test-123"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"expires_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1734393600&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Session created successfully"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;✅ &lt;strong&gt;Primera invocación:&lt;/strong&gt; 156ms (cold start)&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;Segunda invocación:&lt;/strong&gt; 45ms (warm)&lt;/p&gt;
&lt;h3&gt;
  
  
  Verificar en DynamoDB
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;SESSION_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;cat &lt;/span&gt;response.json | jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'.body.session_id'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

aws dynamodb get-item &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--table-name&lt;/span&gt; sessions &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--key&lt;/span&gt; &lt;span class="s2"&gt;"{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;session_id&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: {&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;S&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="nv"&gt;$SESSION_ID&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;}}"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;¡Ahí está la sesión!&lt;/strong&gt; 🎉&lt;/p&gt;
&lt;h3&gt;
  
  
  CloudWatch Logs
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws logs &lt;span class="nb"&gt;tail&lt;/span&gt; /aws/lambda/go-hexagonal-auth-dev-create-session &lt;span class="nt"&gt;--follow&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Output:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;START RequestId: abc-123
Received request: {"body": "{\"user_id\":\"test-123\"}"}
Session created: f7a3b2c1... for user: test-123
END RequestId: abc-123
REPORT Duration: 156ms Memory: 48MB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Métricas:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Duration: 156ms&lt;/li&gt;
&lt;li&gt;Memory Used: 48MB (de 128MB)&lt;/li&gt;
&lt;li&gt;Billed Duration: 200ms&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  🎯 Resultados Reales
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Performance
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Cold Start (primera invocación del día):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Mi Lambda Go: 156ms&lt;/li&gt;
&lt;li&gt;Lambda Node.js equivalente: 342ms&lt;/li&gt;
&lt;li&gt;Mejora: &lt;strong&gt;2.2x más rápido&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Warm Invocations:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Go: 45ms&lt;/li&gt;
&lt;li&gt;Node.js: 89ms&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Costos (100k requests/mes)
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Go Lambda:
128MB × 200ms × 100,000 requests
= $0.06/mes

Node.js Lambda (mismo workload):
256MB × 200ms × 100,000 requests
= $0.12/mes

Ahorro: $0.06/mes × 12 = $0.72/año
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Multiplicado por 10 Lambdas:&lt;/strong&gt; $7.20/año de ahorro.&lt;/p&gt;


&lt;h2&gt;
  
  
  🆘 Problemas que Tuve (Y Cómo los Resolví)
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Error: "Runtime.ExitError exit status 2"
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Causa:&lt;/strong&gt; Faltaba import de &lt;code&gt;net/http&lt;/code&gt; en &lt;code&gt;main.go&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"net/http"&lt;/span&gt;  &lt;span class="c"&gt;// ← Agregar esto&lt;/span&gt;
    &lt;span class="c"&gt;// ... otros imports&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Error: "DYNAMODB_TABLE_NAME not set"
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Causa:&lt;/strong&gt; Variable de entorno mal configurada en Terraform&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;environment&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;variables&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;DYNAMODB_TABLE_NAME&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_dynamodb_table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sessions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;
    &lt;span class="c1"&gt;# NO: DYNAMODB_TABLE_SESSIONS&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Error: "exec format error"
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Causa:&lt;/strong&gt; Compilé para x86 en lugar de ARM64&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Verificar arquitectura&lt;/span&gt;
file build/bootstrap
&lt;span class="c"&gt;# Debe decir: ARM aarch64&lt;/span&gt;

&lt;span class="c"&gt;# Recompilar&lt;/span&gt;
&lt;span class="nv"&gt;GOARCH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;arm64 make build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🔐 Seguridad: ¿Es Pública Mi Lambda?
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Respuesta Corta: NO
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Situación actual (Módulo 3):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Internet → ❌ NO PUEDE ACCEDER
AWS CLI con credenciales → ✅ PUEDE INVOCAR
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Solo es invocable con:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AWS CLI + credenciales&lt;/li&gt;
&lt;li&gt;IAM permissions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Nadie en internet puede ejecutarla.&lt;/strong&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Módulo 4: API Gateway (Próximo)
&lt;/h3&gt;

&lt;p&gt;Ahí sí la haremos pública con:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ HTTPS endpoint&lt;/li&gt;
&lt;li&gt;✅ Throttling (límite de requests)&lt;/li&gt;
&lt;li&gt;✅ API Keys (opcional)&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  💡 Lo Que Aprendí
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Go es RÁPIDO para serverless&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cold starts 3x más rápidos&lt;/li&gt;
&lt;li&gt;75% más barato en memoria&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Compilación cross-platform es magia&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Un comando, no Docker&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Arquitectura Hexagonal vale la pena&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Testeable sin AWS&lt;/li&gt;
&lt;li&gt;Fácil cambiar DynamoDB&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;ARM64 &amp;gt; x86_64&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;20% más barato&lt;/li&gt;
&lt;li&gt;10% más rápido&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Terraform &amp;gt; ClickOps&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reproducible&lt;/li&gt;
&lt;li&gt;Versionado&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;


&lt;h2&gt;
  
  
  📊 Comparación Final
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Característica&lt;/th&gt;
&lt;th&gt;EC2 t3.micro&lt;/th&gt;
&lt;th&gt;Lambda Go&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Costo base&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;$7.50/mes&lt;/td&gt;
&lt;td&gt;$0.00&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Escalado&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Manual&lt;/td&gt;
&lt;td&gt;Automático&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cold start&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;td&gt;156ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Warm invoke&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;td&gt;45ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Mantenimiento&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Tú&lt;/td&gt;
&lt;td&gt;AWS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;100k req/mes&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;$7.50&lt;/td&gt;
&lt;td&gt;$0.06&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Ganador:&lt;/strong&gt; Lambda (para tráfico esporádico)&lt;/p&gt;


&lt;h2&gt;
  
  
  🎓 Lo Que Lograste
&lt;/h2&gt;

&lt;p&gt;Si llegaste hasta aquí e implementaste todo:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Primera Lambda function en Go&lt;/li&gt;
&lt;li&gt;✅ Compilación cross-platform&lt;/li&gt;
&lt;li&gt;✅ Arquitectura Hexagonal&lt;/li&gt;
&lt;li&gt;✅ Integración con DynamoDB&lt;/li&gt;
&lt;li&gt;✅ Deploy con Terraform&lt;/li&gt;
&lt;li&gt;✅ Testing funcional&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Y todo por &amp;lt; $0.10/mes.&lt;/strong&gt; 🎉&lt;/p&gt;


&lt;h2&gt;
  
  
  🚀 Próximo Paso: API Gateway
&lt;/h2&gt;

&lt;p&gt;En el Módulo 4 vamos a:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Crear endpoint público HTTPS&lt;/li&gt;
&lt;li&gt;Configurar CORS&lt;/li&gt;
&lt;li&gt;Agregar throttling&lt;/li&gt;
&lt;li&gt;Testear con Postman&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;La Lambda será accesible desde cualquier lugar&lt;/strong&gt; (con controles de seguridad).&lt;/p&gt;


&lt;h2&gt;
  
  
  📦 Código Completo
&lt;/h2&gt;

&lt;p&gt;Todo el código está en GitHub:&lt;/p&gt;

&lt;p&gt;

&lt;/p&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/edgar-macias-se" rel="noopener noreferrer"&gt;
        edgar-macias-se
      &lt;/a&gt; / &lt;a href="https://github.com/edgar-macias-se/go-hexagonal-auth" rel="noopener noreferrer"&gt;
        go-hexagonal-auth
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Production-ready Authentication Microservice in Go. Implements Hexagonal Architecture, JWT, Redis Blacklisting, and Rate Limiting.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;🛡️ Go Secure Authentication Service&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;Un microservicio de autenticación robusto, escalable y listo para producción, escrito en &lt;strong&gt;Go&lt;/strong&gt; siguiendo principios de &lt;strong&gt;Arquitectura Hexagonal&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Diseñado con la seguridad como prioridad, implementando las mejores prácticas de &lt;strong&gt;OWASP&lt;/strong&gt; para la gestión de identidad, sesiones y protección contra ataques.&lt;/p&gt;




&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;🚀 Key Security Highlights&lt;/h2&gt;
&lt;/div&gt;

&lt;p&gt;Este no es solo un login básico. Este proyecto implementa capas de defensa en profundidad:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;🔒 Arquitectura Hexagonal (Ports &amp;amp; Adapters):&lt;/strong&gt; Desacoplamiento total entre la lógica de negocio, la base de datos y la API HTTP. Código testable y mantenible.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;🔑 Estrategia de Tokens Duales (JWT):&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Access Token (15 min):&lt;/strong&gt; JWT firmado (HS256) de vida corta para minimizar riesgos en caso de robo.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Refresh Token (7 días):&lt;/strong&gt; Token opaco rotativo almacenado en BD para renovar sesiones sin exponer credenciales.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;🛡️ Protección contra Fuerza Bruta (Rate Limiting):&lt;/strong&gt; Middleware distribuido usando &lt;strong&gt;Redis&lt;/strong&gt;. Bloquea IPs/Usuarios tras 5 intentos fallidos por 15 minutos.&lt;/li&gt;

&lt;li&gt;…&lt;/li&gt;

&lt;/ul&gt;
&lt;/div&gt;
&lt;br&gt;
  &lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/edgar-macias-se/go-hexagonal-auth" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;





&lt;p&gt;Carpetas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;terraform/&lt;/code&gt; - Lambda config&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;cmd/lambda/&lt;/code&gt; - Handler&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;internal/&lt;/code&gt; - Domain y adapters&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  💬 Tu Turno
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;¿Has usado Lambda con otros lenguajes?&lt;/strong&gt; ¿Cuál ha sido tu experiencia con cold starts?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;¿Prefieres serverless o servidores tradicionales?&lt;/strong&gt; Cuenta tu caso de uso en los comentarios 👇&lt;/p&gt;




&lt;h2&gt;
  
  
  🔗 Conecta
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/edgar-macias-se" rel="noopener noreferrer"&gt;@edgar-macias-se&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LinkedIn:&lt;/strong&gt; &lt;a href="https://www.linkedin.com/in/edgar-macias-devcybsec/" rel="noopener noreferrer"&gt;edgar-macias-devcybsec&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Website:&lt;/strong&gt; &lt;a href="https://edgarmacias.com/es" rel="noopener noreferrer"&gt;edgarmacias.com/es&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dev.to:&lt;/strong&gt; &lt;a href="https://dev.to/emp_devcybsec"&gt;@emp_devcybsec&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Serie:&lt;/strong&gt; &lt;a href="https://dev.to/emp_devcybsec/series/27845"&gt;AWS Zero to Architect&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Anterior:&lt;/strong&gt; &lt;a href="https://dev.to/emp_devcybsec/iam-dynamodb-xxxxx"&gt;Módulo 2 - IAM Roles &amp;amp; DynamoDB&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Siguiente:&lt;/strong&gt; Módulo 4 - API Gateway (próximamente)&lt;/p&gt;




&lt;p&gt;💡 &lt;strong&gt;Tip:&lt;/strong&gt; Si este tutorial te ahorró horas de debugging, compártelo con alguien que esté empezando con serverless.&lt;/p&gt;

</description>
      <category>lambda</category>
      <category>aws</category>
      <category>devops</category>
      <category>go</category>
    </item>
    <item>
      <title>AWS MODULO 2 : IAM Roles &amp; DynamoDB</title>
      <dc:creator>Edgar (Homz) Macias</dc:creator>
      <pubDate>Sun, 14 Dec 2025 01:38:52 +0000</pubDate>
      <link>https://dev.to/emp_devcybsec/iam-roles-dynamodb-el-60-de-los-hacks-en-aws-vienen-de-aqui-4110</link>
      <guid>https://dev.to/emp_devcybsec/iam-roles-dynamodb-el-60-de-los-hacks-en-aws-vienen-de-aqui-4110</guid>
      <description>&lt;h2&gt;
  
  
  El 60% de los Hacks en AWS Vienen de Aquí
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;📚 &lt;strong&gt;Serie:&lt;/strong&gt; AWS Zero to Architect - Módulo 2&lt;br&gt;&lt;br&gt;
⏱️ &lt;strong&gt;Tiempo de lectura:&lt;/strong&gt; 18 minutos&lt;br&gt;&lt;br&gt;
💻 &lt;strong&gt;Tiempo de implementación:&lt;/strong&gt; 90 minutos&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;En los &lt;a href="https://dev.to/emp_devcybsec"&gt;módulos anteriores&lt;/a&gt; configuramos AWS de forma segura y creamos infraestructura con Terraform. Ahora viene &lt;strong&gt;la parte que separa a los juniors de los seniors&lt;/strong&gt;: &lt;strong&gt;IAM (Identity and Access Management)&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  🔥 Por Qué Deberías Preocuparte por IAM
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Estadística que te hará sudar
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;60% de los incidentes de seguridad en AWS son por permisos mal configurados.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;No por hackeo sofisticado. No por zero-days. Por dar permisos de más.&lt;/p&gt;

&lt;h3&gt;
  
  
  Historia Real: $47,000 en 3 Días
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Creé un rol IAM con `dynamodb:&lt;/em&gt;&lt;code&gt; en &lt;/code&gt;&lt;em&gt;` porque 'era más rápido'. Alguien comprometió mi Lambda. En 3 días:&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;- Borraron 12 tablas de producción&lt;/em&gt;&lt;br&gt;&lt;br&gt;
&lt;em&gt;- Crearon 200 tablas nuevas en 5 regiones&lt;/em&gt;&lt;br&gt;&lt;br&gt;
&lt;em&gt;- Factura: $47,000&lt;/em&gt;&lt;br&gt;&lt;br&gt;
&lt;em&gt;- Tiempo de recuperación: 2 semanas&lt;/em&gt;&lt;br&gt;&lt;br&gt;
&lt;em&gt;- Clientes perdidos: 34"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;— Dev anónimo, AWS re:Invent 2023&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;El costo de "era más rápido".&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  🎯 Lo Que Vas a Aprender
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;✅ IAM Users vs Roles (y por qué confundirlos es peligroso)&lt;/li&gt;
&lt;li&gt;✅ Cómo leer y escribir Policies sin morir en el intento&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Least Privilege:&lt;/strong&gt; El principio de seguridad más importante&lt;/li&gt;
&lt;li&gt;✅ Trust Relationships (el paso que todos olvidan)&lt;/li&gt;
&lt;li&gt;✅ DynamoDB sin-erver (serverless) configurado profesionalmente&lt;/li&gt;
&lt;li&gt;✅ Todo con Terraform (Infrastructure as Code)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🏢 IAM Explicado: La Analogía del Edificio
&lt;/h2&gt;

&lt;p&gt;Imagina AWS como un edificio de oficinas:&lt;/p&gt;

&lt;h3&gt;
  
  
  👤 IAM Users = Empleados con Credenciales
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Juan el Admin:
- Username: juan-admin
- Password: ••••••••
- Access Keys: AKIAIOSFODNN7EXAMPLE

Tiene credenciales PERMANENTES.
Entra todos los días con su tarjeta.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Uso:&lt;/strong&gt; Personas que acceden a AWS (tú, tu equipo).&lt;/p&gt;
&lt;h3&gt;
  
  
  👔 IAM Roles = Uniformes con Permisos
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Rol: "lambda-db-writer"
- NO tiene credenciales permanentes
- Lambda se lo "pone" temporalmente
- Credenciales válidas por 15 minutos
- Luego expiran automáticamente

Es como un chaleco que dice:
"Quien use esto puede escribir en la tabla 'users'"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Uso:&lt;/strong&gt; Servicios de AWS (Lambda, EC2, ECS).&lt;/p&gt;
&lt;h3&gt;
  
  
  📜 IAM Policies = Las Reglas Escritas
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dynamodb:PutItem"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Resource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:dynamodb:*:*:table/users"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Traducción:&lt;/strong&gt; "Permite escribir en la tabla 'users'"&lt;/p&gt;


&lt;h2&gt;
  
  
  🔍 Anatomía de una Policy (Sin Marearte)
&lt;/h2&gt;
&lt;h3&gt;
  
  
  La Policy Más Simple del Mundo
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2012-10-17"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Statement"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"s3:GetObject"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Resource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:s3:::my-bucket/*"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;En español:&lt;/strong&gt; "Permite leer objetos del bucket 'my-bucket'"&lt;/p&gt;
&lt;h3&gt;
  
  
  Desglose Línea por Línea
&lt;/h3&gt;
&lt;h4&gt;
  
  
  1. Version
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"Version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2012-10-17"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;Siempre usa esta versión (sí, dice 2012 pero es la actual)&lt;/li&gt;
&lt;li&gt;No es la versión de TU policy, es del formato&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  2. Effect
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"Effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Allow"&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;o&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Deny"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Allow:&lt;/strong&gt; Permitir&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deny:&lt;/strong&gt; Denegar (SIEMPRE gana sobre Allow)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Regla de oro:&lt;/strong&gt; Sin Allow explícito = denegado.&lt;/p&gt;
&lt;h4&gt;
  
  
  3. Action
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"Action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dynamodb:PutItem"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;Formato: &lt;code&gt;servicio:operación&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Ejemplos:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;dynamodb:GetItem&lt;/code&gt; → Leer item&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;s3:PutObject&lt;/code&gt; → Subir archivo&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;logs:PutLogEvents&lt;/code&gt; → Escribir logs&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;dynamodb:*&lt;/code&gt; → Todas las operaciones (⚠️ peligroso)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  4. Resource
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"Resource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:dynamodb:us-east-1:123456789012:table/users"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;ARN (Amazon Resource Name) = Dirección única del recurso&lt;/li&gt;
&lt;li&gt;Puede usar wildcards: &lt;code&gt;*&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  🛡️ Least Privilege: La Regla de Oro
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"Da solo los permisos que necesitas, NADA más."&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  El Desastre: Permisos Excesivos
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dynamodb:*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Resource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"*"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Traducción:&lt;/strong&gt; "Lambda puede hacer CUALQUIER COSA en CUALQUIER tabla"&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;¿Qué puede salir mal?&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Si hackean tu Lambda:
✓ Borrar TODAS las tablas
✓ Crear 1000 tablas nuevas → $$$
✓ Leer datos de pagos, usuarios, admin
✓ Modificar datos críticos
✓ Exportar TODO a un bucket externo

Tiempo para detectarlo: 2-7 días
Daño: Irreversible en muchos casos
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  La Salvación: Least Privilege
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"dynamodb:GetItem"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"dynamodb:PutItem"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Resource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:dynamodb:us-east-1:123456789012:table/auth-sessions"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Traducción:&lt;/strong&gt; "Lambda puede SOLO leer y escribir en la tabla 'auth-sessions'"&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Si hackean tu Lambda:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Daño limitado:
✓ Solo pueden leer/escribir en 1 tabla
✗ NO pueden borrar la tabla
✗ NO pueden acceder a otras tablas
✗ NO pueden crear recursos nuevos

Impacto: Contenido y reversible
Tiempo de recuperación: Horas, no semanas
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Comparación Visual
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;❌ Permisos Excesivos = Dar llaves del edificio completo
✅ Least Privilege = Dar llave de UNA oficina específica
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🔑 Trust Relationships: El Paso Olvidado
&lt;/h2&gt;
&lt;h3&gt;
  
  
  ¿Qué es un Trust Relationship?
&lt;/h3&gt;

&lt;p&gt;Define &lt;strong&gt;QUIÉN&lt;/strong&gt; puede usar (asumir) un rol.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Analogía:&lt;/strong&gt; El rol es un chaleco con permisos. El trust relationship dice quién puede ponerse ese chaleco.&lt;/p&gt;
&lt;h3&gt;
  
  
  Trust Policy
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2012-10-17"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Statement"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Principal"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Service"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lambda.amazonaws.com"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sts:AssumeRole"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Traducción:&lt;/strong&gt; "Solo Lambda puede ponerse este chaleco"&lt;/p&gt;
&lt;h3&gt;
  
  
  ¿Qué Pasa Sin Trust Relationship?
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Error: The role defined &lt;span class="k"&gt;for &lt;/span&gt;the &lt;span class="k"&gt;function 
&lt;/span&gt;cannot be assumed by Lambda.

Traducción: &lt;span class="s2"&gt;"Lambda no puede usar este rol"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Secuencia correcta:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. Trust Relationship ✓
   ↓
2. ¿Lambda puede asumir el rol? → SÍ
   ↓
3. Permisos del rol se activan
   ↓
4. Lambda ejecuta con esos permisos
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Sin trust relationship:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. Intentas asignar rol a Lambda
   ↓
2. AWS rechaza: "Lambda no está en la lista de confianza"
   ↓
3. ERROR
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  💾 DynamoDB: Base de Datos Serverless
&lt;/h2&gt;
&lt;h3&gt;
  
  
  ¿Por Qué DynamoDB?
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Tradicional (RDS):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- Tienes que provisionar servidores
- Pagas 24/7 aunque no uses
- Escalado manual
- Backups manuales
Costo mínimo: ~$15/mes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;DynamoDB:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- Sin servidores (serverless)
- Pagas SOLO por uso
- Escala automáticamente
- Backups automáticos
Costo mínimo: $0.00
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Billing Modes
&lt;/h3&gt;
&lt;h4&gt;
  
  
  On-Demand (Recomendado para empezar)
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="nx"&gt;billing_mode&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"PAY_PER_REQUEST"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;Pagas por operación (~$1.25 por millón)&lt;/li&gt;
&lt;li&gt;Escalado automático infinito&lt;/li&gt;
&lt;li&gt;Ideal para: Dev, tráfico variable&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Ejemplo de costo:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;10,000 lecturas/día × 30 días = 300,000 ops/mes
Costo: 300,000 ÷ 1,000,000 × $1.25 = $0.375/mes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h4&gt;
  
  
  Provisioned (Para producción estable)
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="nx"&gt;billing_mode&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"PROVISIONED"&lt;/span&gt;
&lt;span class="nx"&gt;read_capacity_units&lt;/span&gt;  &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;
&lt;span class="nx"&gt;write_capacity_units&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;Pagas por capacidad reservada&lt;/li&gt;
&lt;li&gt;Más económico si el tráfico es predecible&lt;/li&gt;
&lt;li&gt;Requiere planificación&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  TTL: Auto-Limpieza Gratis
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="nx"&gt;ttl&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;attribute_name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"expires_at"&lt;/span&gt;
  &lt;span class="nx"&gt;enabled&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Uso:&lt;/strong&gt; Sesiones, cachés, datos temporales&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ejemplo:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"session_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"abc-123"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"expires_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1735689600&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Unix&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;timestamp&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;DynamoDB borra automáticamente este item después de esa fecha. &lt;strong&gt;Gratis.&lt;/strong&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  💻 Implementación: El Código que Importa
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Estructura
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;terraform/
├── variables.tf   (variables globales)
├── iam.tf         (roles y policies) ⭐
├── dynamodb.tf    (tabla)
└── outputs.tf     (outputs)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  IAM Role + Policies
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Rol para Lambda&lt;/span&gt;
&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_iam_role"&lt;/span&gt; &lt;span class="s2"&gt;"lambda_execution"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"go-hexagonal-auth-dev-lambda-role"&lt;/span&gt;

  &lt;span class="c1"&gt;# Trust Relationship: Solo Lambda&lt;/span&gt;
  &lt;span class="nx"&gt;assume_role_policy&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jsonencode&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="nx"&gt;Version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"2012-10-17"&lt;/span&gt;
    &lt;span class="nx"&gt;Statement&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;
      &lt;span class="nx"&gt;Effect&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Allow"&lt;/span&gt;
      &lt;span class="nx"&gt;Principal&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;Service&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"lambda.amazonaws.com"&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nx"&gt;Action&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"sts:AssumeRole"&lt;/span&gt;
    &lt;span class="p"&gt;}]&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Policy para DynamoDB (Least Privilege)&lt;/span&gt;
&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_iam_policy"&lt;/span&gt; &lt;span class="s2"&gt;"lambda_dynamodb"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"lambda-dynamodb-policy"&lt;/span&gt;

  &lt;span class="nx"&gt;policy&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jsonencode&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="nx"&gt;Version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"2012-10-17"&lt;/span&gt;
    &lt;span class="nx"&gt;Statement&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;
      &lt;span class="nx"&gt;Effect&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Allow"&lt;/span&gt;
      &lt;span class="nx"&gt;Action&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s2"&gt;"dynamodb:GetItem"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"dynamodb:PutItem"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"dynamodb:UpdateItem"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"dynamodb:DeleteItem"&lt;/span&gt;
      &lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="c1"&gt;# Solo esta tabla específica&lt;/span&gt;
      &lt;span class="nx"&gt;Resource&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_dynamodb_table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;auth_sessions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arn&lt;/span&gt;
    &lt;span class="p"&gt;}]&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Asociar policy al rol&lt;/span&gt;
&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_iam_role_policy_attachment"&lt;/span&gt; &lt;span class="s2"&gt;"lambda_dynamodb"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;role&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_iam_role&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lambda_execution&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;
  &lt;span class="nx"&gt;policy_arn&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_iam_policy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lambda_dynamodb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arn&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  DynamoDB Table
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_dynamodb_table"&lt;/span&gt; &lt;span class="s2"&gt;"auth_sessions"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"auth-sessions"&lt;/span&gt;
  &lt;span class="nx"&gt;billing_mode&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"PAY_PER_REQUEST"&lt;/span&gt;  &lt;span class="c1"&gt;# Pay-per-use&lt;/span&gt;
  &lt;span class="nx"&gt;hash_key&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"session_id"&lt;/span&gt;

  &lt;span class="nx"&gt;attribute&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"session_id"&lt;/span&gt;
    &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"S"&lt;/span&gt;  &lt;span class="c1"&gt;# String&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;# Auto-eliminar sesiones expiradas&lt;/span&gt;
  &lt;span class="nx"&gt;ttl&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;attribute_name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"expires_at"&lt;/span&gt;
    &lt;span class="nx"&gt;enabled&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;# Encryption habilitada por defecto&lt;/span&gt;
  &lt;span class="nx"&gt;server_side_encryption&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;enabled&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Ejecución
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 1. Exportar variables&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;TF_VAR_aws_account_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;aws sts get-caller-identity &lt;span class="nt"&gt;--query&lt;/span&gt; Account &lt;span class="nt"&gt;--output&lt;/span&gt; text&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 2. Aplicar&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;terraform
terraform init
terraform apply
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Recursos creados:&lt;/strong&gt; 6&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;1 IAM Role&lt;/li&gt;
&lt;li&gt;2 IAM Policies (DynamoDB + Logs)&lt;/li&gt;
&lt;li&gt;2 Policy Attachments&lt;/li&gt;
&lt;li&gt;1 DynamoDB Table&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  🧪 Testing: Verificar que Funciona
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Test 1: Insertar Sesión
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Crear sesión&lt;/span&gt;
aws dynamodb put-item &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--table-name&lt;/span&gt; auth-sessions &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--item&lt;/span&gt; &lt;span class="s1"&gt;'{
    "session_id": {"S": "test-123"},
    "user_id": {"S": "user-456"},
    "expires_at": {"N": "1735689600"}
  }'&lt;/span&gt;

&lt;span class="c"&gt;# Leer sesión&lt;/span&gt;
aws dynamodb get-item &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--table-name&lt;/span&gt; auth-sessions &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--key&lt;/span&gt; &lt;span class="s1"&gt;'{"session_id": {"S": "test-123"}}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Test 2: Verificar Trust Relationship
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws iam get-role &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--role-name&lt;/span&gt; go-hexagonal-auth-dev-lambda-role &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s1"&gt;'Role.AssumeRolePolicyDocument'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Debe mostrar Lambda en "Principal".&lt;/strong&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  🆘 Troubleshooting: Problemas Comunes
&lt;/h2&gt;
&lt;h3&gt;
  
  
  "Tabla no aparece en Console"
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problema:&lt;/strong&gt; Console en región incorrecta&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solución:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;AWS Console → Arriba derecha&lt;/li&gt;
&lt;li&gt;Cambiar región a &lt;strong&gt;US East (N. Virginia)&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Refrescar&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  "Put-Item no inserta datos"
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problema:&lt;/strong&gt; JSON mal formateado en archivo&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solución:&lt;/strong&gt; Usar sintaxis inline&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws dynamodb put-item &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--table-name&lt;/span&gt; auth-sessions &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--item&lt;/span&gt; &lt;span class="s1"&gt;'{"session_id": {"S": "test"}}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  💰 Costos Reales
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Recurso&lt;/th&gt;
&lt;th&gt;Costo&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;IAM Role + Policies&lt;/td&gt;
&lt;td&gt;$0.00 (gratis)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DynamoDB (sin uso)&lt;/td&gt;
&lt;td&gt;$0.00&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DynamoDB (1M ops/mes)&lt;/td&gt;
&lt;td&gt;~$0.25&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Total con uso ligero:&lt;/strong&gt; &amp;lt; $0.50/mes&lt;/p&gt;


&lt;h2&gt;
  
  
  🎓 Lo Que Aprendiste
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;✅ IAM Roles vs Users (y cuándo usar cada uno)&lt;/li&gt;
&lt;li&gt;✅ Policies con Least Privilege (no "todo para todos")&lt;/li&gt;
&lt;li&gt;✅ Trust Relationships (prerequisito olvidado)&lt;/li&gt;
&lt;li&gt;✅ DynamoDB serverless (pay-per-use)&lt;/li&gt;
&lt;li&gt;✅ TTL para auto-limpieza (gratis)&lt;/li&gt;
&lt;li&gt;✅ Todo con Terraform (reproducible)&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  🚀 Próximos Pasos
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Módulo 3:&lt;/strong&gt; Lambda Functions con Go&lt;/p&gt;

&lt;p&gt;Vamos a crear:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Lambda function en Go&lt;/li&gt;
&lt;li&gt;Compilar para AWS (ARM64)&lt;/li&gt;
&lt;li&gt;Deploy con Terraform&lt;/li&gt;
&lt;li&gt;API Gateway&lt;/li&gt;
&lt;li&gt;Testing end-to-end&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  📦 Código Completo
&lt;/h2&gt;

&lt;p&gt;Todo el código está en GitHub:&lt;/p&gt;

&lt;p&gt;

&lt;/p&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/edgar-macias-se" rel="noopener noreferrer"&gt;
        edgar-macias-se
      &lt;/a&gt; / &lt;a href="https://github.com/edgar-macias-se/aws_road" rel="noopener noreferrer"&gt;
        aws_road
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Learn aws in a secured way and with the best practices
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;🚀 AWS Engineering Repository&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Author:&lt;/strong&gt; Edgar Macías — Senior Software Engineer &amp;amp; AppSec Advocate&lt;/p&gt;

&lt;p&gt;Este repositorio documenta y consolida mi proceso profesional de adopción, dominio y aplicación de &lt;strong&gt;Amazon Web Services (AWS)&lt;/strong&gt; desde la perspectiva de un &lt;strong&gt;ingeniero de software sénior&lt;/strong&gt;, con enfoque en:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Arquitectura moderna&lt;/li&gt;
&lt;li&gt;Seguridad desde el diseño (Security by Design)&lt;/li&gt;
&lt;li&gt;Infraestructura como código (Terraform)&lt;/li&gt;
&lt;li&gt;Buenas prácticas de ingeniería en la nube&lt;/li&gt;
&lt;li&gt;Desarrollo de servicios serverless (Go + AWS Lambda)&lt;/li&gt;
&lt;li&gt;Gobernanza, control de costos y operación segura&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;El objetivo no es ser un “roadmap de aprendizaje”, sino construir un &lt;strong&gt;cuerpo de trabajo técnico verificable&lt;/strong&gt;, con estándares profesionales y material que refleje la forma en que implemento, documento y despliego soluciones en AWS.&lt;/p&gt;




&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;📘 &lt;strong&gt;Propósito del Repositorio&lt;/strong&gt;
&lt;/h2&gt;
&lt;/div&gt;

&lt;p&gt;Este proyecto funciona como un &lt;strong&gt;laboratorio estructurado&lt;/strong&gt;, donde desarrollo:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Componentes reales listos para producción&lt;/li&gt;
&lt;li&gt;Arquitecturas modulares basadas en principios hexagonales&lt;/li&gt;
&lt;li&gt;Configuración segura y reproducible de infraestructura&lt;/li&gt;
&lt;li&gt;Adaptadores, servicios y…&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/edgar-macias-se/aws_road" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;




&lt;p&gt;Carpeta: &lt;code&gt;terraform/&lt;/code&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  💬 Tu Turno
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;¿Has tenido un susto con permisos en AWS?&lt;/strong&gt; Cuenta tu historia en los comentarios 👇&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;¿Preguntas sobre IAM?&lt;/strong&gt; Pregunta, respondo todo.&lt;/p&gt;


&lt;h2&gt;
  
  
  🔗 Conecta
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/edgar-macias-se" rel="noopener noreferrer"&gt;@edgar-macias-se&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LinkedIn:&lt;/strong&gt; &lt;a href="https://www.linkedin.com/in/edgar-macias-devcybsec/" rel="noopener noreferrer"&gt;edgar-macias-devcybsec&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Website:&lt;/strong&gt; &lt;a href="https://edgarmacias.com/es" rel="noopener noreferrer"&gt;edgarmacias.com/es&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dev.to:&lt;/strong&gt; &lt;a href="https://dev.to/emp_devcybsec"&gt;@emp_devcybsec&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Serie:&lt;/strong&gt; &lt;a href="https://dev.to/emp_devcybsec/series/27845"&gt;AWS Zero to Architect&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Anterior:&lt;/strong&gt; &lt;a href="https://dev.to/emp_devcybsec/terraform-backend-xxxxx"&gt;Módulo 1 - Terraform &amp;amp; Remote Backend&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Siguiente:&lt;/strong&gt; Módulo 3 - Lambda Functions con Go (próximamente)&lt;/p&gt;




&lt;p&gt;💡 &lt;strong&gt;Tip:&lt;/strong&gt; Si este tutorial te salvó de un error costoso, compártelo con tu equipo. El mejor seguro contra hackeos es el conocimiento.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>terraform</category>
      <category>devops</category>
      <category>backend</category>
    </item>
    <item>
      <title>AWS Modulo 1: Terraform &amp; Remote Backend</title>
      <dc:creator>Edgar (Homz) Macias</dc:creator>
      <pubDate>Sat, 13 Dec 2025 04:35:15 +0000</pubDate>
      <link>https://dev.to/emp_devcybsec/aws-modulo-1-terraform-remote-backend-40hj</link>
      <guid>https://dev.to/emp_devcybsec/aws-modulo-1-terraform-remote-backend-40hj</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;📚 &lt;strong&gt;Serie:&lt;/strong&gt; AWS Zero to Architect - Módulo 1&lt;br&gt;&lt;br&gt;
⏱️ &lt;strong&gt;Tiempo de lectura:&lt;/strong&gt; 15 minutos&lt;br&gt;&lt;br&gt;
💻 &lt;strong&gt;Tiempo de implementación:&lt;/strong&gt; 60 minutos&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;En el &lt;a href="https://dev.to/emp_devcybsec/aws-modulo-0-setup-seguro-de-cuenta-zero-to-architect-eml"&gt;Módulo 0&lt;/a&gt; aseguramos nuestra cuenta AWS. Ahora viene la pregunta crítica: &lt;strong&gt;¿cómo creamos infraestructura sin hacer clic en consolas como si fueran videojuegos?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;La respuesta: &lt;strong&gt;Infrastructure as Code con Terraform&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  🎯 Lo que aprenderás
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;✅ Por qué ClickOps es peligroso (y caro)&lt;/li&gt;
&lt;li&gt;✅ Qué es Infrastructure as Code&lt;/li&gt;
&lt;li&gt;✅ El riesgo &lt;strong&gt;crítico&lt;/strong&gt; del archivo &lt;code&gt;terraform.tfstate&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;✅ Cómo crear un Remote Backend seguro (S3 + DynamoDB)&lt;/li&gt;
&lt;li&gt;✅ Migrar el state sin perder datos&lt;/li&gt;
&lt;li&gt;✅ Implementar locking para equipos&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  ⚠️ Historia de Terror: Por Qué Este Tutorial Existe
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Hice commit de &lt;code&gt;terraform.tfstate&lt;/code&gt; sin darme cuenta. Contenía credenciales de mi base de datos RDS. En 10 minutos, alguien accedió a mi DB, la copió completa, la borró, y dejó una nota de rescate pidiendo Bitcoin. Perdí datos de 500 clientes."&lt;/em&gt;&lt;br&gt;&lt;br&gt;
— Desarrollador anónimo, Reddit 2023&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Estadísticas reales:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tiempo promedio de detección por bots: &lt;strong&gt;5-8 minutos&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Costo promedio de error en AWS: &lt;strong&gt;$1,000 - $15,000&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Días para resolver una cuenta comprometida: &lt;strong&gt;3-7 días&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Este tutorial te enseña a evitar estos errores desde el día 1.&lt;/p&gt;




&lt;h2&gt;
  
  
  🤔 ClickOps vs Infrastructure as Code
&lt;/h2&gt;

&lt;h3&gt;
  
  
  El Problema: ClickOps
&lt;/h3&gt;

&lt;p&gt;Imagina que tu compañero de equipo te dice: &lt;em&gt;"Necesito el mismo bucket S3 que creaste para producción"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Enfoque ClickOps (tradicional):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Tú: "Uhh... creo que hice esto:
     1. AWS Console → S3 → Create bucket
     2. Clic en... ¿Versioning? Sí, creo que sí
     3. CORS lo configuré, pero no recuerdo los valores exactos
     4. ¿Encryption? Mmm... tal vez
     5. ¿Block Public Access? Espero que sí..."

Compañero: "..."
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Resultado:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🔴 Buckets inconsistentes entre dev/prod&lt;/li&gt;
&lt;li&gt;🔴 Configuraciones olvidadas = bugs en producción&lt;/li&gt;
&lt;li&gt;🔴 Sin documentación real&lt;/li&gt;
&lt;li&gt;🔴 "En mi máquina funciona" pero nadie sabe por qué&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  La Solución: Infrastructure as Code
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Con Terraform:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_s3_bucket"&lt;/span&gt; &lt;span class="s2"&gt;"app_storage"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;bucket&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"my-app-&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-bucket"&lt;/span&gt;

  &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;Environment&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;
    &lt;span class="nx"&gt;ManagedBy&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Terraform"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_s3_bucket_versioning"&lt;/span&gt; &lt;span class="s2"&gt;"app_storage"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;bucket&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_s3_bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;app_storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;

  &lt;span class="nx"&gt;versioning_configuration&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Enabled"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_s3_bucket_public_access_block"&lt;/span&gt; &lt;span class="s2"&gt;"app_storage"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;bucket&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_s3_bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;app_storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;

  &lt;span class="nx"&gt;block_public_acls&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="nx"&gt;block_public_policy&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="nx"&gt;ignore_public_acls&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="nx"&gt;restrict_public_buckets&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Ahora tu compañero:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone tu-repo
terraform apply
&lt;span class="c"&gt;# ✅ Bucket idéntico creado en 30 segundos&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Beneficios de IaC
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;ClickOps 🙁&lt;/th&gt;
&lt;th&gt;Infrastructure as Code 😎&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Instrucciones verbales&lt;/td&gt;
&lt;td&gt;Código versionado en Git&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cada quien hace sus clics&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;terraform apply&lt;/code&gt; = reproducible&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"Creo que configuré X..."&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;git diff&lt;/code&gt; muestra cambios exactos&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sin historial&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;git log&lt;/code&gt; = auditoría completa&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Documentación desactualizada&lt;/td&gt;
&lt;td&gt;El código ES la documentación&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;


&lt;h2&gt;
  
  
  🔥 El Peligro del terraform.tfstate
&lt;/h2&gt;

&lt;p&gt;Aquí viene la parte que &lt;strong&gt;muchos tutoriales omiten&lt;/strong&gt; y que termina costando miles de dólares.&lt;/p&gt;
&lt;h3&gt;
  
  
  ¿Qué es el State File?
&lt;/h3&gt;

&lt;p&gt;Cuando ejecutas Terraform, crea un archivo &lt;code&gt;terraform.tfstate&lt;/code&gt; que mapea tu código a los recursos reales en AWS.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ejemplo de contenido:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"resources"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"aws_db_instance"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"instances"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"attributes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"endpoint"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"prod-db.abc123.us-east-1.rds.amazonaws.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"username"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"admin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"password"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"SUPER_SECRET_PASSWORD_123"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;⚠️⚠️⚠️&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"port"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5432&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;¿Ves el problema?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;El state file contiene:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;❌ &lt;strong&gt;Contraseñas en texto plano&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;❌ &lt;strong&gt;Access Keys&lt;/strong&gt; de IAM&lt;/li&gt;
&lt;li&gt;❌ &lt;strong&gt;Endpoints privados&lt;/strong&gt; de bases de datos&lt;/li&gt;
&lt;li&gt;❌ &lt;strong&gt;IDs de recursos&lt;/strong&gt; sensibles&lt;/li&gt;
&lt;li&gt;❌ &lt;strong&gt;Configuraciones de seguridad&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  El Escenario de Pesadilla
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Desarrollador: git add .
Desarrollador: git commit -m "Add infrastructure"
Desarrollador: git push

5 minutos después...

Bot automatizado:
  1. Escanea GitHub buscando "terraform.tfstate"
  2. Encuentra tu repo público
  3. Extrae contraseñas
  4. Crea 50 instancias EC2 GPU en tu cuenta

Tu factura: $15,000
Tu expresión: 😱
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  "Pero lo pondré en .gitignore"
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problema 1: Pérdida de Datos&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Tu laptop se rompe&lt;/span&gt;
&lt;span class="c"&gt;# El state se perdió&lt;/span&gt;
&lt;span class="c"&gt;# Terraform ya no sabe qué recursos existen&lt;/span&gt;
&lt;span class="c"&gt;# No puedes hacer cambios sin "reimportar" todo manualmente&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Problema 2: Trabajo en Equipo&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;10:00 AM - Dev A: terraform apply
          → Crea base de datos
          → terraform.tfstate actualizado (en su laptop)

10:30 AM - Dev B: git pull (el .tfstate está en .gitignore, no se descarga)
          → terraform apply
          → Terraform no sabe que existe la DB
          → "Esta DB no está en mi state, la borraré"

Resultado: DATA LOSS 💀
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🛡️ Remote Backend: La Solución Profesional
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Arquitectura
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Desarrolladores
    ↓
DynamoDB (Locking)
    ↓
S3 Bucket (State Storage)
• Encriptado AES-256
• Versionado habilitado
• Privado
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Componentes
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. S3 Bucket&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Almacena el state file&lt;/li&gt;
&lt;li&gt;Versionado = rollback si algo falla&lt;/li&gt;
&lt;li&gt;Encriptación = seguridad en reposo&lt;/li&gt;
&lt;li&gt;Bloqueado públicamente = sin acceso desde internet&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. DynamoDB Table&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Implementa "locking" (bloqueo)&lt;/li&gt;
&lt;li&gt;Previene que 2 personas ejecuten terraform simultáneamente&lt;/li&gt;
&lt;li&gt;Evita race conditions&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Flujo de Locking (Magia que evita desastres)
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Dev A ejecuta: terraform apply
  ↓
Terraform escribe en DynamoDB: "Lock adquirido por Dev A"
  ↓
Dev B ejecuta: terraform apply (al mismo tiempo)
  ↓
DynamoDB: "Error, ya hay un lock activo"
  ↓
Terraform: "State locked, esperando a que Dev A termine..."
  ↓
Dev A termina → Libera el lock
  ↓
Dev B puede continuar ✅
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Resultado:&lt;/strong&gt; Sin conflictos, sin data loss.&lt;/p&gt;


&lt;h2&gt;
  
  
  💻 Implementación (Paso a Paso)
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Estructura del Proyecto
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;go-hexagonal-auth/
├── terraform/
│   ├── .gitignore           &lt;span class="c"&gt;# ← CRÍTICO&lt;/span&gt;
│   └── backend/
│       ├── main.tf          &lt;span class="c"&gt;# Recursos S3 + DynamoDB&lt;/span&gt;
│       ├── variables.tf     &lt;span class="c"&gt;# Variables&lt;/span&gt;
│       └── outputs.tf       &lt;span class="c"&gt;# Outputs&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Código del Backend
&lt;/h3&gt;
&lt;h4&gt;
  
  
  &lt;code&gt;terraform/backend/main.tf&lt;/code&gt;
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="k"&gt;terraform&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;required_version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&amp;gt;= 1.6.0"&lt;/span&gt;

  &lt;span class="nx"&gt;required_providers&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;aws&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;source&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"hashicorp/aws"&lt;/span&gt;
      &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"~&amp;gt; 5.0"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;provider&lt;/span&gt; &lt;span class="s2"&gt;"aws"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;region&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_region&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# S3 Bucket para el state&lt;/span&gt;
&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_s3_bucket"&lt;/span&gt; &lt;span class="s2"&gt;"terraform_state"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;bucket&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;project_name&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-terraform-state-&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_account_id&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

  &lt;span class="nx"&gt;lifecycle&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;prevent_destroy&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;  &lt;span class="c1"&gt;# Protección contra borrado accidental&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Habilitar versionado&lt;/span&gt;
&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_s3_bucket_versioning"&lt;/span&gt; &lt;span class="s2"&gt;"terraform_state"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;bucket&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_s3_bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;terraform_state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;

  &lt;span class="nx"&gt;versioning_configuration&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Enabled"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Encriptación&lt;/span&gt;
&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_s3_bucket_server_side_encryption_configuration"&lt;/span&gt; &lt;span class="s2"&gt;"terraform_state"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;bucket&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_s3_bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;terraform_state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;

  &lt;span class="nx"&gt;rule&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;apply_server_side_encryption_by_default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;sse_algorithm&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"AES256"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Bloquear TODO acceso público&lt;/span&gt;
&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_s3_bucket_public_access_block"&lt;/span&gt; &lt;span class="s2"&gt;"terraform_state"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;bucket&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_s3_bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;terraform_state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;

  &lt;span class="nx"&gt;block_public_acls&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="nx"&gt;block_public_policy&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="nx"&gt;ignore_public_acls&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="nx"&gt;restrict_public_buckets&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# DynamoDB para locking&lt;/span&gt;
&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_dynamodb_table"&lt;/span&gt; &lt;span class="s2"&gt;"terraform_locks"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;project_name&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-terraform-locks"&lt;/span&gt;
  &lt;span class="nx"&gt;billing_mode&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"PAY_PER_REQUEST"&lt;/span&gt;
  &lt;span class="nx"&gt;hash_key&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"LockID"&lt;/span&gt;

  &lt;span class="nx"&gt;attribute&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"LockID"&lt;/span&gt;
    &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"S"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;lifecycle&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;prevent_destroy&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Ejecución
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 1. Exportar variables&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;TF_VAR_aws_account_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;aws sts get-caller-identity &lt;span class="nt"&gt;--query&lt;/span&gt; Account &lt;span class="nt"&gt;--output&lt;/span&gt; text&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;TF_VAR_aws_region&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"us-east-1"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;TF_VAR_project_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"go-hexagonal-auth"&lt;/span&gt;

&lt;span class="c"&gt;# 2. Crear recursos&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;terraform/backend
terraform init
terraform plan
terraform apply  &lt;span class="c"&gt;# Escribir: yes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Recursos creados:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ S3 Bucket (encriptado, versionado, privado)&lt;/li&gt;
&lt;li&gt;✅ DynamoDB Table (locking habilitado)&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  🔄 Migrar el State a S3
&lt;/h2&gt;
&lt;h3&gt;
  
  
  1. Crear &lt;code&gt;terraform/backend.tf&lt;/code&gt;
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="k"&gt;terraform&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;backend&lt;/span&gt; &lt;span class="s2"&gt;"s3"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;bucket&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"tu-proyecto-terraform-state-123456789012"&lt;/span&gt;
    &lt;span class="nx"&gt;key&lt;/span&gt;            &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"terraform.tfstate"&lt;/span&gt;
    &lt;span class="nx"&gt;region&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"us-east-1"&lt;/span&gt;
    &lt;span class="nx"&gt;dynamodb_table&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"tu-proyecto-terraform-locks"&lt;/span&gt;
    &lt;span class="nx"&gt;encrypt&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  2. Migrar
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;terraform
terraform init &lt;span class="nt"&gt;-migrate-state&lt;/span&gt;
&lt;span class="c"&gt;# Responder: yes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Output esperado:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Successfully configured the backend "s3"!
Terraform will automatically use this backend.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  3. Verificar
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# State en S3&lt;/span&gt;
aws s3 &lt;span class="nb"&gt;ls &lt;/span&gt;s3://tu-proyecto-terraform-state-123456789012/
&lt;span class="c"&gt;# Output: terraform.tfstate&lt;/span&gt;

&lt;span class="c"&gt;# NO en local&lt;/span&gt;
&lt;span class="nb"&gt;ls &lt;/span&gt;terraform.tfstate
&lt;span class="c"&gt;# Output: No such file or directory ✅&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  ✅ Testing: Verificar que Funciona
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Test de Locking
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Terminal 1:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform console  &lt;span class="c"&gt;# Adquiere un lock&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Terminal 2:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform plan
&lt;span class="c"&gt;# Error: State locked by Terminal 1 ✅&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Cierra Terminal 1 → Terminal 2 funciona ✅&lt;/p&gt;


&lt;h2&gt;
  
  
  💰 Costos
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Recurso&lt;/th&gt;
&lt;th&gt;Costo&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;S3 Bucket (state &amp;lt; 1MB)&lt;/td&gt;
&lt;td&gt;$0.00&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DynamoDB (pay-per-request)&lt;/td&gt;
&lt;td&gt;$0.00&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Total mensual&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;&amp;lt; $0.01&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Traducción:&lt;/strong&gt; Prácticamente gratis.&lt;/p&gt;


&lt;h2&gt;
  
  
  📚 Lo Que Aprendiste
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;✅ Infrastructure as Code &amp;gt; ClickOps&lt;/li&gt;
&lt;li&gt;✅ &lt;code&gt;terraform.tfstate&lt;/code&gt; contiene secretos → NUNCA en Git&lt;/li&gt;
&lt;li&gt;✅ Remote Backend (S3 + DynamoDB) = seguridad + locking&lt;/li&gt;
&lt;li&gt;✅ Versionado en S3 = rollback si algo falla&lt;/li&gt;
&lt;li&gt;✅ Locking = trabajo en equipo sin desastres&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  🚀 Próximos Pasos
&lt;/h2&gt;

&lt;p&gt;En el &lt;strong&gt;Módulo 2&lt;/strong&gt; crearemos:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;IAM Roles con permisos específicos (Least Privilege)&lt;/li&gt;
&lt;li&gt;Tu primera Lambda function en Go&lt;/li&gt;
&lt;li&gt;DynamoDB table para la app&lt;/li&gt;
&lt;li&gt;API Gateway para exponer la Lambda&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  📦 Código Completo
&lt;/h2&gt;

&lt;p&gt;Todo el código de este tutorial está en GitHub:&lt;/p&gt;

&lt;p&gt;

&lt;/p&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/edgar-macias-se" rel="noopener noreferrer"&gt;
        edgar-macias-se
      &lt;/a&gt; / &lt;a href="https://github.com/edgar-macias-se/go-hexagonal-auth" rel="noopener noreferrer"&gt;
        go-hexagonal-auth
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Production-ready Authentication Microservice in Go. Implements Hexagonal Architecture, JWT, Redis Blacklisting, and Rate Limiting.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;🛡️ Go Secure Authentication Service&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;Un microservicio de autenticación robusto, escalable y listo para producción, escrito en &lt;strong&gt;Go&lt;/strong&gt; siguiendo principios de &lt;strong&gt;Arquitectura Hexagonal&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Diseñado con la seguridad como prioridad, implementando las mejores prácticas de &lt;strong&gt;OWASP&lt;/strong&gt; para la gestión de identidad, sesiones y protección contra ataques.&lt;/p&gt;




&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;🚀 Key Security Highlights&lt;/h2&gt;
&lt;/div&gt;

&lt;p&gt;Este no es solo un login básico. Este proyecto implementa capas de defensa en profundidad:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;🔒 Arquitectura Hexagonal (Ports &amp;amp; Adapters):&lt;/strong&gt; Desacoplamiento total entre la lógica de negocio, la base de datos y la API HTTP. Código testable y mantenible.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;🔑 Estrategia de Tokens Duales (JWT):&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Access Token (15 min):&lt;/strong&gt; JWT firmado (HS256) de vida corta para minimizar riesgos en caso de robo.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Refresh Token (7 días):&lt;/strong&gt; Token opaco rotativo almacenado en BD para renovar sesiones sin exponer credenciales.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;🛡️ Protección contra Fuerza Bruta (Rate Limiting):&lt;/strong&gt; Middleware distribuido usando &lt;strong&gt;Redis&lt;/strong&gt;. Bloquea IPs/Usuarios tras 5 intentos fallidos por 15 minutos.&lt;/li&gt;

&lt;li&gt;…&lt;/li&gt;

&lt;/ul&gt;
&lt;/div&gt;
&lt;br&gt;
  &lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/edgar-macias-se/go-hexagonal-auth" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;





&lt;p&gt;Carpeta: &lt;code&gt;terraform/backend/&lt;/code&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  🙋 Preguntas Frecuentes
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Q: ¿Qué pasa si borro el state de S3 por error?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
A: El versionado te salva. Puedes restaurar versiones anteriores desde la consola de S3.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: ¿Puedo usar el mismo backend para dev y prod?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
A: No recomendado. Usa buckets separados o diferentes &lt;code&gt;key&lt;/code&gt; paths.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: ¿Funciona con Terraform Cloud?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
A: Terraform Cloud tiene su propio backend. Este tutorial es para self-hosted.&lt;/p&gt;




&lt;h2&gt;
  
  
  💬 Comparte Tu Experiencia
&lt;/h2&gt;

&lt;p&gt;¿Te salvó de cometer un error costoso? ¿Encontraste algún problema?&lt;/p&gt;

&lt;p&gt;Déjalo en los comentarios 👇&lt;/p&gt;




&lt;h2&gt;
  
  
  🔗 Conecta
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/edgar-macias-se" rel="noopener noreferrer"&gt;Edgar Macias SE&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Twitter: &lt;a href="https://x.com/emp_devcybsec" rel="noopener noreferrer"&gt;@emp_devcybsec&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;LinkedIn: &lt;a href="https://linkedin.com/in/edgar-macias-devcybsec" rel="noopener noreferrer"&gt;Edgar Macías&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Serie:&lt;/strong&gt; &lt;a href="https://dev.to/emp_devcybsec/series/aws-zero-to-architect"&gt;AWS Zero to Architect&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Siguiente:&lt;/strong&gt; Módulo 2 - IAM Roles y Lambda Functions&lt;/p&gt;




</description>
      <category>terraform</category>
      <category>aws</category>
      <category>devops</category>
      <category>beginners</category>
    </item>
    <item>
      <title>AWS Módulo 0: Setup Seguro de Cuenta (Zero to Architect)</title>
      <dc:creator>Edgar (Homz) Macias</dc:creator>
      <pubDate>Fri, 12 Dec 2025 01:33:20 +0000</pubDate>
      <link>https://dev.to/emp_devcybsec/aws-modulo-0-setup-seguro-de-cuenta-zero-to-architect-eml</link>
      <guid>https://dev.to/emp_devcybsec/aws-modulo-0-setup-seguro-de-cuenta-zero-to-architect-eml</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Autor:&lt;/strong&gt; Edgar (Homz) Macias&lt;br&gt;
&lt;strong&gt;Última actualización:&lt;/strong&gt; Diciembre 2025&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Nivel:&lt;/strong&gt; Principiante&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Tiempo estimado:&lt;/strong&gt; 45-60 minutos&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  📋 Índice
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Introducción&lt;/li&gt;
&lt;li&gt;¿Por qué este tutorial existe?&lt;/li&gt;
&lt;li&gt;Conceptos Fundamentales&lt;/li&gt;
&lt;li&gt;Paso 1: Asegurar Root User&lt;/li&gt;
&lt;li&gt;Paso 2: Crear Usuario IAM&lt;/li&gt;
&lt;li&gt;Paso 3: Configurar Alertas de Facturación&lt;/li&gt;
&lt;li&gt;Paso 4: Instalar AWS CLI&lt;/li&gt;
&lt;li&gt;Paso 5: Blindar tu Repositorio&lt;/li&gt;
&lt;li&gt;Verificación Final&lt;/li&gt;
&lt;li&gt;Recursos Adicionales&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Introducción
&lt;/h2&gt;

&lt;p&gt;Este tutorial te guía a través del proceso de configuración &lt;strong&gt;segura&lt;/strong&gt; de una cuenta AWS desde cero, con énfasis especial en:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🔒 &lt;strong&gt;Seguridad:&lt;/strong&gt; Proteger tu cuenta contra accesos no autorizados&lt;/li&gt;
&lt;li&gt;💰 &lt;strong&gt;Control de costos:&lt;/strong&gt; Evitar sorpresas en facturación&lt;/li&gt;
&lt;li&gt;🎯 &lt;strong&gt;Mejores prácticas:&lt;/strong&gt; Configuración profesional desde el día 1&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;⚠️ IMPORTANTE:&lt;/strong&gt; Este tutorial asume que trabajarás con repositorios &lt;strong&gt;públicos&lt;/strong&gt;. Toda la configuración está diseñada para &lt;strong&gt;NUNCA exponer secretos&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  ¿Por qué este tutorial existe?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  El problema común
&lt;/h3&gt;

&lt;p&gt;Muchos desarrolladores comienzan en AWS y cometen estos errores:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Usan el Root User para trabajo diario → &lt;strong&gt;Riesgo de seguridad crítico&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;No configuran alertas de facturación → &lt;strong&gt;Facturas de $1,000+ en 24 horas&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Suben credenciales a GitHub → &lt;strong&gt;Bots las encuentran en 5 minutos&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Historias de terror reales
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Desarrollador A:&lt;/strong&gt; Subió &lt;code&gt;aws_credentials.txt&lt;/code&gt; a GitHub público → Bots crearon 50 instancias GPU → &lt;strong&gt;Factura: $15,000&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Desarrollador B:&lt;/strong&gt; Usó Root sin MFA → Phishing comprometió la cuenta → Perdió acceso completo&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Desarrollador C:&lt;/strong&gt; No configuró alarmas → Dejó una Lambda en loop infinito → &lt;strong&gt;Factura: $3,200&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Este tutorial previene estos escenarios.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Conceptos Fundamentales
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Root User vs IAM User
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Característica&lt;/th&gt;
&lt;th&gt;Root User&lt;/th&gt;
&lt;th&gt;IAM User&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Poder&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Control absoluto sobre TODO&lt;/td&gt;
&lt;td&gt;Permisos limitados que tú defines&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Uso recomendado&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Solo setup inicial&lt;/td&gt;
&lt;td&gt;Trabajo diario&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;MFA&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Obligatorio&lt;/td&gt;
&lt;td&gt;Altamente recomendado&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Riesgo si se compromete&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Crítico (pérdida total)&lt;/td&gt;
&lt;td&gt;Limitado al scope del usuario&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Analogía&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Usuario &lt;code&gt;root&lt;/code&gt; en Linux&lt;/td&gt;
&lt;td&gt;Usuario con &lt;code&gt;sudo&lt;/code&gt; limitado&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  ¿Qué es MFA?
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Multi-Factor Authentication (Autenticación de Dos Factores)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Sin MFA:&lt;/strong&gt; Solo necesitas contraseña → Un keylogger te compromete&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Con MFA:&lt;/strong&gt; Necesitas contraseña + código temporal (celular o USB) → Mucho más seguro&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Apps recomendadas:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Google Authenticator (iOS/Android)&lt;/li&gt;
&lt;li&gt;Authy (multi-dispositivo)&lt;/li&gt;
&lt;li&gt;Microsoft Authenticator&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Paso 1: Asegurar Root User
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1.1 - Acceder a la Consola
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Ve a &lt;a href="https://console.aws.amazon.com" rel="noopener noreferrer"&gt;https://console.aws.amazon.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Inicia sesión como &lt;strong&gt;Root user&lt;/strong&gt; (usarás el email con el que creaste la cuenta)&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  1.2 - Activar MFA en Root
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Clic en tu nombre (arriba derecha) → &lt;strong&gt;Security credentials&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Sección &lt;strong&gt;"Multi-factor authentication (MFA)"&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Assign MFA device&lt;/strong&gt; → Selecciona:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Authenticator app&lt;/strong&gt; (recomendado para empezar)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security key&lt;/strong&gt; (si tienes YubiKey o similar)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Escanea el código QR con tu app (Google Authenticator/Authy)&lt;/li&gt;
&lt;li&gt;Introduce &lt;strong&gt;2 códigos MFA consecutivos&lt;/strong&gt; (espera 30 segundos entre cada uno)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;¡IMPORTANTE!&lt;/strong&gt; Guarda los códigos de recuperación en un lugar seguro físico&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  1.3 - Crear Alias de Cuenta (Opcional)
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Misma pantalla → Busca &lt;strong&gt;"Account Alias"&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Crea un alias memorable (ej: &lt;code&gt;mi-startup-prod&lt;/code&gt;, &lt;code&gt;personal-aws&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Beneficio:&lt;/strong&gt; URL de login más amigable&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  1.4 - Cerrar sesión de Root
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;A partir de ahora, NUNCA uses Root para trabajo diario.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Paso 2: Crear Usuario IAM
&lt;/h2&gt;

&lt;h3&gt;
  
  
  2.1 - Ir a IAM
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;En la consola, busca &lt;strong&gt;"IAM"&lt;/strong&gt; en la barra superior&lt;/li&gt;
&lt;li&gt;Menú izquierdo → &lt;strong&gt;Users&lt;/strong&gt; → &lt;strong&gt;Create user&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  2.2 - Configurar Usuario
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;User name:&lt;/strong&gt; Tu nombre (ej: &lt;code&gt;juan-admin&lt;/code&gt;, &lt;code&gt;maria-dev&lt;/code&gt;)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Access type:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Marca: &lt;strong&gt;"Provide user access to the AWS Management Console"&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Console password:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Opción 1: &lt;strong&gt;Custom password&lt;/strong&gt; (crea una contraseña fuerte)&lt;/li&gt;
&lt;li&gt;Opción 2: &lt;strong&gt;Auto-generated&lt;/strong&gt; (AWS genera una aleatoria)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;IMPORTANTE:&lt;/strong&gt; Desmarca "Users must create a new password at next sign-in" (para simplificar)&lt;/p&gt;

&lt;h3&gt;
  
  
  2.3 - Asignar Permisos
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Para aprendizaje:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Selecciona &lt;strong&gt;"Attach policies directly"&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Busca y marca: &lt;strong&gt;&lt;code&gt;AdministratorAccess&lt;/code&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Para producción (después):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Usa permisos más granulares según el principio de "Least Privilege"&lt;/li&gt;
&lt;li&gt;Ejemplo: &lt;code&gt;AmazonS3FullAccess&lt;/code&gt;, &lt;code&gt;AWSLambdaFullAccess&lt;/code&gt;, etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2.4 - Revisar y Crear
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Clic &lt;strong&gt;Next&lt;/strong&gt; → &lt;strong&gt;Create user&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DESCARGA&lt;/strong&gt; el archivo CSV con credenciales&lt;/li&gt;
&lt;li&gt;Guarda este CSV en un &lt;strong&gt;gestor de contraseñas&lt;/strong&gt; (1Password, Bitwarden, etc.)&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  2.5 - Activar MFA para IAM User
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;En la lista de usuarios → Clic en tu usuario&lt;/li&gt;
&lt;li&gt;Pestaña &lt;strong&gt;"Security credentials"&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Sección &lt;strong&gt;"Multi-factor authentication (MFA)"&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Assign MFA device&lt;/strong&gt; → Mismo proceso que Root&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  2.6 - Iniciar sesión como IAM User
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Cierra sesión de Root&lt;/li&gt;
&lt;li&gt;Ve a: &lt;code&gt;https://TU-ALIAS.signin.aws.amazon.com&lt;/code&gt; (o usa el Account ID)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IAM user name:&lt;/strong&gt; &lt;code&gt;juan-admin&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Password:&lt;/strong&gt; La que configuraste&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MFA code:&lt;/strong&gt; Código actual de tu app&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Paso 3: Configurar Alertas de Facturación
&lt;/h2&gt;

&lt;h3&gt;
  
  
  3.1 - Habilitar Alertas (Requiere Root)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;⚠️ Solo este paso requiere Root temporalmente&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Inicia sesión como &lt;strong&gt;Root User&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Clic en tu nombre → &lt;strong&gt;Account&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Baja hasta &lt;strong&gt;"Billing preferences"&lt;/strong&gt; → &lt;strong&gt;Edit&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Activa:

&lt;ul&gt;
&lt;li&gt;✅ &lt;strong&gt;"Receive Free Tier Usage Alerts"&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;"Receive Billing Alerts"&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Introduce tu email → &lt;strong&gt;Save preferences&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cierra sesión de Root&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  3.2 - Crear Presupuesto ($0.01)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Ahora como IAM User:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Busca &lt;strong&gt;"AWS Budgets"&lt;/strong&gt; (o Billing → Budgets)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Create budget&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Template:&lt;/strong&gt; Selecciona &lt;strong&gt;"Zero spend budget"&lt;/strong&gt; (recomendado para empezar)

&lt;ul&gt;
&lt;li&gt;O usa &lt;strong&gt;"Customize"&lt;/strong&gt; para $0.01 exactos&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Si usas Customize:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Budget name:&lt;/strong&gt; &lt;code&gt;Alerta-Costo-Minimo&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Period:&lt;/strong&gt; Monthly&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Budgeted amount:&lt;/strong&gt; &lt;code&gt;0.01&lt;/code&gt; USD&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Budget scope:&lt;/strong&gt; All AWS services&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3.3 - Configurar Alertas del Presupuesto
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Alert 1 - Costo Real:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Threshold:&lt;/strong&gt; Actual costs - 100% (cuando alcances $0.01)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Email:&lt;/strong&gt; Tu email principal&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Alert 2 - Pronóstico (Opcional):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Threshold:&lt;/strong&gt; Forecasted costs - 80%&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Email:&lt;/strong&gt; Tu email&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3.4 - Free Tier Alert (Adicional)
&lt;/h3&gt;

&lt;p&gt;AWS enviará emails automáticamente cuando:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Estés cerca de superar límites del Free Tier&lt;/li&gt;
&lt;li&gt;Hayas superado un servicio del Free Tier&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Paso 4: Instalar AWS CLI
&lt;/h2&gt;

&lt;h3&gt;
  
  
  4.1 - Instalación según OS
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;macOS:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="s2"&gt;"https://awscli.amazonaws.com/AWSCLIV2.pkg"&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="s2"&gt;"AWSCLIV2.pkg"&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;installer &lt;span class="nt"&gt;-pkg&lt;/span&gt; AWSCLIV2.pkg &lt;span class="nt"&gt;-target&lt;/span&gt; /
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Linux:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="s2"&gt;"https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip"&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="s2"&gt;"awscliv2.zip"&lt;/span&gt;
unzip awscliv2.zip
&lt;span class="nb"&gt;sudo&lt;/span&gt; ./aws/install
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Windows:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Descarga: &lt;a href="https://awscli.amazonaws.com/AWSCLIV2.msi" rel="noopener noreferrer"&gt;https://awscli.amazonaws.com/AWSCLIV2.msi&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Ejecuta el instalador&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Verificar:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;span class="c"&gt;# Output esperado: aws-cli/2.x.x Python/3.x.x ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4.2 - Crear Access Keys
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;⚠️ SOLO para tu usuario IAM, NUNCA para Root&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Consola AWS (como IAM User) → &lt;strong&gt;IAM&lt;/strong&gt; → &lt;strong&gt;Users&lt;/strong&gt; → Tu usuario&lt;/li&gt;
&lt;li&gt;Pestaña &lt;strong&gt;"Security credentials"&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Sección &lt;strong&gt;"Access keys"&lt;/strong&gt; → &lt;strong&gt;Create access key&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use case:&lt;/strong&gt; &lt;strong&gt;Command Line Interface (CLI)&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Marca "I understand..." → &lt;strong&gt;Next&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;(Opcional) Description: &lt;code&gt;CLI-Local-DevMachine&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Create access key&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;¡DESCARGA EL CSV!&lt;/strong&gt; (Solo se muestra UNA vez)&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  4.3 - Configurar Perfil Local
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws configure &lt;span class="nt"&gt;--profile&lt;/span&gt; mi-proyecto
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Introduce:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;AWS Access Key ID:&lt;/strong&gt; &lt;code&gt;AKIA...&lt;/code&gt; (del CSV)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AWS Secret Access Key:&lt;/strong&gt; &lt;code&gt;wJalr...&lt;/code&gt; (del CSV)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Default region:&lt;/strong&gt; &lt;code&gt;us-east-1&lt;/code&gt; (recomendado para empezar)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Output format:&lt;/strong&gt; &lt;code&gt;json&lt;/code&gt; (o vacío)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;¿Dónde se guarda?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Linux/macOS: &lt;code&gt;~/.aws/credentials&lt;/code&gt; y &lt;code&gt;~/.aws/config&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Windows: &lt;code&gt;C:\Users\TuUsuario\.aws\credentials&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4.4 - Configurar Variable de Entorno
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Bash/Zsh:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'export AWS_PROFILE=mi-proyecto'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; ~/.zshrc
&lt;span class="nb"&gt;source&lt;/span&gt; ~/.zshrc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Fish:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;echo 'set -gx AWS_PROFILE mi-proyecto' &amp;gt;&amp;gt; ~/.config/fish/config.fish
source ~/.config/fish/config.fish
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Windows PowerShell:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;System.Environment&lt;/span&gt;&lt;span class="p"&gt;]::&lt;/span&gt;&lt;span class="n"&gt;SetEnvironmentVariable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'AWS_PROFILE'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'mi-proyecto'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'User'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4.5 - Probar Configuración
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws sts get-caller-identity
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Output esperado:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"UserId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"AIDAXXXXXXXXXX"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Account"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"123456789012"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Arn"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:iam::123456789012:user/juan-admin"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;✅ Si el &lt;code&gt;Arn&lt;/code&gt; contiene &lt;code&gt;:user/TuNombre&lt;/code&gt; → Todo correcto&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;❌ Si contiene &lt;code&gt;:root&lt;/code&gt; → Estás usando Root (MAL)&lt;/strong&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  Paso 5: Blindar tu Repositorio
&lt;/h2&gt;
&lt;h3&gt;
  
  
  5.1 - Crear &lt;code&gt;.gitignore&lt;/code&gt; (CRÍTICO)
&lt;/h3&gt;

&lt;p&gt;En la raíz de tu proyecto:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;touch&lt;/span&gt; .gitignore
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Contenido mínimo obligatorio:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# === SECRETOS AWS Y TERRAFORM ===
*.tfvars
*.tfstate
*.tfstate.*
.terraform/
.terraform.lock.hcl

# === CREDENCIALES ===
.env
.env.*
*.pem
*.key
aws_credentials.txt
credentials.json
config.json

# === GOLANG ===
*.exe
*.dll
*.so
*.dylib
*.test
*.out
vendor/
go.work

# === SISTEMA ===
.DS_Store
Thumbs.db

# === EDITORES ===
.vscode/
.idea/
*.swp
*.swo
*~
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5.2 - Verificar ANTES de commit
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git status
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;NUNCA debes ver:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Archivos &lt;code&gt;.tfstate&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Archivos &lt;code&gt;.env&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Archivos con extensión &lt;code&gt;.pem&lt;/code&gt; o &lt;code&gt;.key&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Directorios &lt;code&gt;.terraform/&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  5.3 - Agregar Pre-Commit Check (Opcional pero recomendado)
&lt;/h3&gt;

&lt;p&gt;Instala &lt;code&gt;pre-commit&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# macOS&lt;/span&gt;
brew &lt;span class="nb"&gt;install &lt;/span&gt;pre-commit

&lt;span class="c"&gt;# Linux&lt;/span&gt;
pip &lt;span class="nb"&gt;install &lt;/span&gt;pre-commit

&lt;span class="c"&gt;# Windows&lt;/span&gt;
pip &lt;span class="nb"&gt;install &lt;/span&gt;pre-commit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Crea &lt;code&gt;.pre-commit-config.yaml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;repos&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;repo&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://github.com/pre-commit/pre-commit-hooks&lt;/span&gt;
    &lt;span class="na"&gt;rev&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v4.5.0&lt;/span&gt;
    &lt;span class="na"&gt;hooks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;check-added-large-files&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;detect-aws-credentials&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;detect-private-key&lt;/span&gt;

  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;repo&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://github.com/antonbabenko/pre-commit-terraform&lt;/span&gt;
    &lt;span class="na"&gt;rev&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1.83.5&lt;/span&gt;
    &lt;span class="na"&gt;hooks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;terraform_fmt&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;terraform_validate&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Activar:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pre-commit &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Verificación Final
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Checklist de Seguridad
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Root User tiene MFA activado&lt;/li&gt;
&lt;li&gt;[ ] Root User NO se usa para trabajo diario&lt;/li&gt;
&lt;li&gt;[ ] IAM User creado con permisos AdministratorAccess&lt;/li&gt;
&lt;li&gt;[ ] IAM User tiene MFA activado&lt;/li&gt;
&lt;li&gt;[ ] AWS Budgets configurado ($0.01 o Zero Spend)&lt;/li&gt;
&lt;li&gt;[ ] Free Tier alerts habilitadas&lt;/li&gt;
&lt;li&gt;[ ] AWS CLI instalado y verificado&lt;/li&gt;
&lt;li&gt;[ ] Perfil de AWS CLI configurado (NO usa Root)&lt;/li&gt;
&lt;li&gt;[ ] &lt;code&gt;.gitignore&lt;/code&gt; creado y validado&lt;/li&gt;
&lt;li&gt;[ ] Ningún archivo sensible en &lt;code&gt;git status&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Test de Comando
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Debe mostrar tu usuario IAM&lt;/span&gt;
aws sts get-caller-identity

&lt;span class="c"&gt;# Debe listar buckets (vacío si no tienes ninguno)&lt;/span&gt;
aws s3 &lt;span class="nb"&gt;ls&lt;/span&gt;

&lt;span class="c"&gt;# Debe mostrar tu región configurada&lt;/span&gt;
aws configure get region
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Recursos Adicionales
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Documentación Oficial
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html" rel="noopener noreferrer"&gt;AWS IAM Best Practices&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://aws.amazon.com/free/" rel="noopener noreferrer"&gt;AWS Free Tier&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/cli/latest/userguide/" rel="noopener noreferrer"&gt;AWS CLI User Guide&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Herramientas Recomendadas
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Gestores de contraseñas:&lt;/strong&gt; 1Password, Bitwarden, LastPass&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MFA Apps:&lt;/strong&gt; Google Authenticator, Authy, Microsoft Authenticator&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Git Security:&lt;/strong&gt; git-secrets, pre-commit&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Límites del Free Tier (Primeros 12 meses)
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Servicio&lt;/th&gt;
&lt;th&gt;Límite Mensual&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;EC2&lt;/td&gt;
&lt;td&gt;750 horas de t2.micro&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;S3&lt;/td&gt;
&lt;td&gt;5GB de almacenamiento&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lambda&lt;/td&gt;
&lt;td&gt;1M de invocaciones&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DynamoDB&lt;/td&gt;
&lt;td&gt;25GB de almacenamiento&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RDS&lt;/td&gt;
&lt;td&gt;750 horas de db.t2.micro&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;⚠️ Servicios SIN Free Tier:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;NAT Gateway (~$32/mes)&lt;/li&gt;
&lt;li&gt;Load Balancers (~$16/mes)&lt;/li&gt;
&lt;li&gt;Elastic IP no asignada ($3.6/mes)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Próximos Pasos
&lt;/h2&gt;

&lt;p&gt;Una vez completado este módulo:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Módulo 1:&lt;/strong&gt; Terraform y Remote Backend (S3 + DynamoDB)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Módulo 2:&lt;/strong&gt; IAM Roles y Políticas avanzadas&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Módulo 3:&lt;/strong&gt; Desplegar aplicación Go con Lambda&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Preguntas Frecuentes
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Q: ¿Puedo usar Root User ocasionalmente?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
A: Solo para tareas que REQUIERAN Root (cambiar plan de soporte, cerrar cuenta). Día a día: NO.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: ¿Qué hago si pierdo acceso a MFA?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
A: Usa los códigos de recuperación que guardaste. Si los perdiste, contacta a AWS Support (puede tomar días).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: ¿Puedo tener múltiples usuarios IAM?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
A: Sí. Crea uno por persona/servicio con permisos específicos.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: ¿Qué hago si subí credenciales a GitHub por error?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
A: 1) Borra el commit inmediatamente, 2) Rota las credenciales en IAM, 3) Revisa CloudTrail por actividad sospechosa.&lt;/p&gt;




&lt;h2&gt;
  
  
  Contribuciones
&lt;/h2&gt;

&lt;p&gt;Si encuentras errores o mejoras, abre un Issue o Pull Request.&lt;/p&gt;




&lt;h2&gt;
  
  
  Licencia
&lt;/h2&gt;

&lt;p&gt;MIT License - Úsalo, compártelo, mejóralo.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Creado por:&lt;/strong&gt; Edgar (Homz) Macias&lt;br&gt;
&lt;strong&gt;Repositorio:&lt;/strong&gt; &lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
