<?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: olcortesb</title>
    <description>The latest articles on DEV Community by olcortesb (@olcortesb).</description>
    <link>https://dev.to/olcortesb</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%2F1180225%2F9d212134-1619-45a6-9794-ee1aee8d7a6b.png</url>
      <title>DEV Community: olcortesb</title>
      <link>https://dev.to/olcortesb</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/olcortesb"/>
    <language>en</language>
    <item>
      <title>⚙️ Control PID con AWS Lambda Durable Functions: Simulando un PID y un reactor ⚗️ con estado persistente ☁️</title>
      <dc:creator>olcortesb</dc:creator>
      <pubDate>Tue, 10 Feb 2026 09:04:24 +0000</pubDate>
      <link>https://dev.to/aws-espanol/control-pid-con-aws-lambda-durable-functions-simulando-un-pid-y-un-reactor-con-estado-25op</link>
      <guid>https://dev.to/aws-espanol/control-pid-con-aws-lambda-durable-functions-simulando-un-pid-y-un-reactor-con-estado-25op</guid>
      <description>&lt;h2&gt;
  
  
  Introducción
&lt;/h2&gt;

&lt;p&gt;Recordando conceptos básicos de mis años de Ing. Electrónica y cómo ha evolucionado, pensaba en cómo sería la implementación de un controlador PID completamente cloud, al menos a modo práctico, en la operativa posiblemente no sea aplicable a todos los sistemas de control. Los controladores PID (Proporcional-Integral-Derivativo) tradicionalmente requieren ejecución continua, estado persistente y ciclos de control precisos. ¿Cómo implementar esto en un entorno serverless donde las funciones son efímeras por naturaleza?&lt;/p&gt;

&lt;p&gt;En este artículo presento una prueba de concepto que combina &lt;strong&gt;control PID&lt;/strong&gt; con &lt;strong&gt;AWS Lambda Durable Functions&lt;/strong&gt;, demostrando cómo crear sistemas de control de larga duración sin "pagar" por tiempo de espera entre iteraciones. La implementación simula un reactor con control de temperatura, utilizando las nuevas capacidades de Lambda para workflows con estado persistente.&lt;/p&gt;

&lt;h2&gt;
  
  
  🎯 ¿Qué es un Controlador PID?
&lt;/h2&gt;

&lt;p&gt;Un controlador PID es un mecanismo de control por retroalimentación ampliamente utilizado en sistemas industriales. Calcula una señal de control basándose en tres elementos:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Proporcional (P)&lt;/strong&gt;: Responde proporcionalmente al error actual entre el setpoint (SP) y el valor del proceso (PV). Cuanto mayor es el error, mayor es la acción correctiva&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Integral (I)&lt;/strong&gt;: Corrige el error acumulado en el tiempo, eliminando el error residual en estado estacionario&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Derivativo (D)&lt;/strong&gt;: Anticipa cambios futuros basándose en la tasa de cambio del error, proporcionando amortiguación al sistema&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;La fórmula básica del PID es:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CV(t) = Kp·e(t) + Ki·∫e(t)dt + Kd·de(t)/dt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Donde:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;CV&lt;/code&gt;: Control Variable o salida del controlador normalmente en porcentaje (0-100%)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;e(t)&lt;/code&gt;: Error = SP - PV (Setpoint - Process Variable)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;SP&lt;/code&gt;: Setpoint o valor deseado&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;PV&lt;/code&gt;: Process Variable o valor actual medido&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Kp, Ki, Kd&lt;/code&gt;: Constantes de ajuste o ganancias del controlador&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Tipos de respuesta:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;La relación entre las constantes y su aplicación sobre distintos sistemas puede producir tres tipos de respuestas conocidas: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Subamortiguada (underdamped)&lt;/strong&gt;: Oscila antes de estabilizarse (overshoot + oscilaciones)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Críticamente amortiguada (critically damped)&lt;/strong&gt;: Converge lo más rápido posible sin overshoot&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sobreamortiguada (overdamped)&lt;/strong&gt;: Converge lentamente sin overshoot&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🚀 ¿Qué son AWS Lambda Durable Functions?
&lt;/h2&gt;

&lt;p&gt;AWS Lambda Durable Functions es una característica nativa que permite crear workflows de larga duración con estado persistente. A diferencia de las funciones Lambda tradicionales, las Durable Functions pueden:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Suspenderse sin costo&lt;/strong&gt;: &lt;code&gt;context.wait()&lt;/code&gt; pausa la ejecución sin cargos de cómputo&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mantener estado&lt;/strong&gt;: Checkpointing automático en cada step&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Recuperarse de fallos&lt;/strong&gt;: Reinicia desde el último checkpoint exitoso&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ejecutar por horas/días&lt;/strong&gt;: Sin mantener la Lambda activa continuamente&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Referencia oficial&lt;/strong&gt;: &lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/durable-getting-started.html" rel="noopener noreferrer"&gt;AWS Lambda Durable Functions&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Primitivas Core del SDK
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;aws_durable_execution_sdk_python&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;DurableContext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;durable_execution&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;durable_step&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;aws_durable_execution_sdk_python.config&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Duration&lt;/span&gt;

&lt;span class="nd"&gt;@durable_step&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;my_step&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;step_context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;arg&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# Lógica con checkpointing automático
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;

&lt;span class="nd"&gt;@durable_execution&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;lambda_handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&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;DurableContext&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;result&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="nf"&gt;step&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;my_step&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arg&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wait&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_seconds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;  &lt;span class="c1"&gt;# SIN COSTO
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🏗️ Arquitectura de la POC
&lt;/h2&gt;

&lt;p&gt;Bueno, si has llegado leyendo hasta aquí ya es hora de jugarnos, construir e ir a la sustancia. Vamos a implementar una POC que nos permita simular en Lambda Durable Functions tanto un PID como un reactor, persistiendo sus estados internamente y gestionando el flujo con las primitivas del SDK.&lt;/p&gt;

&lt;p&gt;El sistema sería el que se muestra en la imagen a continuación.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F30d8dnmijx5h8w41pme4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F30d8dnmijx5h8w41pme4.png" alt="Arquitectura con Lambda Durable Functions" width="800" height="741"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Bien, ahora, si tuviéramos que simular esto con la versión tradicional de Lambda, necesitaríamos un par de SQS al menos para gestionar el estado y la invocación de las lambdas. La implementación que proponemos sigue este flujo:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk18seccchfv99ki06qet.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk18seccchfv99ki06qet.png" alt="Arquitectura tradicional con SQS" width="800" height="383"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Componentes principales:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;API Gateway&lt;/strong&gt;: Recibe el setpoint deseado&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PID Controller Lambda&lt;/strong&gt;: Ejecuta el loop de control con estado persistente&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reactor Simulator Lambda&lt;/strong&gt;: Simula el comportamiento físico del reactor&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CloudWatch Metrics&lt;/strong&gt;: Almacena métricas para visualización&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  🛠️ Implementación
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Lambda PID Controller
&lt;/h3&gt;

&lt;p&gt;El controlador implementa el algoritmo PID completo usando Durable Functions. El código completo está disponible en el repositorio:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;📁 Código fuente:&lt;/strong&gt; &lt;a href="https://github.com/olcortesb/PID-control-with-lambda-durable/blob/main/terraform/src/pid_controller/app.py" rel="noopener noreferrer"&gt;&lt;code&gt;terraform/src/pid_controller/app.py&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Componentes principales:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;@durable_step calculate_pid()&lt;/code&gt;&lt;/strong&gt;: Implementa el algoritmo PID en forma paralela discreta&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;@durable_step invoke_reactor()&lt;/code&gt;&lt;/strong&gt;: Invoca la Lambda del reactor para obtener la nueva temperatura&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;@durable_step publish_metrics()&lt;/code&gt;&lt;/strong&gt;: Publica métricas a CloudWatch (SetpointTemperature, ActualTemperature, TemperatureError)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;@durable_execution lambda_handler()&lt;/code&gt;&lt;/strong&gt;: Orquesta el loop de control con &lt;code&gt;context.step()&lt;/code&gt; y &lt;code&gt;context.wait()&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Ecuación PID implementada:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;setpoint&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;current_temp&lt;/span&gt;
&lt;span class="n"&gt;integral&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;SAMPLE_TIME&lt;/span&gt;
&lt;span class="n"&gt;derivative&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;last_error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;SAMPLE_TIME&lt;/span&gt;
&lt;span class="n"&gt;cv&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;KP&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;KI&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;integral&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;KD&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;derivative&lt;/span&gt;
&lt;span class="n"&gt;cv&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cv&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;  &lt;span class="c1"&gt;# Limitar entre 0-100
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Lambda Reactor Simulator
&lt;/h3&gt;

&lt;p&gt;El simulador implementa un modelo físico simplificado del reactor. El código completo está disponible en el repositorio:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;📁 Código fuente:&lt;/strong&gt; &lt;a href="https://github.com/olcortesb/PID-control-with-lambda-durable/blob/main/terraform/src/reactor_simulator/app.py" rel="noopener noreferrer"&gt;&lt;code&gt;terraform/src/reactor_simulator/app.py&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Componentes principales:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;@durable_step simulate_reactor_step()&lt;/code&gt;&lt;/strong&gt;: Simula el comportamiento térmico del reactor&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;@durable_execution lambda_handler()&lt;/code&gt;&lt;/strong&gt;: Recibe el control value y retorna la nueva temperatura&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Física del reactor implementada:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Enfriamiento natural (Ley de enfriamiento de Newton)
&lt;/span&gt;&lt;span class="n"&gt;cooling&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;current_temp&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;AMBIENT_TEMP&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;COOLING_RATE&lt;/span&gt;

&lt;span class="c1"&gt;# Calentamiento por control value (0-100)
&lt;/span&gt;&lt;span class="n"&gt;heating&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;control_value&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;HEATING_EFFICIENCY&lt;/span&gt;

&lt;span class="c1"&gt;# Cambio de temperatura con inercia térmica
&lt;/span&gt;&lt;span class="n"&gt;temp_change&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;heating&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;cooling&lt;/span&gt;
&lt;span class="n"&gt;new_temp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;current_temp&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;temp_change&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;THERMAL_INERTIA&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🚀 Despliegue con Terraform
&lt;/h2&gt;

&lt;p&gt;Inicialmente el despliegue lo realizaría con AWS SAM, sin embargo encontré algunos issues con la integración del SDK con AWS SAM. La alternativa era agregarle una lambda layer, pero no quería probar Lambda Durable fuera de la configuración más básica, por lo tanto me mudé a Terraform para el despliegue.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configuración de Lambda Durable
&lt;/h3&gt;

&lt;p&gt;En Terraform, las funciones durable requieren configuración específica:&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_function"&lt;/span&gt; &lt;span class="s2"&gt;"pid_controller"&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="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;archive_file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pid_controller&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;output_path&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;"${var.project_name}-pid-controller"&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;pid_controller&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;"app.lambda_handler"&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;"python3.13"&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;900&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;publish&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;durable_config&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;execution_timeout&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3600&lt;/span&gt;  &lt;span class="c1"&gt;# 1 hora&lt;/span&gt;
    &lt;span class="nx"&gt;retention_period&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;     &lt;span class="c1"&gt;# 7 días&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;logging_config&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;log_format&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"JSON"&lt;/span&gt;
    &lt;span class="nx"&gt;log_group&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_cloudwatch_log_group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pid_controller&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="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;REACTOR_FUNCTION_NAME&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${aws_lambda_function.reactor_simulator.function_name}:prod"&lt;/span&gt;
      &lt;span class="nx"&gt;KP&lt;/span&gt;                    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"0.50"&lt;/span&gt;
      &lt;span class="nx"&gt;KI&lt;/span&gt;                    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"0.0004"&lt;/span&gt;
      &lt;span class="nx"&gt;KD&lt;/span&gt;                    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"0.20"&lt;/span&gt;
      &lt;span class="nx"&gt;SAMPLE_TIME&lt;/span&gt;           &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"60"&lt;/span&gt;
      &lt;span class="nx"&gt;MAX_ITERATIONS&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"40"&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;h3&gt;
  
  
  IAM Policy para Durable Functions
&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_iam_role_policy_attachment"&lt;/span&gt; &lt;span class="s2"&gt;"pid_controller_durable"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;policy_arn&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"arn:aws:iam::aws:policy/service-role/AWSLambdaBasicDurableExecutionRolePolicy"&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;pid_controller&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🧪 Pruebas y Simulación
&lt;/h2&gt;

&lt;p&gt;Cómo lo probamos: dejamos un valor esperado para que el PID use como referencia realizando un CURL al API Gateway y empezamos a ver cómo se comporta el sistema.&lt;/p&gt;

&lt;h3&gt;
  
  
  Iniciar Control PID
&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; POST https://xxxxx.execute-api.us-east-1.amazonaws.com/prod/setpoint &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&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;'{"setpoint": 75.0}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Respuesta:&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;"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;"PID control completed - 40 iterations"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"final_temperature"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;74.82&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"setpoint"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;75.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"iterations"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;40&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;h3&gt;
  
  
  ¿Cómo se ven los Steps?
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnzfbaqs8eirsuhsun4c8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnzfbaqs8eirsuhsun4c8.png" alt="Ejecuciones Lambda Durable" width="800" height="315"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Una nueva pestaña (1) nos mostrará cómo se ven las ejecuciones de toda la Lambda Durable Function, indicando qué versión (2) está corriendo, indicando un ID (3) y el estado de la ejecución (4).&lt;/p&gt;

&lt;h3&gt;
  
  
  La relación de las máquinas de estado de las dos lambdas
&lt;/h3&gt;

&lt;p&gt;Una de las features más interesantes es que podemos aprovechar la persistencia del estado evitando colocar SQS, DynamoDB, S3 dependiendo del caso, y ahora podemos llamar a otra función directamente gracias a la gestión de la máquina de estado. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7yooxrxenp1disp4zhbq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7yooxrxenp1disp4zhbq.png" alt="Interacciones entre steps y waits" width="800" height="324"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;En la imagen arriba, 1 y 2 muestran las interacciones entre los steps y los waits, que indicamos antes en el presente artículo. Lo interesante es que se ve claramente el invoke_reactor que para nuestra simulación es invocar a otra Lambda Durable Function que permitirá simular el estado del reactor, guardando la temperatura y la inercia térmica del mismo.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqfsuozqplfvxwzdu72lm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqfsuozqplfvxwzdu72lm.png" alt="Ejecución del reactor simulator" width="800" height="222"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ya dentro de la Lambda del reactor podemos ver cómo se ejecutan los pasos que recalculan el comportamiento del reactor respecto a la temperatura, simulando el comportamiento real de un reactor.&lt;/p&gt;

&lt;h3&gt;
  
  
  La respuesta final:
&lt;/h3&gt;

&lt;p&gt;Para visualizar de manera aproximada cómo se estudian las gráficas de los sistemas PID, creé un dashboard que pueden consultar en el código fuente:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/olcortesb/PID-control-with-lambda-durable/blob/main/terraform/dashboard.tf" rel="noopener noreferrer"&gt;Código en Terraform del dashboard&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6vfwf88297ilbz5v8cj5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6vfwf88297ilbz5v8cj5.png" alt="Dashboard CloudWatch" width="800" height="387"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Los valores configurados son para obtener una respuesta críticamente amortiguada que sería el objetivo a perseguir para un sistema de control tipo PID. Los valores los encontré realizando una simulación en Python que muestro a continuación. &lt;/p&gt;

&lt;h3&gt;
  
  
  Simulación Local
&lt;/h3&gt;

&lt;p&gt;Sí, esto podría ser otro post, pero he disfrutado tanto haciendo este artículo que lo dejo por aquí. Como quería refrescar cómo funcionaba el PID, me creé un proyecto que es básicamente un simulador Python para probar diferentes configuraciones de PID. En base a esta simulación encontré los valores que colocamos al sistema simulado en la POC.&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;cd &lt;/span&gt;simulation

&lt;span class="c"&gt;# Configurar parámetros en .env&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; .env &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;
KP=0.50
KI=0.0004
KD=0.20
SETPOINT=75.0
SAMPLE_TIME=30
MAX_ITERATIONS=40
THERMAL_INERTIA=0.18
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;&lt;span class="c"&gt;# Ejecutar simulación&lt;/span&gt;
python simulate_pid.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Resultados de Simulación
&lt;/h3&gt;

&lt;p&gt;El simulador genera gráficas mostrando el comportamiento del sistema con diferentes configuraciones de PID. Todas las simulaciones utilizan:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Setpoint&lt;/strong&gt;: 75°C&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Temperatura inicial&lt;/strong&gt;: 20°C&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sample time&lt;/strong&gt;: 30 segundos&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Iteraciones&lt;/strong&gt;: 40&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Inercia térmica&lt;/strong&gt;: 0.18&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Configuración 1: Kp=0.20, Ki=0.0001, Kd=0.30 (Sobreamortiguada)
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Folcortesb%2Fpost-article-backup%2Frefs%2Fheads%2Fmain%2Fimages%2Fpid_kp0.20_ki0.0001_kd0.30_ti0.18_st30_iter40_sp75.0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Folcortesb%2Fpost-article-backup%2Frefs%2Fheads%2Fmain%2Fimages%2Fpid_kp0.20_ki0.0001_kd0.30_ti0.18_st30_iter40_sp75.0.png" alt="Respuesta Sobreamortiguada" width="800" height="454"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Convergencia lenta y suave sin overshoot&lt;/li&gt;
&lt;li&gt;Tiempo de estabilización: ~20 minutos&lt;/li&gt;
&lt;li&gt;Temperatura final: ~74.5°C&lt;/li&gt;
&lt;li&gt;Error final: ~0.5°C&lt;/li&gt;
&lt;li&gt;Control Value máximo: ~15%&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Comportamiento&lt;/strong&gt;: Sistema muy conservador, ideal cuando no se permiten sobrepasadas&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Configuración 2: Kp=0.50, Ki=0.0004, Kd=0.20 (Críticamente amortiguada)
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Folcortesb%2Fpost-article-backup%2Frefs%2Fheads%2Fmain%2Fimages%2Fpid_kp0.50_ki0.0004_kd0.20_ti0.18_st30_iter40_sp75.0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Folcortesb%2Fpost-article-backup%2Frefs%2Fheads%2Fmain%2Fimages%2Fpid_kp0.50_ki0.0004_kd0.20_ti0.18_st30_iter40_sp75.0.png" alt="Respuesta Críticamente Amortiguada" width="800" height="454"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Convergencia rápida sin overshoot significativo&lt;/li&gt;
&lt;li&gt;Tiempo de estabilización: ~15 minutos&lt;/li&gt;
&lt;li&gt;Temperatura final: ~74.8°C&lt;/li&gt;
&lt;li&gt;Error final: ~0.2°C&lt;/li&gt;
&lt;li&gt;Control Value máximo: ~30%&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Comportamiento&lt;/strong&gt;: Balance óptimo entre velocidad y estabilidad&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Configuración 3: Kp=1.2, Ki=0.002, Kd=0.1 (Subamortiguada)
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Folcortesb%2Fpost-article-backup%2Frefs%2Fheads%2Fmain%2Fimages%2Fpid_kp1.2_ki0.002_kd0.1_ti0.18_st30_iter40_sp75.0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Folcortesb%2Fpost-article-backup%2Frefs%2Fheads%2Fmain%2Fimages%2Fpid_kp1.2_ki0.002_kd0.1_ti0.18_st30_iter40_sp75.0.png" alt="Respuesta Subamortiguada" width="800" height="454"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Convergencia muy rápida con overshoot&lt;/li&gt;
&lt;li&gt;Overshoot máximo: ~2°C (alcanza ~77°C)&lt;/li&gt;
&lt;li&gt;Oscilaciones visibles antes de estabilizar&lt;/li&gt;
&lt;li&gt;Tiempo de estabilización: ~12 minutos&lt;/li&gt;
&lt;li&gt;Temperatura final: ~75.0°C&lt;/li&gt;
&lt;li&gt;Error final: ~0.05°C&lt;/li&gt;
&lt;li&gt;Control Value máximo: ~70%&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Comportamiento&lt;/strong&gt;: Sistema agresivo, respuesta rápida pero con oscilaciones&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  📊 Verificación de Resultados
&lt;/h2&gt;

&lt;h3&gt;
  
  
  CloudWatch Logs
&lt;/h3&gt;

&lt;p&gt;Los logs muestran cada iteración del control:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "timestamp": "2026-01-15T10:30:45.123Z",
  "level": "INFO",
  "message": "Iteration 15: Setpoint=75.00, Temp=72.34, CV=45.67"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  CloudWatch Metrics
&lt;/h3&gt;

&lt;p&gt;Las métricas publicadas permiten visualizar:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;SetpointTemperature&lt;/strong&gt;: Temperatura deseada (constante)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ActualTemperature&lt;/strong&gt;: Temperatura real del reactor&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TemperatureError&lt;/strong&gt;: Error absoluto&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Para visualizar en CloudWatch:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Ir a CloudWatch Console → Metrics&lt;/li&gt;
&lt;li&gt;Buscar namespace "PIDControl-v2"&lt;/li&gt;
&lt;li&gt;Crear dashboard con las tres métricas&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Logs del Reactor
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "timestamp": "2026-01-15T10:30:45.456Z",
  "level": "INFO",
  "message": "Reactor: CV=45.67, Temp=72.34, Ambient=20.0"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  💡 Ventajas de Lambda Durable Functions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ✅ Sin Costo Durante Esperas
&lt;/h3&gt;

&lt;p&gt;El uso de &lt;code&gt;context.wait()&lt;/code&gt; es una de las características más poderosas de Lambda Durable Functions. Según la &lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/durable-functions-how-it-works.html" rel="noopener noreferrer"&gt;documentación oficial de AWS&lt;/a&gt;, cuando una función durable está en estado de espera usando &lt;code&gt;context.wait()&lt;/code&gt;, &lt;strong&gt;no se cobra por tiempo de cómputo&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Antes: Lambda ejecutándose = $$$
&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# Pagas por 60 segundos de ejecución
&lt;/span&gt;
&lt;span class="c1"&gt;# Ahora: Lambda suspendida = $0
&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wait&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_seconds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;  &lt;span class="c1"&gt;# Sin cargo durante la espera
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Ejemplo de ahorro de costos&lt;/strong&gt; para el caso de nuestro control PID con SAMPLE_TIME de 60 segundos y 40 iteraciones:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Sin Durable&lt;/strong&gt;: 40 minutos de ejecución continua = 2,400 segundos facturables&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Con Durable&lt;/strong&gt;: ~5 segundos de ejecución real (solo cómputo activo) = 5 segundos facturables&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ahorro&lt;/strong&gt;: ~99.8% en costos de cómputo&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Referencia&lt;/strong&gt;: &lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/durable-functions-how-it-works.html" rel="noopener noreferrer"&gt;How Lambda Durable Functions work - AWS Documentation&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Checkpointing Automático
&lt;/h3&gt;

&lt;p&gt;Cada &lt;code&gt;context.step()&lt;/code&gt; guarda el estado:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Si falla aquí, reinicia desde el último step exitoso
&lt;/span&gt;&lt;span class="n"&gt;pid_result&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="nf"&gt;step&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;calculate_pid&lt;/span&gt;&lt;span class="p"&gt;(...))&lt;/span&gt;  &lt;span class="c1"&gt;# ✓ Checkpoint
&lt;/span&gt;&lt;span class="n"&gt;reactor_data&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="nf"&gt;step&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;invoke_reactor&lt;/span&gt;&lt;span class="p"&gt;(...))&lt;/span&gt;  &lt;span class="c1"&gt;# ✓ Checkpoint
&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;step&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;publish_metrics&lt;/span&gt;&lt;span class="p"&gt;(...))&lt;/span&gt;  &lt;span class="c1"&gt;# ✓ Checkpoint
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Sin Infraestructura Extra
&lt;/h3&gt;

&lt;p&gt;Comparación con arquitecturas tradicionales:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sin Durable Functions:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;DynamoDB para estado&lt;/li&gt;
&lt;li&gt;SQS para coordinación&lt;/li&gt;
&lt;li&gt;EventBridge para scheduling&lt;/li&gt;
&lt;li&gt;Step Functions para workflow&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Con Durable Functions:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Solo Lambda (estado incluido)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Código Más Simple
&lt;/h3&gt;

&lt;p&gt;El código es más legible y mantenible:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Loop natural con estado persistente
&lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;iteration&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MAX_ITERATIONS&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;pid_result&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="nf"&gt;step&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;calculate_pid&lt;/span&gt;&lt;span class="p"&gt;(...))&lt;/span&gt;
    &lt;span class="n"&gt;reactor_data&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="nf"&gt;step&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;invoke_reactor&lt;/span&gt;&lt;span class="p"&gt;(...))&lt;/span&gt;
    &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;step&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;publish_metrics&lt;/span&gt;&lt;span class="p"&gt;(...))&lt;/span&gt;
    &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wait&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_seconds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SAMPLE_TIME&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  📋 Casos de Uso Prácticos
&lt;/h2&gt;

&lt;p&gt;Según el &lt;a href="https://aws.amazon.com/es/blogs/aws/build-multi-step-applications-and-ai-workflows-with-aws-lambda-durable-functions/" rel="noopener noreferrer"&gt;blog oficial de AWS&lt;/a&gt; y la &lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/durable-functions-use-cases.html" rel="noopener noreferrer"&gt;documentación técnica&lt;/a&gt;, Lambda Durable Functions es especialmente útil para escenarios donde se requieren workflows de larga duración con estado persistente. Los casos de uso documentados incluyen: &lt;strong&gt;workflows de aprobación humana&lt;/strong&gt; donde los procesos esperan aprobaciones sin mantener recursos activos; &lt;strong&gt;procesamiento de datos por lotes&lt;/strong&gt; como ETL jobs que esperan entre etapas; &lt;strong&gt;orquestación de microservicios&lt;/strong&gt; para coordinar múltiples servicios con estado; &lt;strong&gt;monitoreo y polling&lt;/strong&gt; para sistemas que verifican condiciones periódicamente; y &lt;strong&gt;workflows de IA y ML&lt;/strong&gt; para pipelines de entrenamiento y procesamiento de modelos. Estos patrones aprovechan la capacidad de &lt;code&gt;context.wait()&lt;/code&gt; para suspender la ejecución sin cargos de cómputo, haciendo viable económicamente mantener workflows activos durante períodos prolongados.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Referencias&lt;/strong&gt;: &lt;a href="https://aws.amazon.com/es/blogs/aws/build-multi-step-applications-and-ai-workflows-with-aws-lambda-durable-functions/" rel="noopener noreferrer"&gt;AWS Blog - Lambda Durable Functions&lt;/a&gt; | &lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/durable-functions-use-cases.html" rel="noopener noreferrer"&gt;Use Cases Documentation&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  ⚠️ Consideraciones Importantes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Límites del Servicio
&lt;/h3&gt;

&lt;p&gt;Según la &lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/durable-functions-quotas.html" rel="noopener noreferrer"&gt;documentación oficial de AWS Lambda Durable Functions&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Execution timeout&lt;/strong&gt;: Sin límite de tiempo total de ejecución (la función puede ejecutarse indefinidamente usando &lt;code&gt;context.wait()&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Individual function timeout&lt;/strong&gt;: Máximo 15 minutos por invocación individual de función&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Retention period&lt;/strong&gt;: Máximo 1 año (365 días) para estado persistente&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Payload size&lt;/strong&gt;: Máximo 256 KB por step&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Maximum concurrent executions&lt;/strong&gt;: Sujeto a las cuotas de concurrencia de Lambda de la cuenta&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Nota importante&lt;/strong&gt;: Aunque no hay límite en el tiempo total de ejecución durable, cada invocación individual de la función Lambda está limitada a 15 minutos. El estado se persiste automáticamente entre invocaciones.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Mejores Prácticas
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Usar steps granulares&lt;/strong&gt;: Cada step debe ser una unidad lógica de trabajo&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Limitar payload&lt;/strong&gt;: Pasar solo datos necesarios entre steps&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Implementar timeouts&lt;/strong&gt;: Configurar timeouts apropiados para cada función&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Monitorear métricas&lt;/strong&gt;: Usar CloudWatch para detectar problemas&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Manejar errores&lt;/strong&gt;: Implementar retry logic en steps críticos&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Cuándo NO usar Durable Functions
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Latencia ultra-baja&lt;/strong&gt;: Si necesitas respuesta en milisegundos&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Alto throughput&lt;/strong&gt;: Para miles de ejecuciones concurrentes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Workflows complejos&lt;/strong&gt;: Step Functions puede ser mejor opción&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Estado muy grande&lt;/strong&gt;: Si el estado excede 256 KB&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  📊 Conclusiones (Para nuestro caso de prueba PID)
&lt;/h2&gt;

&lt;p&gt;Esta POC intente validar que es posible implementar un sistema de control PID en arquitecturas serverless usando AWS Lambda Durable Functions. y lo que podemos identificar como concluciones, comparado con el uso de AWS lambda tradicional sin 'Durable Function' seria:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Economía&lt;/strong&gt;: &lt;code&gt;context.wait()&lt;/code&gt; elimina costos de tiempo de espera entre iteraciones&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Simplicidad&lt;/strong&gt;: Código más limpio sin necesidad de DynamoDB, SQS o Step Functions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Resiliencia&lt;/strong&gt;: Checkpointing automático permite recuperación ante fallos&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Flexibilidad&lt;/strong&gt;: Fácil ajuste de parámetros PID y configuración del reactor&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Escalabilidad&lt;/strong&gt;: Cada control PID es independiente y puede escalar horizontalmente&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Limitaciones identificadas:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Latencia&lt;/strong&gt;: No apto para control en tiempo real (&amp;lt; 1 segundo)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cold starts&lt;/strong&gt;: Primera invocación puede tardar 2-3 segundos&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Debugging&lt;/strong&gt;: Más complejo que funciones Lambda tradicionales&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Finalmente el código completo está disponible en &lt;a href="https://github.com/olcortesb/PID-control-with-lambda-durable" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  🔗 Referencias y Enlaces Útiles
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/durable-functions-quotas.html" rel="noopener noreferrer"&gt;AWS Lambda Durable Functions - Quotas&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://aws.amazon.com/es/blogs/aws/build-multi-step-applications-and-ai-workflows-with-aws-lambda-durable-functions/" rel="noopener noreferrer"&gt;AWS Lambda Durable Functions - Blog Oficial&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/durable-getting-started.html" rel="noopener noreferrer"&gt;Documentación Oficial - Getting Started&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://pypi.org/project/aws-durable-execution-sdk-python/" rel="noopener noreferrer"&gt;SDK Python - PyPI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/aws/developing-aws-lambda-durable-functions-with-aws-sam-ga9"&gt;Developing with SAM&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://es.wikipedia.org/wiki/Controlador_PID" rel="noopener noreferrer"&gt;Control PID - Wikipedia&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.fujielectric.fr/es/blog/regulacion-pid-para-dummies-todo-lo-que-necesita-saber/" rel="noopener noreferrer"&gt;Regulación PID para Dummies - Fuji Electric&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lambda_function" rel="noopener noreferrer"&gt;Terraform AWS Lambda Resources&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Gracias por leer.&lt;/p&gt;

&lt;p&gt;¡Saludos!&lt;/p&gt;

&lt;p&gt;Oscar Cortés&lt;/p&gt;

</description>
      <category>aws</category>
      <category>durablefunction</category>
      <category>pid</category>
    </item>
    <item>
      <title>Agregando reactividad ⚡ a nuestras arquitecturas con AWS CloudWatch Subscription Filter</title>
      <dc:creator>olcortesb</dc:creator>
      <pubDate>Wed, 21 Jan 2026 09:04:40 +0000</pubDate>
      <link>https://dev.to/aws-espanol/agregando-reactividad-a-nuestras-arquitecturas-con-aws-cloudwatch-subscription-filter-27da</link>
      <guid>https://dev.to/aws-espanol/agregando-reactividad-a-nuestras-arquitecturas-con-aws-cloudwatch-subscription-filter-27da</guid>
      <description>&lt;h2&gt;
  
  
  Introducción
&lt;/h2&gt;

&lt;p&gt;Uno de los principales desafíos al implementar sistemas basados en eventos es lograr la &lt;strong&gt;reactividad&lt;/strong&gt; necesaria para que respondan automáticamente ante eventos específicos. Cuando integramos sistemas tradicionales de request-response con arquitecturas gestionadas por eventos, siempre surge la necesidad de agregar esta capacidad reactiva.&lt;/p&gt;

&lt;p&gt;En esta búsqueda constante de soluciones robustas para agregar reactividad a los sistemas, hoy presento una prueba de concepto utilizando una de esas &lt;em&gt;features&lt;/em&gt; que están "escondidas" dentro de los servicios de AWS. Exploraremos &lt;strong&gt;AWS CloudWatch Subscription Filter&lt;/strong&gt; como una herramienta poderosa para agregar reactividad automática a nuestros sistemas. &lt;/p&gt;

&lt;p&gt;La intención de esta &lt;strong&gt;POC&lt;/strong&gt; (Proof of Concept) es utilizar &lt;strong&gt;AWS CloudWatch Subscription Filter&lt;/strong&gt; para procesar logs automáticamente y ejecutar acciones basadas en el contenido de los mismos. Crearemos una arquitectura serverless que captura logs específicos y los procesa en tiempo real.&lt;/p&gt;

&lt;h2&gt;
  
  
  🚀 ¿Qué es AWS CloudWatch Subscription Filter?
&lt;/h2&gt;

&lt;p&gt;AWS CloudWatch Subscription Filter es una &lt;em&gt;feature&lt;/em&gt; que permite &lt;strong&gt;filtrar y procesar logs en "tiempo real"&lt;/strong&gt; desde CloudWatch Logs. Cuando los logs coinciden con un patrón específico, el filtro puede enviar automáticamente esos datos a destinos como Lambda, Kinesis Data Streams, o Amazon Data Firehose. En este &lt;em&gt;post&lt;/em&gt; vamos a explorar la integración con AWS Lambda específicamente.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/SubscriptionFilters.html" rel="noopener noreferrer"&gt;AWS CloudWatch Logs Subscription Filters&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  🏗️ Arquitectura de la POC
&lt;/h2&gt;

&lt;p&gt;Nuestra implementación sigue este flujo:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;API Gateway&lt;/strong&gt; → Recibe POST requests&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lambda Controller&lt;/strong&gt; → Procesa el POST y loggea siempre&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CloudWatch Subscription Filter&lt;/strong&gt; → Captura logs con &lt;code&gt;enable=true&lt;/code&gt; automáticamente&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lambda Processor&lt;/strong&gt; → Procesa logs capturados y guarda en &lt;strong&gt;DynamoDB&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fngdm7q5vsh9e5l3znf6q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fngdm7q5vsh9e5l3znf6q.png" alt="Architecture" width="721" height="221"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  🛠️ Implementación
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Lambda Controller
&lt;/h3&gt;

&lt;p&gt;La Lambda Controller recibe los POST requests y genera logs específicos que serán capturados por el subscription filter. Aquí es importante anotar que esta lambda podría ser cualquier log de nuestro sistema tradicional o reactivo que tenga la capacidad de escribir logs en CloudWatch.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;try&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="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;body&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

        &lt;span class="c1"&gt;# Log específico para el subscription filter
&lt;/span&gt;        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;enable&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;✅ ORIGINAL_POST: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;response_message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;POST logged - subscription filter will process it&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;❌ POST received but enable=false: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;response_message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;POST received but enable=false, not processed&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;statusCode&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;body&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
                &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;response_message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;received_data&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;timestamp&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;utcnow&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;isoformat&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;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Error processing request: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;statusCode&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;body&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;error&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)})}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  ⚙️ CloudWatch Subscription Filter
&lt;/h3&gt;

&lt;p&gt;El filtro se configura para capturar únicamente logs que contengan el patrón &lt;code&gt;✅ ORIGINAL_POST&lt;/code&gt;. Es importante destacar que este patrón lo agrego en la lambda cuando el post cumple las condiciones de &lt;code&gt;enable:true&lt;/code&gt;. Destaco la manera cómo se configura el filtro; aunque hay límites en el número de filtros que se pueden configurar por log group (&lt;a href="https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/cloudwatch_limits_cwl.html" rel="noopener noreferrer"&gt;Ref&lt;/a&gt;), la forma de agregar filtros es muy simple.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="cp"&gt;# Ejemplo en Terraform de cómo configurar un subscription filter
&lt;/span&gt;&lt;span class="n"&gt;resource&lt;/span&gt; &lt;span class="s"&gt;"aws_cloudwatch_log_subscription_filter"&lt;/span&gt; &lt;span class="s"&gt;"posts_filter"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;name&lt;/span&gt;            &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"${var.project_name}-posts-filter"&lt;/span&gt;
  &lt;span class="n"&gt;log_group_name&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;aws_cloudwatch_log_group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;controller_lambda_logs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;
  &lt;span class="n"&gt;filter_pattern&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"✅ ORIGINAL_POST"&lt;/span&gt;
  &lt;span class="n"&gt;destination_arn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;aws_lambda_function&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;processor_lambda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&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;
  
  
  Lambda Processor
&lt;/h3&gt;

&lt;p&gt;La Lambda Processor recibe los logs filtrados, los descomprime y procesa el contenido. En este caso, a diferencia de la lambda controller, sí necesitamos esta lambda para que procese el log y se active ante el evento del subscription filter.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;gzip&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;base64&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;uuid&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;table&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;dynamodb&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nc"&gt;Table&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;DYNAMODB_TABLE&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

        &lt;span class="c1"&gt;# Descomprimir datos de CloudWatch Logs
&lt;/span&gt;        &lt;span class="c1"&gt;# Este punto es importante: CloudWatch envía los datos codificados 
&lt;/span&gt;        &lt;span class="c1"&gt;# en base64, hay que decodificarlos primero antes de trabajarlos
&lt;/span&gt;        &lt;span class="n"&gt;compressed_payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;base64&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;b64decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;awslogs&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;data&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="n"&gt;uncompressed_payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;gzip&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decompress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;compressed_payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;log_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uncompressed_payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# Procesar cada evento de log
&lt;/span&gt;        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;log_event&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;log_data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;logEvents&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
            &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;log_event&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;✅ ORIGINAL_POST:&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="c1"&gt;# Extraer JSON del POST original
&lt;/span&gt;                &lt;span class="n"&gt;json_start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;json_start&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                    &lt;span class="n"&gt;original_post&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;json_start&lt;/span&gt;&lt;span class="p"&gt;:])&lt;/span&gt;

                    &lt;span class="c1"&gt;# Guardar en DynamoDB
&lt;/span&gt;                    &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;uuid4&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
                        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;original_post&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;original_post&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;timestamp&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;utcnow&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;isoformat&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
                        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;processed_at&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;utcnow&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;isoformat&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                    &lt;span class="p"&gt;}&lt;/span&gt;

                    &lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;put_item&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;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Saved to DynamoDB: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;statusCode&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;body&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Successfully processed logs&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Error processing logs: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;statusCode&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;body&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Error: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🚀 Despliegue con Terraform
&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;aws-cloudwatch-subscription-filter/
├── src/
│   ├── controller_lambda.py
│   └── processor_lambda.py
├──  main.tf
├──  api_gateway.tf
├──  lambda_functions.tf
├──  cloudwatch_subscription_filter.tf
├──  dynamodb_table.tf
├──  iam_roles.tf
├──  build_lambdas.sh
└──  README.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Comandos de despliegue
&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. Construir las Lambdas (zip y dependencias)&lt;/span&gt;
./build_lambdas.sh

&lt;span class="c"&gt;# 2. Inicializar Terraform (credenciales previamente configuradas)&lt;/span&gt;
terraform init

&lt;span class="c"&gt;# 3. Planificar el despliegue&lt;/span&gt;
terraform plan

&lt;span class="c"&gt;# 4. Aplicar la infraestructura&lt;/span&gt;
terraform apply

&lt;span class="c"&gt;# 5. Luego de las pruebas&lt;/span&gt;
terraform destroy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🧪 Pruebas y Validación
&lt;/h2&gt;

&lt;h3&gt;
  
  
  POST que NO será procesado
&lt;/h3&gt;

&lt;p&gt;Es importante tomar los outputs del Terraform y reemplazarlos en &lt;code&gt;{your-api-gateway-url}&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;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://your-api-gateway-url/posts &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&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;'{
    "enable": false,
    "message": "Este mensaje no será procesado",
    "data": "algunos datos"
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  POST que SÍ será procesado
&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; POST https://your-api-gateway-url/posts &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&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;'{
    "enable": true,
    "message": "Este mensaje será procesado y guardado en DynamoDB",
    "data": "algunos datos importantes"
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  📊 Verificación de Resultados
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. CloudWatch Logs
&lt;/h3&gt;

&lt;p&gt;Revisar los logs de la Lambda Controller para confirmar que se generan correctamente:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
❌ POST received but enable=false: {"enable": false, "message": "Este mensaje no ser\u00e1 procesado", "data": "algunos datos"}
END RequestId: 
REPORT RequestId: 
Duration: 1.81 ms Billed Duration: 87 ms Memory Size: 128 MB Max Memory Used: 32 MB Init Duration: 84.44 ms

START RequestId:  Version: $LATEST
✅ ORIGINAL_POST: {"enable": true, "message": "Este mensaje ser\u00e1 procesado y guardado en DynamoDB", "data": "algunos datos importantes"}
END RequestId: 
REPORT RequestId:  
Duration: 1.46 ms Billed Duration: 2 ms 
Memory Size: 128 MB Max Memory Used: 32 MB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;Verificar que los datos se guardaron en la tabla:&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 scan &lt;span class="nt"&gt;--table-name&lt;/span&gt; cloudwatch-subscription-filter-posts &lt;span class="nt"&gt;--limit&lt;/span&gt; 1 &lt;span class="nt"&gt;--region&lt;/span&gt; us-east-1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Response&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;"Items"&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;"original_post"&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;"M"&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;"message"&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;"S"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Este mensaje será procesado y guardado en DynamoDB"&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;"data"&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;"S"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"algunos datos importantes"&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;"enable"&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;"BOOL"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&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="nl"&gt;"log_timestamp"&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;"N"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1768222052525"&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;"id"&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;"S"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"51991cf6-23a5-463b-a8d1-dc2f047ab13c"&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;"processed_at"&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;"S"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-01-12T12:47:42.465810"&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;"timestamp"&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;"S"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-01-12T12:47:42.465790"&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="nl"&gt;"Count"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"ScannedCount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"LastEvaluatedKey"&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;"id"&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;"S"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"51991cf6-23a5-463b-a8d1-dc2f047ab13c"&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;h3&gt;
  
  
  3. CloudWatch Subscription Filter
&lt;/h3&gt;

&lt;p&gt;Confirmar que el filtro se creó y está activo en la consola de AWS.&lt;/p&gt;

&lt;h2&gt;
  
  
  🔧 Características Técnicas
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Filtro de Patrones
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Patrón&lt;/strong&gt;: &lt;code&gt;✅ ORIGINAL_POST&lt;/code&gt; (He probado íconos porque me parece un punto crítico)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sensible a mayúsculas&lt;/strong&gt;: Sí&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Procesamiento&lt;/strong&gt;: máximo 30 segundos hasta que se persiste el registro en DynamoDB. &lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Compresión de Datos
&lt;/h3&gt;

&lt;p&gt;CloudWatch Logs envía los datos &lt;strong&gt;comprimidos con gzip&lt;/strong&gt; y &lt;strong&gt;codificados en base64&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Proceso de descompresión
&lt;/span&gt;&lt;span class="n"&gt;compressed_payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;base64&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;b64decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;awslogs&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;data&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="n"&gt;uncompressed_payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;gzip&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decompress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;compressed_payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;log_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uncompressed_payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Estructura de Datos
&lt;/h3&gt;

&lt;p&gt;Los logs procesados incluyen:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;ID único&lt;/strong&gt; generado con UUID&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;POST original&lt;/strong&gt; completo&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Timestamps&lt;/strong&gt; de creación y procesamiento&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Metadatos&lt;/strong&gt; del log event&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  💡 Casos de Uso Prácticos (Siempre como referencia, pero hay muchos más...)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Monitoreo de Errores
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# En la Lambda Controller
&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;error_occurred&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;🚨 ERROR_ALERT: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;error_details&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&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. Auditoría de Transacciones
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Para transacciones críticas
&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;transaction_type&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;payment&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;💰 PAYMENT_LOG: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;transaction_data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Análisis de Comportamiento
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Para eventos de usuario
&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;user_action&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;login&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;purchase&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;signup&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;👤 USER_EVENT: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  📋 Conclusiones
&lt;/h2&gt;

&lt;p&gt;Aproveché esta POC para validar el funcionamiento de &lt;strong&gt;AWS CloudWatch Subscription Filter&lt;/strong&gt; identificando:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Automatización&lt;/strong&gt; del procesamiento de logs basado en patrones&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Integración&lt;/strong&gt; despierta la lambda processor con una latencia razonable&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Arquitectura serverless&lt;/strong&gt; escalable y costo-eficiente. Importante anotar que no se cobra por detectar los eventos sino por los datos enviados a la lambda o el Kinesis.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Filtrado inteligente&lt;/strong&gt; que procesa solo logs relevantes y se puede tener un control atómico de los eventos que se necesitan.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Esta implementación sirve como base para casos de uso más complejos como &lt;strong&gt;monitoreo de errores&lt;/strong&gt;, &lt;strong&gt;auditoría de transacciones&lt;/strong&gt;, &lt;strong&gt;análisis de comportamiento de usuarios&lt;/strong&gt;, y &lt;strong&gt;alertas en tiempo real&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;El código completo está disponible en &lt;a href="https://github.com/olcortesb/aws-cloudwatch-subscription-filter" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; y listo para desplegar con Terraform, proporcionando una base sólida para proyectos que requieran procesamiento automático de logs en AWS.&lt;/p&gt;

&lt;h2&gt;
  
  
  ⚠️ Consideraciones Importantes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Límites del Servicio
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Máximo 2 subscription filters&lt;/strong&gt; por log group&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Filtros de hasta 1024 caracteres&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rate limiting&lt;/strong&gt; en destinos Lambda&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Mejores Prácticas
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Usar patrones específicos&lt;/strong&gt; para evitar ruido&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Implementar retry logic&lt;/strong&gt; en el processor&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Monitorear métricas&lt;/strong&gt; de CloudWatch&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Configurar alertas&lt;/strong&gt; para errores&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🔗 Referencias y Enlaces Útiles
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/SubscriptionFilters.html" rel="noopener noreferrer"&gt;AWS CloudWatch Logs Subscription Filters&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/FilterAndPatternSyntax.html" rel="noopener noreferrer"&gt;CloudWatch Logs Filter Pattern Syntax&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/cloudwatch_limits_cwl.html" rel="noopener noreferrer"&gt;AWS CloudWatch Limit And Quotas&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/services-cloudwatchlogs.html" rel="noopener noreferrer"&gt;Lambda Function for CloudWatch Logs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_subscription_filter" rel="noopener noreferrer"&gt;Terraform AWS CloudWatch Resources&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Gracias por leer.&lt;/p&gt;

&lt;p&gt;¡Saludos!&lt;/p&gt;

&lt;p&gt;Oscar Cortés&lt;/p&gt;

</description>
      <category>aws</category>
      <category>cloudwatch</category>
      <category>serverless</category>
    </item>
    <item>
      <title>AWS DocumentDB Streams: Configuración y caso de uso</title>
      <dc:creator>olcortesb</dc:creator>
      <pubDate>Wed, 07 Jan 2026 15:29:39 +0000</pubDate>
      <link>https://dev.to/aws-espanol/aws-documentdb-streams-configuracion-y-casos-de-uso-574e</link>
      <guid>https://dev.to/aws-espanol/aws-documentdb-streams-configuracion-y-casos-de-uso-574e</guid>
      <description>&lt;p&gt;Entre finales del 2025 y principios del presente año, con el equipo de trabajo de ACKStorm nos enfrentamos a un desafío interesante. Necesitábamos diseñar una arquitectura serverless para una aplicación que necesitaba un manejo de un alto volumen de datos. En el proceso de diseño se identifico que el uso de una DynamoDB no era una buena opción. Con este escenario analizamos el costo de las distintas bases de datos que tenemos en AWS donde DocumentDB se presentó como la mejor opción.&lt;/p&gt;

&lt;p&gt;Una de las mejores características, al menos desde mi parecer, que tiene DynamoDB es el &lt;a href="https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Streams.html" rel="noopener noreferrer"&gt;DynamoDB Streams&lt;/a&gt;, feature que permite disparar eventos en base a las modificaciones que se realicen sobre la base e integrarlas con el ecosistema de eventos de AWS(lambda, eventbridge, etc). Bien, DocumentDB tiene streams heredados de MongoDB pero no es directamente integrable con el ecosistema. En este post pretendo mostrar cómo configurar DocumentDB streams e integrarlo al flujo de trabajo de AWS.&lt;/p&gt;

&lt;h2&gt;
  
  
  ¿Qué es AWS DocumentDB Stream?
&lt;/h2&gt;

&lt;p&gt;AWS DocumentDB Streams es una funcionalidad que permite capturar y procesar cambios en los datos de una base de datos DocumentDB en tiempo real. Esta característica es especialmente útil para aplicaciones que requieren sincronización de datos, auditoría o integración con otros servicios.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Referencia oficial&lt;/strong&gt;: &lt;a href="https://docs.aws.amazon.com/documentdb/latest/developerguide/change_streams.html" rel="noopener noreferrer"&gt;AWS DocumentDB Change Streams&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Características principales:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Captura de cambios en tiempo real&lt;/strong&gt;: Detecta inserciones, actualizaciones y eliminaciones&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compatibilidad con MongoDB&lt;/strong&gt;: Utiliza la API de Change Streams de MongoDB 3.6+&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Integración nativa&lt;/strong&gt;: Se puede conectar directamente con servicios AWS como Lambda, Kinesis y EventBridge&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Filtrado granular&lt;/strong&gt;: Permite filtrar eventos por colección, operación o campos específicos&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Durabilidad&lt;/strong&gt;: Los eventos se almacenan de forma persistente durante un período configurable&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Documentación técnica&lt;/strong&gt;: &lt;a href="https://docs.aws.amazon.com/documentdb/latest/developerguide/change_streams.html#change_streams-enabling" rel="noopener noreferrer"&gt;Working with Change Streams&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  POC: Arquitectura propuesta
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcf02q4d90x4cvhvihx0r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcf02q4d90x4cvhvihx0r.png" alt="Architecture" width="621" height="371"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Como muestra la figura, la arquitectura propuesta se basa en una lambda que permite escribir los documentos tomando lo que el API Gateway reciba como un POST desde el exterior, luego eso se envía al DocumentDB y del otro lado del dominio de la aplicación está el Event Bridge que está evaluando cada tiempo (1m para esta POC) si se insertó un nuevo documento.&lt;/p&gt;

&lt;h2&gt;
  
  
  EL codigo:
&lt;/h2&gt;

&lt;p&gt;Todo el codigo utilizada para esta POC esta ene este repositorio &lt;a href="https://github.com/olcortesb/aws-documentdb-streams" rel="noopener noreferrer"&gt;Link&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Configuración de DocumentDB Streams
&lt;/h2&gt;

&lt;p&gt;A diferencia de DynamoDB Streams, en DocumentDB es necesario configurarla. Para configurarla se puede hacer a nivel global de la base de datos o a nivel de colecciones; vamos a configurar a nivel de colecciones. También se puede configurar por la línea de comandos o a través de SDK, este segundo será nuestra opción.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;lambda_handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&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;docdb_uri&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;DOCDB_URI&lt;/span&gt;&lt;span class="sh"&gt;'&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="bp"&gt;None&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Connecting to DocumentDB...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# Conectar a DocumentDB con timeouts agresivos
&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;pymongo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;MongoClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;docdb_uri&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;ssl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;tlsCAFile&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;global-bundle.pem&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;retryWrites&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;connectTimeoutMS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# 5 segundos
&lt;/span&gt;            &lt;span class="n"&gt;serverSelectionTimeoutMS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# 5 segundos
&lt;/span&gt;            &lt;span class="n"&gt;socketTimeoutMS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# 10 segundos
&lt;/span&gt;            &lt;span class="n"&gt;maxPoolSize&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# Conexión única
&lt;/span&gt;            &lt;span class="n"&gt;waitQueueTimeoutMS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2000&lt;/span&gt;  &lt;span class="c1"&gt;# 2 segundos
&lt;/span&gt;        &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# Verificar conexión rápidamente
&lt;/span&gt;        &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;admin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ping&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Connected successfully&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;demo_db&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;collection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;users&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

        &lt;span class="c1"&gt;# Habilitar change streams si no están habilitados
&lt;/span&gt;        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;admin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;modifyChangeStreams&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;database&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;demo_db&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;collection&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;users&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;enable&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Change streams enabled for collection&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Change streams already enabled or error: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&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;h2&gt;
  
  
  Prueba de funcionamiento
&lt;/h2&gt;

&lt;p&gt;Una vez configurado el DocumentDB Stream, nos queda realizar pruebas.&lt;/p&gt;

&lt;p&gt;Primero realizamos un POST al API Gateway para agregar un nuevo documento a la DocumentDB.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;~/workspace/aws-documentdb-streams/lambda/writer &lt;span class="o"&gt;(&lt;/span&gt;main&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://umncz7md48.execute-api.us-east-1.amazonaws.com/dev/write   &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt;   &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "user_id": "user_008",
    "name": "Elena Morales", 
    "email": "elena@example.com"
  }'&lt;/span&gt;
&lt;span class="c"&gt;# {"message": "Document inserted successfully", "document_id": "695e6e66a68ca00398aeb3ca"}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verificamos el log del lambda-writer:&lt;/p&gt;

&lt;p&gt;Registro del Log completo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;~/workspace/aws-documentdb-streams/lambda/writer &lt;span class="o"&gt;(&lt;/span&gt;main&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;aws logs &lt;span class="nb"&gt;tail&lt;/span&gt; /aws/lambda/docdb-streams-demo-writer &lt;span class="nt"&gt;--since&lt;/span&gt; 1m &lt;span class="nt"&gt;--region&lt;/span&gt; us-east-1
&lt;span class="c"&gt;## 2026/01/07/[$LATEST] INIT_START Runtime Version: python:3.11.v109      Runtime Version ARN: arn:aws:lambda:us-east-1::runtime:49f733259c7ce7e0deee75ff91c6afe35c7d58b04ed300f32701216263b4590c&lt;/span&gt;
&lt;span class="c"&gt;## 2026/01/07/[$LATEST] START RequestId: 717faf29-0efd-470e-925f-12434921dc45 Version: $LATEST&lt;/span&gt;
&lt;span class="c"&gt;## 2026/01/07/[$LATEST] Connecting to DocumentDB...&lt;/span&gt;
&lt;span class="c"&gt;## 2026/01/07/[$LATEST] /var/task/lambda_function.py:14: UserWarning: You appear to be connected to a DocumentDB cluster. For more information regarding feature compatibility and support please visit https://www.mongodb.com/supportability/documentdb&lt;/span&gt;
&lt;span class="c"&gt;## 2026/01/07/[$LATEST] client = pymongo.MongoClient(&lt;/span&gt;
&lt;span class="c"&gt;## 2026/01/07/[$LATEST] Connected successfully&lt;/span&gt;
&lt;span class="c"&gt;## 2026/01/07/[$LATEST] Change streams enabled for collection&lt;/span&gt;
&lt;span class="c"&gt;## 026/01/07/[$LATEST] Inserting document: {'user_id': 'user_008', 'name': 'Elena Morales', 'email': 'elena@example.com', 'timestamp': datetime.datetime(2026, 1, 7, 14, 32, 6, 64071), 'action': 'user_created'}&lt;/span&gt;
&lt;span class="c"&gt;## 2026/01/07/[$LATEST] Document inserted with ID: 695e6e66a68ca00398aeb3ca&lt;/span&gt;
&lt;span class="c"&gt;## 2026/01/07/[$LATEST] END RequestId: 717faf29-0efd-470e-925f-12434921dc45&lt;/span&gt;
&lt;span class="c"&gt;## 2026/01/07/[$LATEST] REPORT RequestId: 717faf29-0efd-470e-925f-12434921dc45    Duration: 1026.52 ms    Billed Duration: 1470 msMemory Size: 128 MB     Max Memory Used: 58 MB  Init Duration: 443.42 ms&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Registro específico del momento en que el documento se inserta: &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Inserting document: {'user_id': 'user_008', 'name': 'Elena Morales', 'email': '&lt;a href="mailto:elena@example.com"&gt;elena@example.com&lt;/a&gt;', 'timestamp': datetime.datetime(2026, 1, 7, 14, 32, 6, 64071), 'action': 'user_created'}&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Luego verificamos el log del lambda-processor:&lt;/p&gt;

&lt;p&gt;Registro del log completo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;~/workspace/aws-documentdb-streams/lambda/processor &lt;span class="o"&gt;(&lt;/span&gt;main&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;aws logs &lt;span class="nb"&gt;tail&lt;/span&gt; /aws/lambda/docdb-streams-demo-stream-processor &lt;span class="nt"&gt;--since&lt;/span&gt; 1m &lt;span class="nt"&gt;--region&lt;/span&gt; us-east-1
&lt;span class="c"&gt;## 2026/01/07/[$LATEST] INIT_START Runtime Version: python:3.11.v109 Runtime Version ARN: arn:aws:lambda:us-east-1::runtime:49f733259c7ce7e0deee75ff91c6afe35c7d58b04ed300f32701216263b4590c&lt;/span&gt;
&lt;span class="c"&gt;## 2026/01/07/[$LATEST] START RequestId: ba850df0-2e96-4c01-81cd-9e2109db3587 Version: $LATEST&lt;/span&gt;
&lt;span class="c"&gt;## 2026/01/07/[$LATEST] /var/task/lambda_function.py:17: UserWarning: You appear to be connected to a DocumentDB cluster. For more information regarding feature compatibility and support please visit https://www.mongodb.com/supportability/documentdb&lt;/span&gt;
&lt;span class="c"&gt;## 2026/01/07/[$LATEST] client = pymongo.MongoClient(docdb_uri,&lt;/span&gt;
&lt;span class="c"&gt;## 2026/01/07/[$LATEST] Starting change stream listener...&lt;/span&gt;
&lt;span class="c"&gt;## 2026/01/07/[$LATEST] Change detected: insert on document 695e6e66a68ca00398aeb3ca&lt;/span&gt;
&lt;span class="c"&gt;## 2026/01/07/[$LATEST] New document: {"_id": "695e6e66a68ca00398aeb3ca", "user_id": "user_008", "name": "Elena Morales", "email": "elena@example.com", "timestamp": "2026-01-07 14:32:06.064071", "action": "user_created"}&lt;/span&gt;
&lt;span class="c"&gt;## 2026/01/07/[$LATEST] Change detected: insert on document 695e6e66a68ca00398aeb3ca&lt;/span&gt;
&lt;span class="c"&gt;## 2026/01/07/[$LATEST] New document: {"_id": "695e6e66a68ca00398aeb3ca", "user_id": "user_008", "name": "Elena Morales", "email": "elena@example.com", "timestamp": "2026-01-07 14:32:06.064071", "action": "user_created"}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Registro específico del momento en que el stream detecta el nuevo documento:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Change detected: insert on document 695e6e66a68ca00398aeb3ca&lt;br&gt;
New document: {"_id": "695e6e66a68ca00398aeb3ca", "user_id": "user_008", "name": "Elena Morales", "email": "&lt;a href="mailto:elena@example.com"&gt;elena@example.com&lt;/a&gt;", "timestamp": "2026-01-07 14:32:06.064071", "action": "user_created"}&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Conclusiones
&lt;/h2&gt;

&lt;p&gt;La implementación de AWS DocumentDB Streams es una solución viable para capturar y procesar cambios en tiempo real en aplicaciones serverless de alto volumen cuando DynamoDB no es una opción. Sin embargo, hay que tener en cuenta:&lt;/p&gt;

&lt;h3&gt;
  
  
  Ventajas identificadas:
&lt;/h3&gt;

&lt;p&gt;✅ &lt;strong&gt;Integración nativa con el ecosistema AWS&lt;/strong&gt;: Aunque requiere configuración adicional comparado con DynamoDB Streams, la integración con Lambda y EventBridge funciona de manera fluida. La desventaja en este punto es que el sistema en general no es reactivo por naturaleza; necesitamos EventBridge para poder detectar el evento. Sin embargo, una vez detectado, lo podemos integrar con el resto del ecosistema. &lt;/p&gt;

&lt;p&gt;✅ &lt;strong&gt;Flexibilidad de configuración&lt;/strong&gt;: La posibilidad de habilitar streams a nivel de colección específica permite un control granular sobre qué cambios capturar.&lt;/p&gt;

&lt;p&gt;✅ &lt;strong&gt;Compatibilidad con MongoDB&lt;/strong&gt;: Al utilizar la API estándar de Change Streams de MongoDB, facilita la migración de aplicaciones existentes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Consideraciones importantes:
&lt;/h3&gt;

&lt;p&gt;⚠️ &lt;strong&gt;Configuración manual requerida&lt;/strong&gt;: A diferencia de DynamoDB, DocumentDB Streams requiere habilitación explícita por colección.&lt;/p&gt;

&lt;p&gt;⚠️ &lt;strong&gt;Gestión de conexiones&lt;/strong&gt;: Es crucial implementar timeouts y pooling de conexiones adecuados para evitar problemas de rendimiento.&lt;/p&gt;

&lt;p&gt;⚠️ &lt;strong&gt;Monitoreo&lt;/strong&gt;: Se recomienda implementar alertas para detectar fallos en el procesamiento de streams.&lt;/p&gt;

&lt;h3&gt;
  
  
  Casos de uso recomendados (en el papel, pueden ser muchos más):
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Sincronización de datos&lt;/strong&gt; entre microservicios&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Auditoría en tiempo real&lt;/strong&gt; de cambios en documentos críticos&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Triggers de notificaciones&lt;/strong&gt; basados en cambios de estado&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Replicación selectiva&lt;/strong&gt; de datos a sistemas analíticos&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Importante antes de implementar esta solución en un ambiente productivo:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Implementar manejo de errores robusto con DLQ (Dead Letter Queues)&lt;/li&gt;
&lt;li&gt;Configurar métricas personalizadas en CloudWatch&lt;/li&gt;
&lt;li&gt;Evaluar el uso de Kinesis Data Streams para mayor throughput&lt;/li&gt;
&lt;li&gt;Implementar filtros más específicos para optimizar el procesamiento&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;En resumen, DocumentDB Streams ofrece una alternativa sólida a DynamoDB Streams cuando se requiere flexibilidad de esquemas, volumen alto de datos y compatibilidad con MongoDB, aunque con un overhead de configuración adicional que debe considerarse en el diseño de la arquitectura.&lt;/p&gt;




&lt;p&gt;Gracias por leer.&lt;/p&gt;

&lt;p&gt;¡Saludos!&lt;/p&gt;

&lt;p&gt;Oscar Cortés&lt;/p&gt;

</description>
      <category>aws</category>
      <category>awddocumentdb</category>
      <category>stream</category>
    </item>
    <item>
      <title>AWS Landing Zone: Conceptos y contexto</title>
      <dc:creator>olcortesb</dc:creator>
      <pubDate>Fri, 14 Nov 2025 10:57:54 +0000</pubDate>
      <link>https://dev.to/aws-espanol/aws-landing-zone-conceptos-y-contexto-5aad</link>
      <guid>https://dev.to/aws-espanol/aws-landing-zone-conceptos-y-contexto-5aad</guid>
      <description>&lt;p&gt;Buenas ... Normalmente escribo sobre serverless en mis posts/artículos técnicos, sin embargo, hoy inauguro una nueva línea que tiene que ver con la "gobernanza en el cloud". El crecimiento del uso del cloud, la adopción de buenas prácticas y la evolución de las organizaciones nos llevan a que la gestión de recursos a gran escala sea parte de nuestro trabajo diario con el cloud.&lt;/p&gt;

&lt;p&gt;Después de varios años trabajando con AWS(Amazon Web Services), he visto cómo los clientes que empiezan con una sola cuenta terminan con un "caos organizado" cuando crecen. AWS Landing Zone es la solución que ofrece AWS para gestionar este caos organizado.&lt;/p&gt;

&lt;h2&gt;
  
  
  🎯 ¿Qué es AWS Landing Zone y por qué me parece fundamental?
&lt;/h2&gt;

&lt;p&gt;En términos simples, &lt;strong&gt;AWS Landing Zone&lt;/strong&gt; es como tener un "plano maestro" para organizar tu infraestructura en AWS. Imagínate que vas a construir un barrio completo: necesitas planificar las calles, los servicios básicos, las zonas residenciales y comerciales antes de empezar a construir casas.&lt;/p&gt;

&lt;p&gt;Eso es exactamente lo que hace Landing Zone: nos da una estructura &lt;em&gt;multi-cuenta&lt;/em&gt; bien organizada, con seguridad integrada desde el día uno y todas las estructuras(logging, monitoreo, compliance) ya conectadas. Es la diferencia entre crecer de forma ordenada o terminar con un laberinto imposible de mantener, al menos en mi experiencia.&lt;/p&gt;

&lt;h2&gt;
  
  
  📅 Evolución Histórica
&lt;/h2&gt;

&lt;p&gt;Como siempre que inicio un nuevo tema o nueva línea de trabajo me parece buen ejercicio rastrear los orígenes, he intentado revisar post, artículos varios y esta sería una línea de tiempo no oficial de cómo AWS Landing Zone llegó a nosotros.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Landing Zone (Solución original) Antes de 2019&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;No he encontrado referencias oficiales directas de lo que era AWS Landing Zone previo a 2019, pero en todos lados tenemos referencia a migrar landing zone (Solución Original) a control tower por ejemplo el link a continuación: &lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fss7dljxtyvugrm3l1nvz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fss7dljxtyvugrm3l1nvz.png" alt="AWS Landing Zone Migration Guide" width="800" height="354"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/prescriptive-guidance/latest/aws-control-tower/introduction.html" rel="noopener noreferrer"&gt;https://docs.aws.amazon.com/prescriptive-guidance/latest/aws-control-tower/introduction.html&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Por lo tanto es correcto asumir que teníamos una versión 1, y control tower se presenta como la versión 2.0.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Control Tower ¿cómo llegamos hasta aquí?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;24 de junio de 2019&lt;/strong&gt;: Disponibilidad general (GA) versión 2.1 (&lt;a href="https://aws.amazon.com/blogs/aws/aws-control-tower-set-up-govern-a-multi-account-aws-environment/" rel="noopener noreferrer"&gt;AWS News Blog&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Agosto 2019&lt;/strong&gt;: Controles detectivos adicionales (&lt;a href="https://docs.aws.amazon.com/controltower/latest/userguide/release-notes.html" rel="noopener noreferrer"&gt;Release Notes&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Septiembre 2019&lt;/strong&gt;: Controles electivos adicionales&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Noviembre 2019&lt;/strong&gt;: Versión 2.2 con controles preventivos contra drift&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Años con Release Notes y Versiones Disponibles:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;2019 (v2.1 - v2.2)&lt;/strong&gt;: &lt;a href="https://docs.aws.amazon.com/controltower/latest/userguide/2019-all.html" rel="noopener noreferrer"&gt;https://docs.aws.amazon.com/controltower/latest/userguide/2019-all.html&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;2020 (v2.3 - v2.6)&lt;/strong&gt;: &lt;a href="https://docs.aws.amazon.com/controltower/latest/userguide/2020-all.html" rel="noopener noreferrer"&gt;https://docs.aws.amazon.com/controltower/latest/userguide/2020-all.html&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;2021 (v2.7 - v2.9)&lt;/strong&gt;: &lt;a href="https://docs.aws.amazon.com/controltower/latest/userguide/2021-all.html" rel="noopener noreferrer"&gt;https://docs.aws.amazon.com/controltower/latest/userguide/2021-all.html&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;2022 (v3.0 - v3.1)&lt;/strong&gt;: &lt;a href="https://docs.aws.amazon.com/controltower/latest/userguide/2022-all.html" rel="noopener noreferrer"&gt;https://docs.aws.amazon.com/controltower/latest/userguide/2022-all.html&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;2023 (v3.2 - v3.3)&lt;/strong&gt;: &lt;a href="https://docs.aws.amazon.com/controltower/latest/userguide/2023-all.html" rel="noopener noreferrer"&gt;https://docs.aws.amazon.com/controltower/latest/userguide/2023-all.html&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;2024 (v3.3)&lt;/strong&gt;: &lt;a href="https://docs.aws.amazon.com/controltower/latest/userguide/2024-all.html" rel="noopener noreferrer"&gt;https://docs.aws.amazon.com/controltower/latest/userguide/2024-all.html&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;2025 (v3.3)&lt;/strong&gt;: &lt;a href="https://docs.aws.amazon.com/controltower/latest/userguide/2025-all.html" rel="noopener noreferrer"&gt;https://docs.aws.amazon.com/controltower/latest/userguide/2025-all.html&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🍜 Los "ingredientes" de la landing zone a grandes rasgos:
&lt;/h2&gt;

&lt;p&gt;Control Tower es el elemento principal pero una landing zone realmente es un conjunto de recursos. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;AWS Control Tower&lt;/strong&gt;: El "cerebro/control" que orquesta todo. Una vez configurado, prácticamente se maneja solo&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AWS Organizations&lt;/strong&gt;: El "árbol genealógico" de cuentas. Aquí defines quién puede hacer qué y dónde&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AWS SSO (Identity Center)&lt;/strong&gt;: Único Login Centralizado para todas las cuentas un SSO de libro...&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AWS Config&lt;/strong&gt;: El "detective" que te avisa cuando algo no está como debería estar configurado&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AWS Service Catalog&lt;/strong&gt;: El "catálogo de productos" donde defines plantillas pre-aprobadas que los equipos pueden usar sin romper nada (en principio)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AWS CloudTrail&lt;/strong&gt;: El "diario" que registra absolutamente todo lo que pasa en tus cuentas&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;... y muchos más!&lt;/p&gt;

&lt;p&gt;Aquí los elementos mínimos que propone AWS para implementar una AWS Landing Zone&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgymsvse9km3upvek1vsw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgymsvse9km3upvek1vsw.png" alt="AWS Landing Zone Components" width="800" height="416"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/prescriptive-guidance/latest/migration-aws-environment/building-landing-zones.html" rel="noopener noreferrer"&gt;https://docs.aws.amazon.com/prescriptive-guidance/latest/migration-aws-environment/building-landing-zones.html&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  🏗️ ¿Y cómo orquestamos / construimos todos estos recursos?
&lt;/h2&gt;

&lt;p&gt;Aquí hay un punto importante, AWS ofrece una guía prescriptiva de cómo implementar un landing zone con CloudFormation:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzm70syt2doki5o685v7f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzm70syt2doki5o685v7f.png" alt="AWS Landing Zone Accelerator Architecture" width="800" height="441"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/solutions/latest/landing-zone-accelerator-on-aws/architecture-overview.html" rel="noopener noreferrer"&gt;https://docs.aws.amazon.com/solutions/latest/landing-zone-accelerator-on-aws/architecture-overview.html&lt;/a&gt; &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Sin embargo he hecho pruebas y creo que me inclinaría más por Terraform o Pulumi, este es el inicio de esta serie así que ya contaré sobre este tema más adelante.&lt;/p&gt;

&lt;h2&gt;
  
  
  ¿A partir de cuántas cuentas tendría que montarme una landing zone?
&lt;/h2&gt;

&lt;p&gt;No hay una recomendación oficial sobre a partir de cuántas cuentas está bien crear una landing zone, creo que esta discusión escapa un poco de este artículo en particular pero es un tema interesante a tratar:&lt;/p&gt;

&lt;p&gt;Sin embargo, aquí algunas referencias que se pueden encontrar sobre las recomendaciones oficiales de la documentación de AWS:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;"Más de una cuenta"&lt;br&gt;
• &lt;strong&gt;Fuente&lt;/strong&gt;: &lt;a href="https://docs.aws.amazon.com/prescriptive-guidance/latest/migration-aws-environment/understanding-landing-zones.html" rel="noopener noreferrer"&gt;https://docs.aws.amazon.com/prescriptive-guidance/latest/migration-aws-environment/understanding-landing-zones.html&lt;/a&gt;&lt;br&gt;
• &lt;strong&gt;Texto exacto&lt;/strong&gt;: "Although there is no standard number of AWS accounts you should have, we recommend that you create more than one AWS account."&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;"Más de un puñado de cuentas"&lt;br&gt;
• &lt;strong&gt;Fuente&lt;/strong&gt;: &lt;a href="https://docs.aws.amazon.com/controltower/latest/userguide/what-is-control-tower.html" rel="noopener noreferrer"&gt;https://docs.aws.amazon.com/controltower/latest/userguide/what-is-control-tower.html&lt;/a&gt;&lt;br&gt;
• &lt;strong&gt;Texto exacto&lt;/strong&gt;: "If you are hosting more than a handful of accounts, it's beneficial to have an orchestration layer that facilitates account deployment and account &lt;br&gt;
governance."&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Sobre las cuentas y OU (Unidades Organizacionales)
&lt;/h3&gt;

&lt;p&gt;Bueno, supongamos que ya tenemos claro que tenemos muchas cuentas, ahora, ¿cómo las organizamos? Sobre este aspecto hay mucho escrito, a continuación un ejemplo clásico de cómo se organizan las cuentas.&lt;/p&gt;

&lt;h3&gt;
  
  
  Ejemplo clásico
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Root Organization
├── Security OU (Organizational Unit)
│   ├── Log Archive Account
│   ├── Audit Account
│   └── Security Tooling Account
├── Sandbox OU
│   └── Developer Sandbox Accounts
├── Production OU
│   ├── Production Account 1
│   └── Production Account 2
└── Non-Production OU
    ├── Development Account
    ├── Testing Account
    └── Staging Account
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Aunque este es un ejemplo clásico el tema es complejo y tiene análisis de estudio grande como se puede ver en los estudios en los White Papers que dejo en este link:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/pdfs/whitepapers/latest/aws-overview/aws-overview.pdf" rel="noopener noreferrer"&gt;https://docs.aws.amazon.com/pdfs/whitepapers/latest/aws-overview/aws-overview.pdf&lt;/a&gt;  &lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  🔗 Recursos y Referencias Oficiales
&lt;/h2&gt;

&lt;p&gt;A continuación, y a modo de referencia, dejo mis links, no los he leído todos al completo pero los he revisado al menos una vez... &lt;/p&gt;

&lt;h3&gt;
  
  
  Documentación Principal de AWS
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/controltower/latest/userguide/what-is-control-tower.html" rel="noopener noreferrer"&gt;AWS Control Tower User Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/controltower/latest/userguide/aws-multi-account-landing-zone.html" rel="noopener noreferrer"&gt;AWS Multi-Account Strategy for Landing Zone&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/controltower/latest/userguide/how-control-tower-works.html" rel="noopener noreferrer"&gt;How AWS Control Tower Works&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/organizations/latest/userguide/orgs_best-practices.html" rel="noopener noreferrer"&gt;AWS Organizations Best Practices&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Whitepapers y Guías Estratégicas
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/whitepapers/latest/organizing-your-aws-environment/organizing-your-aws-environment.html" rel="noopener noreferrer"&gt;Organizing Your AWS Environment Using Multiple Accounts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/whitepapers/latest/organizing-your-aws-environment/how-does-aws-control-tower-establish-your-multi-account-environment.html" rel="noopener noreferrer"&gt;How AWS Control Tower Establishes Multi-Account Environment&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://aws.amazon.com/architecture/well-architected/" rel="noopener noreferrer"&gt;AWS Well-Architected Framework&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  AWS Prescriptive Guidance
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/prescriptive-guidance/latest/migration-aws-environment/understanding-landing-zones.html" rel="noopener noreferrer"&gt;What is a Landing Zone?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/prescriptive-guidance/latest/migration-aws-environment/building-landing-zones.html" rel="noopener noreferrer"&gt;Building a Landing Zone&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/prescriptive-guidance/latest/designing-control-tower-landing-zone/introduction.html" rel="noopener noreferrer"&gt;Designing an AWS Control Tower Landing Zone&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/prescriptive-guidance/latest/strategy-migration/aws-landing-zone.html" rel="noopener noreferrer"&gt;Landing Zone for Cloud Migration&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/prescriptive-guidance/latest/designing-control-tower-landing-zone/account-structure.html" rel="noopener noreferrer"&gt;Configuring Account Structure and OUs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/prescriptive-guidance/latest/designing-control-tower-landing-zone/logging-monitoring.html" rel="noopener noreferrer"&gt;Centralized Logging and Monitoring&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Servicios Complementarios
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/controltower/latest/userguide/logging-and-monitoring.html" rel="noopener noreferrer"&gt;Logging and Monitoring in AWS Control Tower&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/controltower/latest/userguide/nested-ous.html" rel="noopener noreferrer"&gt;Nested OUs in AWS Control Tower&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/controltower/latest/userguide/lz-update-best-practices.html" rel="noopener noreferrer"&gt;Best Practices for Landing Zone Updates&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Herramientas de Implementación que vamos a usar en estos post!
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;AWS CLI&lt;/strong&gt;: Automatización de tareas&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Terraform&lt;/strong&gt;: Infrastructure as Code (IaC)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AWS CDK&lt;/strong&gt;: Desarrollo programático (IaC)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pulumi&lt;/strong&gt;: Infrastructura as Code (IaC)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CloudFormations&lt;/strong&gt;: Infrastructure as Code (IaC)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Comunidad y Soporte
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://aws.amazon.com/architecture/" rel="noopener noreferrer"&gt;AWS Architecture Center&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/aws-samples" rel="noopener noreferrer"&gt;AWS Samples GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://repost.aws/" rel="noopener noreferrer"&gt;AWS re:Post&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  💡 Conclusiones
&lt;/h2&gt;

&lt;p&gt;Este post solo pretende ser un punto de partida para compartir todas las experiencias que me he encontrado desplegando AWS Landing Zone, &lt;/p&gt;

&lt;p&gt;gracias por leer, nos vemos&lt;/p&gt;

&lt;p&gt;Saludos&lt;/p&gt;

</description>
      <category>aws</category>
      <category>landingzone</category>
    </item>
    <item>
      <title>🏗️ 4 Estrategias para Procesar Mensajes SQS en Batch con AWS Lambda</title>
      <dc:creator>olcortesb</dc:creator>
      <pubDate>Fri, 10 Oct 2025 08:01:04 +0000</pubDate>
      <link>https://dev.to/aws-espanol/4-estrategias-para-procesar-mensajes-sqs-en-batch-con-aws-lambda-50oa</link>
      <guid>https://dev.to/aws-espanol/4-estrategias-para-procesar-mensajes-sqs-en-batch-con-aws-lambda-50oa</guid>
      <description>&lt;p&gt;Durante una charla de sincronización con un compañero de trabajo, sobre un cliente que necesita una estrategia para gestionar una cola de eventos a través de procesamiento por lotes, surgió la duda de cuál es la estrategia que más le convenía, y cuál era el estado del arte de las colas SQS en estos momentos.&lt;/p&gt;

&lt;p&gt;Basado en lo que sacamos de esa charla, en este &lt;em&gt;post&lt;/em&gt; exploraremos &lt;strong&gt;4 estrategias diferentes&lt;/strong&gt; para procesar mensajes de Amazon SQS en lotes usando AWS Lambda (como único consumidor, esto es importante para el análisis). Cada estrategia tiene sus propios casos de uso, ventajas y consideraciones de rendimiento que analizaremos con ejemplos prácticos.&lt;/p&gt;

&lt;h2&gt;
  
  
  🚀 ¿Por qué procesar mensajes SQS en lotes?
&lt;/h2&gt;

&lt;p&gt;Amazon SQS permite procesar hasta &lt;strong&gt;10 mensajes por llamada&lt;/strong&gt; usando &lt;code&gt;receive_message()&lt;/code&gt; (Python), lo que es más eficiente que procesar mensajes uno por uno. Sin embargo, no todos los mensajes pueden procesarse exitosamente en el primer intento, por lo que necesitamos diferentes estrategias para manejar los fallos.&lt;/p&gt;

&lt;h3&gt;
  
  
  ⚠️ Validación del Límite de 10 Mensajes
&lt;/h3&gt;

&lt;p&gt;AWS SQS &lt;strong&gt;estrictamente limita&lt;/strong&gt; el parámetro &lt;code&gt;MaxNumberOfMessages&lt;/code&gt; entre 1 y 10. Si intentas usar un valor mayor, obtienes este error:&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;"error"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"An error occurred (InvalidParameterValue) when calling the ReceiveMessage operation: Value 20 for parameter MaxNumberOfMessages is invalid. Reason: Must be between 1 and 10, if provided."&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;Este límite aplica tanto para &lt;strong&gt;colas Standard como FIFO&lt;/strong&gt;, confirmando que el procesamiento en lotes está limitado a máximo 10 mensajes por operación &lt;code&gt;receive_message()&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Colas estándar vs Colas FIFO: &lt;a href="https://docs.aws.amazon.com/es_es/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-queue-types.html" rel="noopener noreferrer"&gt;Ref&lt;/a&gt; &lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  🏗️ Arquitectura del Proyecto
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;⚠️ Importante&lt;/strong&gt;: Este análisis y casos de uso están basados en un &lt;strong&gt;consumidor único&lt;/strong&gt; por cola. Con &lt;strong&gt;múltiples consumidores concurrentes&lt;/strong&gt;, el comportamiento y las consideraciones de cada estrategia pueden ser significativamente más complejas, especialmente en términos de duplicados, orden de procesamiento y gestión de fallos.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;El proyecto implementa 4 funciones Lambda diferentes, cada una con una estrategia distinta para manejar mensajes que no cumplen ciertos criterios (en nuestro caso, números menores a 50). Partiendo de un script que genera 100 mensajes aleatorios con números entre 1 y 100 (el script pueden encontrarlo en el GitHub):&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://github.com/olcortesb/aws-examples" rel="noopener noreferrer"&gt;https://github.com/olcortesb/aws-examples&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A continuación, las dos colas que utilizaremos en las pruebas:&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;Resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;MessagesQueue&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::SQS::Queue&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;VisibilityTimeoutSeconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;30&lt;/span&gt;
      &lt;span class="na"&gt;MessageRetentionPeriod&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1209600&lt;/span&gt;

  &lt;span class="na"&gt;DeadLetterQueue&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::SQS::Queue&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;MessageRetentionPeriod&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1209600&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  📋 Las 4 Estrategias Implementadas
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Estrategia Simple: Eliminar Todos los Mensajes
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Función&lt;/strong&gt;: &lt;code&gt;GetMessagesFunction&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;lambda_handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&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;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sqs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;receive_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;QueueUrl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;queue_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;MaxNumberOfMessages&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# Máximo permitido por AWS
&lt;/span&gt;        &lt;span class="n"&gt;WaitTimeSeconds&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;messages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Messages&lt;/span&gt;&lt;span class="sh"&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;for&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# Procesar mensaje
&lt;/span&gt;        &lt;span class="n"&gt;processed_messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;messageId&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;MessageId&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;body&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Body&lt;/span&gt;&lt;span class="sh"&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;# Eliminar siempre
&lt;/span&gt;        &lt;span class="n"&gt;sqs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;delete_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;QueueUrl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;queue_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;ReceiptHandle&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ReceiptHandle&lt;/span&gt;&lt;span class="sh"&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;strong&gt;Simplicidad&lt;/strong&gt;: Lógica directa y fácil de entender&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Rendimiento&lt;/strong&gt;: No hay operaciones adicionales&lt;/li&gt;
&lt;li&gt;❌ &lt;strong&gt;Pérdida de datos&lt;/strong&gt;: Los mensajes fallidos se pierden permanentemente&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Caso de uso&lt;/strong&gt;: Procesamiento donde la pérdida ocasional de mensajes es aceptable.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Estrategia de Reenvío: Devolver Mensajes a la Cola
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Función&lt;/strong&gt;: &lt;code&gt;ProcessMessagesFunction&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;lambda_handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;message_number&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Body&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;message_number&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;threshold&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="c1"&gt;# Reenviar a la misma cola
&lt;/span&gt;            &lt;span class="n"&gt;sqs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;QueueUrl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;queue_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;MessageBody&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Body&lt;/span&gt;&lt;span class="sh"&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;# Eliminar mensaje original
&lt;/span&gt;        &lt;span class="n"&gt;sqs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;delete_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;QueueUrl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;queue_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;ReceiptHandle&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ReceiptHandle&lt;/span&gt;&lt;span class="sh"&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;strong&gt;Sin pérdida&lt;/strong&gt;: Los mensajes fallidos se reenvían&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Flexibilidad&lt;/strong&gt;: Permite modificar el mensaje antes del reenvío (creo que es lo único positivo que tiene este caso de uso, pero lo dejo porque alguna vez lo he tenido que usar 😂)&lt;/li&gt;
&lt;li&gt;❌ &lt;strong&gt;Duplicación&lt;/strong&gt;: Puede crear mensajes duplicados&lt;/li&gt;
&lt;li&gt;❌ &lt;strong&gt;Overhead&lt;/strong&gt;: Operaciones adicionales de escritura&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Caso de uso&lt;/strong&gt;: Cuando necesitas modificar (por ejemplo, sumar un número en nuestro caso de prueba) mensajes antes de reintentar un nuevo procesamiento.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Estrategia de Visibility Timeout: Dejar Reaparecer
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Función&lt;/strong&gt;: &lt;code&gt;ProcessWithVisibilityFunction&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;lambda_handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;message_number&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Body&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;message_number&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;threshold&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="c1"&gt;# Solo eliminar si cumple criterios
&lt;/span&gt;            &lt;span class="n"&gt;sqs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;delete_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;QueueUrl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;queue_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;ReceiptHandle&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ReceiptHandle&lt;/span&gt;&lt;span class="sh"&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;# Los mensajes no eliminados reaparecen automáticamente
&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;strong&gt;Eficiencia&lt;/strong&gt;: Menos operaciones de escritura&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Automático&lt;/strong&gt;: SQS maneja la reaparición&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Sin duplicados&lt;/strong&gt;: No crea mensajes adicionales&lt;/li&gt;
&lt;li&gt;❌ &lt;strong&gt;Tiempo fijo&lt;/strong&gt;: Depende del visibility timeout configurado

&lt;ul&gt;
&lt;li&gt; &lt;strong&gt;Visibility Timeout grande&lt;/strong&gt;: Puede darce el caso que el procesamiento de mensajes puede ser distinto por ejemplo un minuto para un mensaje y 30 minutos para otro, por lo tanto, es necesario entender si podemos esperar ese tiempo o colocar un visibility mas bajo.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Visibility Timeout pequeño&lt;/strong&gt;: Si el timeout es muy pequeño y el procesamiento de los mensajes es mayor al timeout, se pueden generar duplicados.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Caso de uso&lt;/strong&gt;: Ideal para reintentos automáticos sin lógica compleja.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Estrategia Dead Letter Queue: Segregar Mensajes Fallidos
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Función&lt;/strong&gt;: &lt;code&gt;ProcessWithDlqFunction&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;lambda_handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;message_number&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Body&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;message_number&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;threshold&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;processed_messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="c1"&gt;# Enviar a Dead Letter Queue
&lt;/span&gt;            &lt;span class="n"&gt;sqs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;QueueUrl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;dlq_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;MessageBody&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Body&lt;/span&gt;&lt;span class="sh"&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;# Eliminar de cola original
&lt;/span&gt;        &lt;span class="n"&gt;sqs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;delete_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;QueueUrl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;queue_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;ReceiptHandle&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ReceiptHandle&lt;/span&gt;&lt;span class="sh"&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;strong&gt;Segregación&lt;/strong&gt;: Mensajes fallidos en cola separada&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Análisis&lt;/strong&gt;: Permite investigar patrones de fallo&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Reprocesamiento&lt;/strong&gt;: Posibilidad de procesar DLQ posteriormente&lt;/li&gt;
&lt;li&gt;❌ &lt;strong&gt;Complejidad&lt;/strong&gt;: Requiere gestión de múltiples colas&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Caso de uso&lt;/strong&gt;: Sistemas críticos que requieren análisis de fallos y reprocesamiento.&lt;/p&gt;

&lt;h2&gt;
  
  
  🚀 Despliegue y Pruebas
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Desplegar la Aplicación
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sam build
sam deploy &lt;span class="nt"&gt;--guided&lt;/span&gt; &lt;span class="nt"&gt;--profile&lt;/span&gt; your-aws-profile
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Enviar Mensajes de Prueba
&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;scripts
python3 send_messages.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;El script envía 100 mensajes numerados del 1 al 100 para probar las diferentes estrategias (los que no tomamos como válidos son los menores a 50).&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Probar Cada Estrategia
&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;# Estrategia 1: Eliminar todos&lt;/span&gt;
aws lambda invoke &lt;span class="nt"&gt;--function-name&lt;/span&gt; bach-messages-GetMessagesFunction-XXXXX response1.json

&lt;span class="c"&gt;# Estrategia 2: Reenvío&lt;/span&gt;
aws lambda invoke &lt;span class="nt"&gt;--function-name&lt;/span&gt; bach-messages-ProcessMessagesFunction-XXXXX response2.json

&lt;span class="c"&gt;# Estrategia 3: Visibility Timeout&lt;/span&gt;
aws lambda invoke &lt;span class="nt"&gt;--function-name&lt;/span&gt; bach-messages-ProcessWithVisibilityFunction-XXXXX response3.json

&lt;span class="c"&gt;# Estrategia 4: Dead Letter Queue&lt;/span&gt;
aws lambda invoke &lt;span class="nt"&gt;--function-name&lt;/span&gt; bach-messages-ProcessWithDlqFunction-XXXXX response4.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Estrategia&lt;/th&gt;
&lt;th&gt;Pérdida de Datos&lt;/th&gt;
&lt;th&gt;Duplicados&lt;/th&gt;
&lt;th&gt;Operaciones Extra&lt;/th&gt;
&lt;th&gt;Complejidad&lt;/th&gt;
&lt;th&gt;Análisis de Fallos&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Simple&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;❌ Sí&lt;/td&gt;
&lt;td&gt;✅ No&lt;/td&gt;
&lt;td&gt;✅ Ninguna&lt;/td&gt;
&lt;td&gt;✅ Baja&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Reenvío&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ No&lt;/td&gt;
&lt;td&gt;❌ Posible&lt;/td&gt;
&lt;td&gt;❌ Send&lt;/td&gt;
&lt;td&gt;🟡 Media&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Visibility&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ No&lt;/td&gt;
&lt;td&gt;✅ No&lt;/td&gt;
&lt;td&gt;✅ Ninguna&lt;/td&gt;
&lt;td&gt;✅ Baja&lt;/td&gt;
&lt;td&gt;❌ Limitado&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;DLQ&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ No&lt;/td&gt;
&lt;td&gt;✅ No&lt;/td&gt;
&lt;td&gt;❌ Send&lt;/td&gt;
&lt;td&gt;❌ Alta&lt;/td&gt;
&lt;td&gt;✅ Completo&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  🎯 Recomendaciones por Caso de Uso
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Usar &lt;strong&gt;Estrategia Simple&lt;/strong&gt; cuando:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Los mensajes no son críticos&lt;/li&gt;
&lt;li&gt;El rendimiento es prioritario&lt;/li&gt;
&lt;li&gt;La pérdida ocasional es aceptable&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Usar &lt;strong&gt;Estrategia de Reenvío&lt;/strong&gt; cuando:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Necesitas modificar mensajes antes del reintento&lt;/li&gt;
&lt;li&gt;Tienes lógica compleja de reintento&lt;/li&gt;
&lt;li&gt;Puedes manejar duplicados&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Usar &lt;strong&gt;Visibility Timeout&lt;/strong&gt; cuando:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Quieres reintentos automáticos simples&lt;/li&gt;
&lt;li&gt;El rendimiento es importante&lt;/li&gt;
&lt;li&gt;Los fallos son temporales&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Usar &lt;strong&gt;Dead Letter Queue&lt;/strong&gt; cuando:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Los mensajes son críticos&lt;/li&gt;
&lt;li&gt;Necesitas análisis de fallos&lt;/li&gt;
&lt;li&gt;Requieres reprocesamiento manual&lt;/li&gt;
&lt;li&gt;Tienes sistemas de monitoreo avanzados&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🔧 Configuraciones Importantes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Visibility Timeout
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;MessagesQueue&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;VisibilityTimeoutSeconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;30&lt;/span&gt;  &lt;span class="c1"&gt;# Tiempo antes de reaparecer&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Message Retention
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;DeadLetterQueue&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;MessageRetentionPeriod&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1209600&lt;/span&gt;  &lt;span class="c1"&gt;# 14 días&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Lambda Timeout
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Globals&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Function&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;  &lt;span class="c1"&gt;# Debe ser menor que visibility timeout&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  📈 Métricas y Monitoreo
&lt;/h2&gt;

&lt;p&gt;Para cada estrategia, monitorea:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Throughput&lt;/strong&gt;: Mensajes procesados por segundo&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Error Rate&lt;/strong&gt;: Porcentaje de mensajes fallidos&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Latency&lt;/strong&gt;: Tiempo de procesamiento por lote&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Queue Depth&lt;/strong&gt;: Mensajes pendientes en cola&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DLQ Messages&lt;/strong&gt;: Mensajes en Dead Letter Queue (estrategia 4)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Y luego identifica cuál es mejor para tu caso de uso.&lt;/p&gt;

&lt;h2&gt;
  
  
  🏁 Conclusiones
&lt;/h2&gt;

&lt;p&gt;Cada estrategia tiene su lugar en diferentes arquitecturas:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Simple&lt;/strong&gt;: Para casos no críticos con alta performance&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reenvío&lt;/strong&gt;: Para lógica compleja de reintento&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Visibility Timeout&lt;/strong&gt;: Para reintentos automáticos eficientes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dead Letter Queue&lt;/strong&gt;: Para sistemas críticos con análisis de fallos&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;La elección depende de tus requisitos específicos de &lt;strong&gt;confiabilidad&lt;/strong&gt;, &lt;strong&gt;rendimiento&lt;/strong&gt; y &lt;strong&gt;observabilidad&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Recuerda&lt;/strong&gt;: Estas recomendaciones aplican principalmente para &lt;strong&gt;consumidores únicos&lt;/strong&gt;. En escenarios con &lt;strong&gt;múltiples consumidores&lt;/strong&gt;, considera factores adicionales como concurrencia, orden de mensajes y coordinación entre procesos.&lt;/p&gt;

&lt;p&gt;El código completo está disponible en el repositorio &lt;a href="https://github.com/olcortesb/aws-examples/tree/main/sqs/batch-messages" rel="noopener noreferrer"&gt;aws-examples&lt;/a&gt; con todas las implementaciones y documentación detallada.&lt;/p&gt;

&lt;p&gt;Gracias por leer, ¡Saludos!&lt;/p&gt;

</description>
      <category>serverless</category>
      <category>spanish</category>
      <category>architecture</category>
      <category>aws</category>
    </item>
    <item>
      <title>Kiro: Primeras impresiones y configuración de MCPs</title>
      <dc:creator>olcortesb</dc:creator>
      <pubDate>Tue, 23 Sep 2025 07:48:02 +0000</pubDate>
      <link>https://dev.to/aws-espanol/kiro-primeras-impresiones-y-configuracion-de-mcps-4nfc</link>
      <guid>https://dev.to/aws-espanol/kiro-primeras-impresiones-y-configuracion-de-mcps-4nfc</guid>
      <description>&lt;p&gt;No soy un usuario avanzado de herramientas de IA, sin embargo en el último &lt;a href="https://www.meetup.com/madrid-amazon-web-services-meetup/events/310420236/?eventOrigin=group_past_events" rel="noopener noreferrer"&gt;AWS Meetup Madrid&lt;/a&gt;, nos acompañó &lt;a href="https://www.linkedin.com/in/alevzfdez/" rel="noopener noreferrer"&gt;Alejandro Veliz Fernandes&lt;/a&gt; como uno de los ponentes. Durante su charla en demos interactivas nos mostró una visión general de cómo funciona &lt;a href="https://kiro.dev/" rel="noopener noreferrer"&gt;KIRO&lt;/a&gt;, entrando en detalles en la parte que más me interesa de la IA, los límites de la herramienta (Guard rail) y la seguridad de nuestra información. Después de escuchar y tener una charla interesante con Alejandro y coincidiendo que me llegó el código de acceso a probar KIRO, aprovecharemos este &lt;em&gt;post&lt;/em&gt; para explorar &lt;strong&gt;KIRO&lt;/strong&gt;, un asistente de IA especializado en desarrollo de software, revisaremos cómo configurar el &lt;strong&gt;Model Context Protocol (MCP)&lt;/strong&gt; para extender sus capacidades (intentando usarlo para algo mas que un chatbot) y revisaremos su configuración básica.&lt;/p&gt;

&lt;h2&gt;
  
  
  🚀 En primer lugar ¿Qué es Kiro?
&lt;/h2&gt;

&lt;p&gt;Mi opinión: &lt;strong&gt;Kiro&lt;/strong&gt; es un IDE que en su esencia es un fork de VSCode (es igual...) soportado en su desarrollo por AWS, que permite el acceso a él sin tener una cuenta de AWS y orientado a un desarrollo basado en especificaciones. Kiro tiene una versión free y varias alternativas de pago que exploraremos más adelante en futuros posts.&lt;/p&gt;

&lt;p&gt;Según la documentación oficial de AWS: Kiro es una herramienta que permite construir exactamente lo que quieres y está listo para compartir con tu equipo. Los agentes de Kiro te ayudan a resolver problemas desafiantes y automatizar tareas como generar documentación y pruebas unitarias.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/signin/latest/userguide/builder_id-apps.html" rel="noopener noreferrer"&gt;Documentación oficial AWS - Kiro&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Desarrollo spec-driven&lt;/strong&gt;: Convierte prompts en especificaciones detalladas y código funcional&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Agentes inteligentes&lt;/strong&gt;: Automatización de tareas complejas como documentación y testing&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Autonomía configurable&lt;/strong&gt;: Modo Autopilot para cambios automáticos o modo Supervisado para revisión&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Contexto inteligente&lt;/strong&gt;: Acceso a archivos (#File), carpetas (#Folder), problemas (#Problems), terminal (#Terminal) y diferencias de Git (#Git Diff)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Integración completa&lt;/strong&gt;: Puede leer, escribir, modificar archivos y ejecutar comandos del sistema 🚥 + ⚠️.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Extensibilidad&lt;/strong&gt;: Soporte para MCP (Model Context Protocol) para conectar herramientas externas&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  ¿ Como instalarlo?
&lt;/h2&gt;

&lt;p&gt;Al momento de escribir este post el acceso es a través de un &lt;em&gt;waitlist&lt;/em&gt; y está en "Preview", pero se está liberando rápido: &lt;a href="https://kiro.dev/waitlist/" rel="noopener noreferrer"&gt;https://kiro.dev/waitlist/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmvybno4i589z2bouh7jt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmvybno4i589z2bouh7jt.png" alt="kiro_5" width="800" height="248"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  ¿Qué tiene de novedoso? para mí...
&lt;/h2&gt;

&lt;p&gt;Al abrir el panel de Kiro se despliega una barra lateral por defecto que tiene 4 elementos: Specs, Agent Hooks, Agent Steering, MCP Servers, en principio tengo pensado escribir un post de cada uno (este será sobre MCP), pero vamos a describirlos:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fngbeyv8vtb8enuktvv78.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fngbeyv8vtb8enuktvv78.png" alt="kiro_6" width="359" height="180"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Specs (Especificaciones)
&lt;/h3&gt;

&lt;p&gt;Las Specs son una forma estructurada de construir y documentar funcionalidades que quieres desarrollar con Kiro. Es básicamente una formalización del proceso de diseño e implementación que te permite:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Iterar con el agente sobre requerimientos, diseño y tareas de implementación&lt;/li&gt;
&lt;li&gt;Desarrollo incremental de funcionalidades complejas con control y retroalimentación&lt;/li&gt;
&lt;li&gt;Documentar el proceso desde la concepción hasta la implementación&lt;/li&gt;
&lt;li&gt;Incluir referencias a archivos usando la sintaxis #[[file:]] para incorporar specs de OpenAPI, GraphQL, etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Steering (Dirección/Guía)
&lt;/h2&gt;

&lt;p&gt;Steering te permite incluir contexto adicional e instrucciones que se aplican a todas o algunas de tus interacciones con Kiro. Se almacenan en .kiro/steering/*.md y pueden ser:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tipos de inclusión:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Siempre incluidos (comportamiento por defecto)&lt;/li&gt;
&lt;li&gt;Condicionales cuando se lee un archivo específico (usando front-matter con inclusion: fileMatch y fileMatchPattern: 'README*')&lt;/li&gt;
&lt;li&gt;Manuales cuando los proporcionas vía contexto con '#' en el chat (configurado con inclusion: manual)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Agent Hooks (Automatización a la mano...)
&lt;/h2&gt;

&lt;p&gt;Los Agent Hooks son la capacidad de Kiro para crear ejecuciones automáticas del agente que se activan cuando ocurre un evento específico (o cuando el usuario hace clic en un botón) en el IDE.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ejemplos de hooks:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Al guardar código: Cuando guardas un archivo de código, se activa automáticamente una ejecución para actualizar y ejecutar tests&lt;/li&gt;
&lt;li&gt;Al actualizar traducciones: Cuando modificas strings de traducción, se asegura de que otros idiomas también se actualicen&lt;/li&gt;
&lt;li&gt;Hook manual de spell-check: Cuando haces clic en un botón de 'revisión ortográfica', revisa y corrige errores gramaticales en tu archivo README&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🔧 ¿Qué es el Model Context Protocol (MCP) - El 4 de las lista?
&lt;/h2&gt;

&lt;p&gt;El &lt;strong&gt;Model Context Protocol (MCP)&lt;/strong&gt; es un estándar que permite a aplicaciones de IA conectarse con herramientas y servicios externos, extendiendo significativamente sus capacidades más allá del desarrollo básico. En nuestro caso la aplicación de IA que queremos extender es Kiro.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Beneficios del MCP:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Extensibilidad&lt;/strong&gt;: Conecta con APIs, bases de datos, servicios cloud&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automatización&lt;/strong&gt;: Integra workflows complejos en tu flujo de desarrollo&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Personalización&lt;/strong&gt;: Adapta Kiro a las necesidades específicas de tu proyecto&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://modelcontextprotocol.io/" rel="noopener noreferrer"&gt;Documentación oficial MCP&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  📋 Configuración Inicial de MCP
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Paso 1: Estructura de configuración
&lt;/h3&gt;

&lt;p&gt;Kiro utiliza archivos de configuración JSON para gestionar las conexiones MCP, tenemos dos opciones , configuración a nivel global y configuración a nivel de workspaces:&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;# Configuración a nivel de workspace&lt;/span&gt;
.kiro/settings/mcp.json

&lt;span class="c"&gt;# Configuración global del usuario&lt;/span&gt;
~/.kiro/settings/mcp.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Paso 2: Instalación de dependencias
&lt;/h3&gt;

&lt;p&gt;Para la mayoría de servidores MCP necesitarás &lt;strong&gt;uv&lt;/strong&gt; y &lt;strong&gt;uvx&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;# Instalación usando pip&lt;/span&gt;
pip &lt;span class="nb"&gt;install &lt;/span&gt;uv

&lt;span class="c"&gt;# O usando homebrew (macOS)&lt;/span&gt;
brew &lt;span class="nb"&gt;install &lt;/span&gt;uv

&lt;span class="c"&gt;# Verificar instalación&lt;/span&gt;
uvx &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🛠️ Configuración de Servidores MCP
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Ejemplo 1: Servidor de Documentación AWS
&lt;/h3&gt;

&lt;p&gt;Configuramos un servidor MCP para acceder a la documentación de AWS:&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;"mcpServers"&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;"aws-docs"&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;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"uvx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&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="s2"&gt;"awslabs.aws-documentation-mcp-server@latest"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"env"&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;"FASTMCP_LOG_LEVEL"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ERROR"&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;"disabled"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"autoApprove"&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;"disabledTools"&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;Y podemos ver en el panel lateral específico de Kiro en la sección de MCP los que tenemos configurados y activos o desactivados, como muestra la imagen:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1zvviro6ekn3blgx6xi5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1zvviro6ekn3blgx6xi5.png" alt="kiro_4" width="253" height="160"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Ejemplo 2: Servidor de Sistema de Archivos
&lt;/h3&gt;

&lt;p&gt;Para operaciones avanzadas con archivos y directorios:&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;"mcpServers"&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;"filesystem"&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;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"uvx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&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="s2"&gt;"mcp-server-filesystem@latest"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"env"&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;"ALLOWED_DIRECTORIES"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/home/user/projects,/tmp"&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;"disabled"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"autoApprove"&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="s2"&gt;"list_directory"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"read_file"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"disabledTools"&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="s2"&gt;"delete_file"&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;h2&gt;
  
  
  🧪 Casos de Uso Prácticos
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Caso 1: Consulta de Documentación AWS
&lt;/h3&gt;

&lt;p&gt;Con el servidor AWS configurado, puedes preguntar directamente:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"¿Cómo configuro un bucket S3 con cifrado?"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2vruypb1qregzacszljb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2vruypb1qregzacszljb.png" alt="kiro-2" width="348" height="224"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fadjhzim1dbhf8wnc9d9r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fadjhzim1dbhf8wnc9d9r.png" alt="kiro-2" width="352" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Kiro accederá automáticamente a la documentación oficial de AWS y te proporcionará información actualizada y precisa.&lt;/p&gt;

&lt;h3&gt;
  
  
  Caso 2: Análisis de Logs del Sistema
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"Analiza los logs de error de la última hora en /var/log/application.log"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Con el servidor de filesystem, Kiro puede leer y analizar archivos de log directamente.&lt;/p&gt;

&lt;h3&gt;
  
  
  Caso 3: Integración con APIs Externas
&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;"mcpServers"&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;"github-api"&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;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"uvx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&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="s2"&gt;"mcp-server-github@latest"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"env"&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;"GITHUB_TOKEN"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"${GITHUB_TOKEN}"&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;"disabled"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"autoApprove"&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="s2"&gt;"get_repository"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"list_issues"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"disabledTools"&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="s2"&gt;"delete_repository"&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;h2&gt;
  
  
  ⚙️ Configuración Avanzada para el MCP
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Gestión de Permisos ( Fundamental )
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;autoApprove&lt;/strong&gt;: Lista de herramientas que se ejecutan automáticamente sin confirmación&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="nl"&gt;"autoApprove"&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="s2"&gt;"read_file"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"list_directory"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"get_documentation"&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;disabledTools&lt;/strong&gt;: Herramientas específicas que quieres deshabilitar&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="nl"&gt;"disabledTools"&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="s2"&gt;"delete_file"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"format_disk"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"shutdown_system"&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;
  
  
  Variables de Entorno (Usar en local, nunca subir al repositorio)
&lt;/h3&gt;

&lt;p&gt;Permite usar variables de entorno para configuraciones sensibles, usar responsablemente ⚠️: Sigo sin tener claro que podamos pasar a cualquier herramienta información sensible de cliente pero evaluaré qué ofrece Kiro en este aspecto a futuro.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Recomendado a nivel de Workspace: .kiro/settings/mcp.json&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&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;"env"&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;"API_KEY"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"${MY_API_KEY}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"DATABASE_URL"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"${DB_CONNECTION_STRING}"&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;h2&gt;
  
  
  🔗 Referencias y Enlaces Útiles
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://kiro.ai/docs" rel="noopener noreferrer"&gt;Documentación oficial de Kiro&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://modelcontextprotocol.io/" rel="noopener noreferrer"&gt;Model Context Protocol Specification&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.astral.sh/uv/getting-started/installation/" rel="noopener noreferrer"&gt;Instalación de uv&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/modelcontextprotocol/servers" rel="noopener noreferrer"&gt;Repositorio MCP Servers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/awslabs/aws-documentation-mcp-server" rel="noopener noreferrer"&gt;AWS MCP Server&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kiro.ai/docs/steering" rel="noopener noreferrer"&gt;Kiro Steering Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kiro.ai/docs/hooks" rel="noopener noreferrer"&gt;Kiro Hooks Guide&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  📈 Conclusiones
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;✅ &lt;strong&gt;Kiro&lt;/strong&gt; Es un fork de VSCode, esto maximiza la compatibilidad.&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Kiro + MCP&lt;/strong&gt; Me pareció ágil de configurar.&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Configuración flexible&lt;/strong&gt; me parece interesante que tenga configuración para cada contexto y se puedan tener MCP en cada proyecto de manera independiente.&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Extensibilidad ilimitada&lt;/strong&gt; No soy experto en MCP, pero aparentemente si lo puedes configurar puedes usarlo.&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Integración nativa&lt;/strong&gt; Es compatible con lo que la mayoría usamos todos los días VSCode.&lt;/li&gt;
&lt;li&gt;✅ Es una alternativa interesante a &lt;a href="https://cursor.com/" rel="noopener noreferrer"&gt;cursor&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;⚠️ Sigo sin usarlo para que me genere código de aplicaciones de manera directa para cliente final.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Próximos pasos:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Explorar los SPECS y tratar de saltarlos/romperlos para ver cómo funcionan.&lt;/li&gt;
&lt;li&gt;Configurar hooks automáticos para tareas repetitivas, con esto puede estar interesante el proceso de mejorar nuestro desarrollo.&lt;/li&gt;
&lt;li&gt;Implementar steering rules para estándares de equipo&lt;/li&gt;
&lt;li&gt;Crear servidores MCP personalizados para APIs internas a largo plazo claro ☺️.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Gracias por leer, Saludos…&lt;/p&gt;

</description>
      <category>aws</category>
      <category>kiro</category>
      <category>programming</category>
    </item>
    <item>
      <title>mimic 👓 (Api gateway + Lambda + Dynamo) en golang</title>
      <dc:creator>olcortesb</dc:creator>
      <pubDate>Tue, 12 Aug 2025 09:17:45 +0000</pubDate>
      <link>https://dev.to/aws-espanol/mimic-api-gateway-lambda-dynamo-en-golang-3m9g</link>
      <guid>https://dev.to/aws-espanol/mimic-api-gateway-lambda-dynamo-en-golang-3m9g</guid>
      <description>&lt;p&gt;Continuando con la serie de &lt;a href="https://www.youtube.com/watch?v=2pu0ojBH7hI&amp;amp;list" rel="noopener noreferrer"&gt;&lt;strong&gt;mimic&lt;/strong&gt;&lt;/a&gt;, después de explorar la implementación básica en JavaScript y la relación con las distintas herramientas de IaC, vamos a probar esta implementación con &lt;strong&gt;Go y Terraform&lt;/strong&gt;. En este artículo te muestro cómo crear una API completa de almacenamiento JSON usando Lambda en Go, API Gateway y DynamoDB.&lt;/p&gt;




&lt;h2&gt;
  
  
  ¿Qué es mimic?
&lt;/h2&gt;

&lt;p&gt;Como vimos en el &lt;a href="https://olcortesb.hashnode.dev/comparing-different-iac-tools-for-serverless-deployment-1-mimic-serverless-lab" rel="noopener noreferrer"&gt;artículo anterior&lt;/a&gt;, &lt;em&gt;mimic&lt;/em&gt; es un stack serverless simple, que permite:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;POST&lt;/strong&gt; &lt;code&gt;/mimic&lt;/code&gt; - Almacenar cualquier JSON y obtener un ID único&lt;br&gt;&lt;br&gt;
&lt;strong&gt;GET&lt;/strong&gt; &lt;code&gt;/mimic/{id}&lt;/code&gt; - Recuperar el JSON almacenado por su ID&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Es como una base de datos en memoria que acepta cualquier estructura JSON, perfecta para testing, &lt;em&gt;mocking&lt;/em&gt; de servicios y entornos efímeros.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7n30ziexp50q0ng5nom4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7n30ziexp50q0ng5nom4.png" alt="Imagen: Diagrama de arquitectura mimic con Go" width="732" height="335"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  ¿Por qué Go + Terraform?
&lt;/h2&gt;

&lt;p&gt;En el artículo sobre &lt;a href="https://olcortesb.hashnode.dev/golang-lambda-terraform" rel="noopener noreferrer"&gt;Lambda en Go con Terraform&lt;/a&gt;, exploramos las ventajas del runtime &lt;code&gt;provided.al2023&lt;/code&gt;. Para mimic, estas ventajas se multiplican:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Rendimiento superior&lt;/strong&gt;: Go compila a binarios nativos, ideal para &lt;em&gt;APIs&lt;/em&gt; de alta frecuencia&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Costo optimizado&lt;/strong&gt;: ARM64 (Graviton2) reduce costos hasta 50%&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Escalabilidad&lt;/strong&gt;: Cada operación (POST/GET) tiene su propia Lambda&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Infraestructura como código&lt;/strong&gt;: Terraform nos da control total y reproducibilidad&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Estructura del proyecto
&lt;/h2&gt;

&lt;p&gt;Aquí &lt;a href="https://github.com/olcortesb/golang-serverless-terraform/tree/main/01_GST_mimic" rel="noopener noreferrer"&gt;Link&lt;/a&gt; dejaré el repositorio con el código completo.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="m"&gt;01&lt;/span&gt;&lt;span class="n"&gt;_GST_mimic&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;
&lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="n"&gt;src&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;   &lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;   &lt;span class="err"&gt;│&lt;/span&gt;   &lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="n"&gt;go&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mod&lt;/span&gt;          &lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="n"&gt;M&lt;/span&gt;&lt;span class="err"&gt;ó&lt;/span&gt;&lt;span class="n"&gt;dulo&lt;/span&gt; &lt;span class="n"&gt;Go&lt;/span&gt; &lt;span class="n"&gt;para&lt;/span&gt; &lt;span class="n"&gt;POST&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;   &lt;span class="err"&gt;│&lt;/span&gt;   &lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;go&lt;/span&gt;         &lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="n"&gt;Handler&lt;/span&gt; &lt;span class="n"&gt;POST&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;   &lt;span class="err"&gt;│&lt;/span&gt;   &lt;span class="err"&gt;└──&lt;/span&gt; &lt;span class="n"&gt;bootstrap&lt;/span&gt;       &lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="n"&gt;Binario&lt;/span&gt; &lt;span class="n"&gt;compilado&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;   &lt;span class="err"&gt;└──&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;       &lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="n"&gt;go&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mod&lt;/span&gt;          &lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="n"&gt;M&lt;/span&gt;&lt;span class="err"&gt;ó&lt;/span&gt;&lt;span class="n"&gt;dulo&lt;/span&gt; &lt;span class="n"&gt;Go&lt;/span&gt; &lt;span class="n"&gt;para&lt;/span&gt; &lt;span class="n"&gt;GET&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;       &lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;go&lt;/span&gt;         &lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="n"&gt;Handler&lt;/span&gt; &lt;span class="n"&gt;GET&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;       &lt;span class="err"&gt;└──&lt;/span&gt; &lt;span class="n"&gt;bootstrap&lt;/span&gt;       &lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="n"&gt;Binario&lt;/span&gt; &lt;span class="n"&gt;compilado&lt;/span&gt;
&lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="n"&gt;apigateway&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tf&lt;/span&gt;           &lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="n"&gt;API&lt;/span&gt; &lt;span class="n"&gt;Gateway&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="n"&gt;API&lt;/span&gt; &lt;span class="n"&gt;Key&lt;/span&gt;
&lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="n"&gt;dynamo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tf&lt;/span&gt;               &lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="n"&gt;Tabla&lt;/span&gt; &lt;span class="n"&gt;DynamoDB&lt;/span&gt;
&lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="n"&gt;lambdarequest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tf&lt;/span&gt;        &lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="n"&gt;Lambda&lt;/span&gt; &lt;span class="n"&gt;POST&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="n"&gt;IAM&lt;/span&gt;
&lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="n"&gt;lambdaresponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tf&lt;/span&gt;       &lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="n"&gt;Lambda&lt;/span&gt; &lt;span class="n"&gt;GET&lt;/span&gt;
&lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tf&lt;/span&gt;               &lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="n"&gt;Sufijos&lt;/span&gt; &lt;span class="n"&gt;aleatorios&lt;/span&gt;
&lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="n"&gt;variables&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tf&lt;/span&gt;            &lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="n"&gt;Variables&lt;/span&gt; &lt;span class="n"&gt;configurables&lt;/span&gt;
&lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="n"&gt;outputs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tf&lt;/span&gt;              &lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="n"&gt;Outputs&lt;/span&gt; &lt;span class="n"&gt;del&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="err"&gt;ó&lt;/span&gt;&lt;span class="n"&gt;dulo&lt;/span&gt;
&lt;span class="err"&gt;└──&lt;/span&gt; &lt;span class="n"&gt;README&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;md&lt;/span&gt;               &lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="n"&gt;Documentaci&lt;/span&gt;&lt;span class="err"&gt;ó&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Implementación en Go
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Lambda POST (Request)
&lt;/h3&gt;

&lt;p&gt;La lambda de almacenamiento acepta cualquier JSON válido(Esto está así a propósito):&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;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;"context"&lt;/span&gt;
    &lt;span class="s"&gt;"encoding/json"&lt;/span&gt;
    &lt;span class="s"&gt;"log"&lt;/span&gt;
    &lt;span class="s"&gt;"os"&lt;/span&gt;

    &lt;span class="s"&gt;"github.com/aws/aws-lambda-go/events"&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/aws/aws-lambda-go/lambda"&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/aws/aws-sdk-go/aws"&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/aws/aws-sdk-go/aws/session"&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/aws/aws-sdk-go/service/dynamodb"&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/aws/aws-sdk-go/service/dynamodb/dynamodbattribute"&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;MimicItem&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;ID&lt;/span&gt;   &lt;span class="kt"&gt;string&lt;/span&gt;                 &lt;span class="s"&gt;`json:"id"`&lt;/span&gt;
    &lt;span class="n"&gt;Body&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;`json:"body"`&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;dynamoClient&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;DynamoDB&lt;/span&gt;
&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;tableName&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;init&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;sess&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;Must&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;NewSession&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="n"&gt;dynamoClient&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;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sess&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;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"MIMIC_TABLE"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;tableName&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;span class="n"&gt;tableName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"mimic-table"&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="n"&gt;createBodyResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;body&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="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="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="n"&gt;id&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;mimicItem&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;MimicItem&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;ID&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;   &lt;span class="n"&gt;id&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;body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;av&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;dynamodbattribute&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;mimicItem&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&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;return&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
    &lt;span class="p"&gt;}&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;dynamoClient&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="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;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;av&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;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&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;events&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;APIGatewayProxyRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;events&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;APIGatewayProxyResponse&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="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Creating new mimic item"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;body&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="n"&gt;err&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;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;body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Error parsing JSON: %v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&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;events&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;APIGatewayProxyResponse&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;400&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="s"&gt;"Invalid JSON"&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="n"&gt;id&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;createBodyResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Error creating item: %v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&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;events&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;APIGatewayProxyResponse&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;500&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="s"&gt;"Internal server error"&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="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Created item with ID: %s"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;id&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;events&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;APIGatewayProxyResponse&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;200&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;id&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;Clave&lt;/strong&gt;: Usamos &lt;code&gt;map[string]interface{}&lt;/code&gt; para aceptar cualquier estructura JSON sin validaciones específicas.&lt;/p&gt;




&lt;h3&gt;
  
  
  Lambda GET (Response)
&lt;/h3&gt;

&lt;p&gt;La lambda de recuperación devuelve el JSON original:&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="c"&gt;// get.go&lt;/span&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;"context"&lt;/span&gt;
    &lt;span class="s"&gt;"encoding/json"&lt;/span&gt;
    &lt;span class="s"&gt;"fmt"&lt;/span&gt;
    &lt;span class="s"&gt;"log"&lt;/span&gt;
    &lt;span class="s"&gt;"os"&lt;/span&gt;

    &lt;span class="s"&gt;"github.com/aws/aws-lambda-go/events"&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/aws/aws-lambda-go/lambda"&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/aws/aws-sdk-go/aws"&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/aws/aws-sdk-go/aws/session"&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/aws/aws-sdk-go/service/dynamodb"&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/aws/aws-sdk-go/service/dynamodb/dynamodbattribute"&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;MimicItem&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;ID&lt;/span&gt;   &lt;span class="kt"&gt;string&lt;/span&gt;                 &lt;span class="s"&gt;`json:"id"`&lt;/span&gt;
    &lt;span class="n"&gt;Body&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;`json:"body"`&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;dynamoClient&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;DynamoDB&lt;/span&gt;
&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;tableName&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;init&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;sess&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;Must&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;NewSession&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="n"&gt;dynamoClient&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;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sess&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;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"MIMIC_TABLE"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;tableName&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;span class="n"&gt;tableName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"mimic-table"&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="n"&gt;getBodyResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;MimicItem&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="n"&gt;result&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;dynamoClient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetItem&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;GetItemInput&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;tableName&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;Key&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="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;AttributeValue&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="s"&gt;"id"&lt;/span&gt;&lt;span class="o"&gt;:&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;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;id&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="p"&gt;})&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&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;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Item&lt;/span&gt; &lt;span class="o"&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;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Errorf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"item not found"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="n"&gt;MimicItem&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;dynamodbattribute&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UnmarshalMap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Item&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;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&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;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&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;item&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;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;events&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;APIGatewayProxyRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;events&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;APIGatewayProxyResponse&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="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exists&lt;/span&gt; &lt;span class="o"&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;PathParameters&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;exists&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;events&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;APIGatewayProxyResponse&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;400&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="s"&gt;"Missing id parameter"&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="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Getting mimic item with ID: %s"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;id&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="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;getBodyResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Error getting item: %v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&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;events&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;APIGatewayProxyResponse&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;404&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="s"&gt;"Item not found"&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="n"&gt;responseBody&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;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;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Error marshaling response: %v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&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;events&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;APIGatewayProxyResponse&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;500&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="s"&gt;"Internal server error"&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;return&lt;/span&gt; &lt;span class="n"&gt;events&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;APIGatewayProxyResponse&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;200&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="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;responseBody&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;Headers&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="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="s"&gt;"Content-Type"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"application/json"&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="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;h2&gt;
  
  
  Infraestructura Terraform
&lt;/h2&gt;

&lt;h3&gt;
  
  
  DynamoDB en terraform.
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;resource&lt;/span&gt; &lt;span class="s"&gt;"aws_dynamodb_table"&lt;/span&gt; &lt;span class="s"&gt;"mimic_table"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;name&lt;/span&gt;           &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"${var.table_name}-${random_id.suffix.hex}"&lt;/span&gt;
  &lt;span class="n"&gt;billing_mode&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"PAY_PER_REQUEST"&lt;/span&gt;
  &lt;span class="n"&gt;hash_key&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"id"&lt;/span&gt;

  &lt;span class="n"&gt;attribute&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"id"&lt;/span&gt;
    &lt;span class="n"&gt;type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"S"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="n"&gt;point_in_time_recovery&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;enabled&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="n"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"MimicTable"&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;
  
  
  API Gateway con API Key y Cuotas en terraform
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="n"&gt;API&lt;/span&gt; &lt;span class="n"&gt;Gateway&lt;/span&gt; &lt;span class="n"&gt;con&lt;/span&gt; &lt;span class="n"&gt;autenticaci&lt;/span&gt;&lt;span class="err"&gt;ó&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;
&lt;span class="n"&gt;resource&lt;/span&gt; &lt;span class="s"&gt;"aws_api_gateway_rest_api"&lt;/span&gt; &lt;span class="s"&gt;"mimic_api"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;name&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"${var.api_name}-${random_id.suffix.hex}"&lt;/span&gt;
  &lt;span class="n"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Mimic API for storing and retrieving JSON data"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="n"&gt;API&lt;/span&gt; &lt;span class="n"&gt;Key&lt;/span&gt; &lt;span class="n"&gt;para&lt;/span&gt; &lt;span class="n"&gt;autenticaci&lt;/span&gt;&lt;span class="err"&gt;ó&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;
&lt;span class="n"&gt;resource&lt;/span&gt; &lt;span class="s"&gt;"aws_api_gateway_api_key"&lt;/span&gt; &lt;span class="s"&gt;"mimic_api_key"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"${var.api_name}-key-${random_id.suffix.hex}"&lt;/span&gt;
  &lt;span class="n"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"API Key for Mimic API"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="n"&gt;Usage&lt;/span&gt; &lt;span class="n"&gt;Plan&lt;/span&gt; &lt;span class="n"&gt;con&lt;/span&gt; &lt;span class="n"&gt;cuotas&lt;/span&gt; &lt;span class="n"&gt;mensuales&lt;/span&gt;
&lt;span class="n"&gt;resource&lt;/span&gt; &lt;span class="s"&gt;"aws_api_gateway_usage_plan"&lt;/span&gt; &lt;span class="s"&gt;"mimic_usage_plan"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"${var.api_name}-usage-plan-${random_id.suffix.hex}"&lt;/span&gt;

  &lt;span class="n"&gt;quota_settings&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;limit&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;api_quota_limit&lt;/span&gt;  &lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="m"&gt;1000&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="n"&gt;month&lt;/span&gt;
    &lt;span class="n"&gt;period&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"MONTH"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="n"&gt;throttle_settings&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;rate_limit&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;api_rate_limit&lt;/span&gt;   &lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="n"&gt;sec&lt;/span&gt;
    &lt;span class="n"&gt;burst_limit&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;api_burst_limit&lt;/span&gt;  &lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="m"&gt;20&lt;/span&gt; &lt;span class="n"&gt;burst&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;
  
  
  Compilación automática separada (No implementar external en PROD ⚠️)
&lt;/h3&gt;

&lt;p&gt;Esto es para el despliegue desde el local, lo recomendable es hacer el build en los pipelines y no usar external.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="n"&gt;Build&lt;/span&gt; &lt;span class="n"&gt;REQUEST&lt;/span&gt; &lt;span class="n"&gt;Lambda&lt;/span&gt;
&lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="s"&gt;"external"&lt;/span&gt; &lt;span class="s"&gt;"build_create_lambda"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;program&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"bash"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"-c"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"cd src/request &amp;amp;&amp;amp; go mod tidy &amp;amp;&amp;amp; env GOOS=linux GOARCH=arm64 go build -o bootstrap main.go &amp;amp;&amp;amp; echo '{\"filename\":\"bootstrap\"}'"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="n"&gt;Build&lt;/span&gt; &lt;span class="n"&gt;RESPONSE&lt;/span&gt; &lt;span class="n"&gt;Lambda&lt;/span&gt;
&lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="s"&gt;"external"&lt;/span&gt; &lt;span class="s"&gt;"build_get_lambda"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;program&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"bash"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"-c"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"cd src/response &amp;amp;&amp;amp; go mod tidy &amp;amp;&amp;amp; env GOOS=linux GOARCH=arm64 go build -o bootstrap main.go &amp;amp;&amp;amp; echo '{\"filename\":\"bootstrap\"}'"&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;Importante&lt;/strong&gt;: Cada lambda tiene su propio módulo Go y se compila independientemente.&lt;/p&gt;




&lt;h2&gt;
  
  
  Despliegue
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Clonar e inicializar&lt;/span&gt;
git clone https://github.com/tu-usuario/golang-serverless-terraform.git
&lt;span class="nb"&gt;cd &lt;/span&gt;golang-serverless-terraform/01_GST_mimic

&lt;span class="c"&gt;# Configurar credenciales AWS &lt;/span&gt;
&lt;span class="c"&gt;# Ref: https://gist.github.com/olcortesb/a471797eb1d45c54ad51d920b78aa664&lt;/span&gt;

&lt;span class="c"&gt;# Desplegar&lt;/span&gt;
terraform init
terraform plan
terraform apply
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Probando la API
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Obtener valores de salida&lt;/span&gt;
&lt;span class="nv"&gt;API_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;terraform output &lt;span class="nt"&gt;-raw&lt;/span&gt; api_gateway_url&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;terraform output &lt;span class="nt"&gt;-raw&lt;/span&gt; api_key_value&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Almacenar JSON de usuario&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="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;API_URL&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/mimic"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"x-api-key: &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;API_KEY&lt;/span&gt;&lt;span class="k"&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;-H&lt;/span&gt; &lt;span class="s2"&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;'{
    "name": "Alice",
    "email": "alice@example.com",
    "preferences": {
      "theme": "dark",
      "notifications": true
    }
  }'&lt;/span&gt;

&lt;span class="c"&gt;# Respuesta: "550e8400-e29b-41d4-a716-446655440000"&lt;/span&gt;

&lt;span class="c"&gt;# Recuperar JSON&lt;/span&gt;
curl &lt;span class="nt"&gt;-X&lt;/span&gt; GET &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;API_URL&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/mimic/550e8400-e29b-41d4-a716-446655440000"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"x-api-key: &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;API_KEY&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Comparativa Js vs Go
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Aspecto&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;JavaScript&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Go&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cold Start&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;~200ms&lt;/td&gt;
&lt;td&gt;~50ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Memoria&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;128 MB mínimo&lt;/td&gt;
&lt;td&gt;128 MB eficiente&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Costo&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;x86_64 estándar&lt;/td&gt;
&lt;td&gt;ARM64 (-50%) Aproximado&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Tipado&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Dinámico&lt;/td&gt;
&lt;td&gt;Estático&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Compilación&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Runtime&lt;/td&gt;
&lt;td&gt;Build time&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Monitoreo y observabilidad
&lt;/h2&gt;

&lt;p&gt;Terraform automáticamente configura:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;CloudWatch Logs&lt;/strong&gt;: Para debugging de las lambdas&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;API Gateway Metrics&lt;/strong&gt;: Latencia, errores, throttling&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;DynamoDB Metrics&lt;/strong&gt;: Read/Write capacity, throttling&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Usage Plan Monitoring&lt;/strong&gt;: Cuotas y límites de rate&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Limpieza
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform destroy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Conclusiones
&lt;/h2&gt;

&lt;p&gt;La implementación de &lt;strong&gt;mimic en Go + Terraform&lt;/strong&gt; nos ofrece:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Rendimiento superior&lt;/strong&gt;: Cold starts más rápidos y mejor throughput&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Costo optimizado&lt;/strong&gt;: ARM64 reduce significativamente los costos&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Infraestructura reproducible&lt;/strong&gt;: Terraform garantiza consistencia&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;POC&lt;/strong&gt;: Utilizaré este código como una POC para ir mejorando, actualizando y realizando pruebas sobre Golang + AWS Lambda&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Este stack es perfecto para:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Entornos de desarrollo y testing&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Mocking de servicios externos&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Prototipado rápido de APIs&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Cache temporal de datos&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;🙋 Laboratorios de infraestructura (Fundamentalmente para lo que lo uso …)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;En el próximo artículo exploraremos cómo integrar &lt;strong&gt;mimic&lt;/strong&gt; con otros servicios AWS como S3 Events y SQS para crear arquitecturas event-driven más complejas.&lt;/p&gt;

&lt;p&gt;¡Gracias por leer, saludos!&lt;/p&gt;




&lt;h2&gt;
  
  
  Referencias
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/olcortesb/mimic-src" rel="noopener noreferrer"&gt;https://github.com/olcortesb/mimic-src&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/lambda-golang.html" rel="noopener noreferrer"&gt;https://docs.aws.amazon.com/lambda/latest/dg/lambda-golang.html&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.terraform.io/" rel="noopener noreferrer"&gt;https://www.terraform.io/&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/api_gateway_rest_api" rel="noopener noreferrer"&gt;https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/api_gateway_rest_api&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>aws</category>
      <category>terraform</category>
      <category>go</category>
    </item>
    <item>
      <title>Probando AWS CodeCatalyst🚀 desde el AWS Builder ID 👷</title>
      <dc:creator>olcortesb</dc:creator>
      <pubDate>Wed, 06 Aug 2025 11:43:20 +0000</pubDate>
      <link>https://dev.to/aws-espanol/probando-aws-codecatalyst-desde-el-aws-builder-id-595m</link>
      <guid>https://dev.to/aws-espanol/probando-aws-codecatalyst-desde-el-aws-builder-id-595m</guid>
      <description>&lt;p&gt;Source: &lt;a href="https://olcortesb.hashnode.dev/probando-aws-codecatalyst-desde-el-aws-builder-id" rel="noopener noreferrer"&gt;https://olcortesb.hashnode.dev/probando-aws-codecatalyst-desde-el-aws-builder-id&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;En este &lt;em&gt;post&lt;/em&gt; mostraré cómo conectarse a &lt;em&gt;AWS CodeCatalyst&lt;/em&gt; desde el ID de Builder, aprovechando la capa gratuita. Realizaremos algunas pruebas de las funcionalidades básicas y, en futuros &lt;em&gt;posts&lt;/em&gt;, profundizaremos en las características avanzadas del servicio.&lt;/p&gt;

&lt;h2&gt;
  
  
  🚀 En primer lugar ¿Qué es AWS CodeCatalyst?
&lt;/h2&gt;

&lt;p&gt;AWS CodeCatalyst es un servicio gestionado por AWS que unifica en una sola plataforma todas las etapas del desarrollo de software: planificación, codificación, pruebas, integración continua (CI), entrega continua (CD) y despliegue.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/codecatalyst/latest/userguide/welcome.html" rel="noopener noreferrer"&gt;https://docs.aws.amazon.com/codecatalyst/latest/userguide/welcome.html&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  👷 y ¿Qué es el AWS Builder ID?
&lt;/h2&gt;

&lt;p&gt;AWS BuilderID es tu &lt;strong&gt;identidad personal unificada&lt;/strong&gt; dentro del ecosistema de AWS. A diferencia de una cuenta de AWS, que está diseñada para acceder a servicios desde la consola o AWS cli, el BuilderID te permite iniciar sesión en herramientas de aprendizaje, desarrollo o colaboración como &lt;strong&gt;Amazon Q Developer&lt;/strong&gt;, &lt;strong&gt;CodeCatalyst&lt;/strong&gt;, entre otros.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/signin/latest/userguide/sign-in-aws_builder_id.html" rel="noopener noreferrer"&gt;https://docs.aws.amazon.com/signin/latest/userguide/sign-in-aws_builder_id.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/signin/latest/userguide/differences-aws_builder_id.html" rel="noopener noreferrer"&gt;https://docs.aws.amazon.com/signin/latest/userguide/differences-aws_builder_id.html&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Conectándonos a CodeCatalyst
&lt;/h2&gt;

&lt;p&gt;Accediendo a la &lt;em&gt;URL&lt;/em&gt; de CodeCatalyst nos da las opciones de conectarnos por Builder ID (1) o SSO (2), ya podemos observar que es un servicio con inicio de sesión y acceso fuera de la consola tradicional de AWS.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Facxe8eo1z4exix4kmr4a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Facxe8eo1z4exix4kmr4a.png" alt="Login" width="800" height="292"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Luego de seleccionar el ID, si es la primera vez que entramos nos solicitará un nombre de espacio de trabajo, región y la cuenta a la que queremos asociar los recursos y servicios que vamos a desplegar, así como el costo.&lt;/p&gt;

&lt;p&gt;Configurados estos elementos pasamos a la pantalla principal de administración del servicio, donde podemos gestionar las cuentas sobre las que actuaremos (1) y los miembros de este espacio de trabajo (2) entre otras configuraciones.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2r5wguslhx3p7slsg9j7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2r5wguslhx3p7slsg9j7.png" alt="Pantalla principal" width="800" height="476"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Creación de un nuevo proyecto
&lt;/h1&gt;

&lt;p&gt;Bien, ahora vamos a crear un proyecto desde un Blueprint y a desplegar la aplicación. Tenemos tres opciones para crear un proyecto, vamos a utilizar la opción desde Blueprint (1) para este post.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvr3ru11m4vdhxczzce06.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvr3ru11m4vdhxczzce06.png" alt="Opciones de proyecto" width="800" height="166"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Al seleccionar Blueprint se despliegan una serie de Blueprints configurados que al elegir alguno (1) nos dará detalles de los servicios que usa, arquitectura y permisos necesarios (2).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpvsik845m5fwnhq7906y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpvsik845m5fwnhq7906y.png" alt="Blueprint selection" width="800" height="299"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;En el siguiente paso podemos seleccionar el nombre del proyecto (1), dónde vamos a almacenar el código (2) que me parece una funcionalidad interesante para equipos que no quieren gestionar su código desde CodeCatalyst, y la cuenta donde vamos a desplegar el servicio (3).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjdcoh6esxoms1esk20ds.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjdcoh6esxoms1esk20ds.png" alt="Configuración del proyecto" width="800" height="635"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Importante para poder acceder a la cuenta realizar una aprobación en la cuenta destino.&lt;/p&gt;

&lt;p&gt;Y también nos pedirá agregar un rol desde la cuenta:&lt;/p&gt;

&lt;p&gt;💡&lt;br&gt;
Importante es necesario agregar una cuenta donde facturar aunque estemos usando el free tier&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpir9bsbtk0bc6oxa4hl1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpir9bsbtk0bc6oxa4hl1.png" alt="Configuración de cuenta" width="800" height="391"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Y finalmente creamos el proyecto, elegimos Python, pero podemos elegir la herramienta de IaC (Infrastructure as Code) que en este caso es &lt;strong&gt;CDK&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzp43f1slvtenwun2sglj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzp43f1slvtenwun2sglj.png" alt="Creación del proyecto" width="800" height="356"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Con un "Run" en la sección CI/CD ejecutamos el pipeline que tiene el Blueprint&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F38fu0ca359zm05ntrox1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F38fu0ca359zm05ntrox1.png" alt="Pipeline execution" width="800" height="543"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Y probando la &lt;strong&gt;API&lt;/strong&gt; validamos que funciona correctamente:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiq7zjcb7xr9rcoihdhur.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiq7zjcb7xr9rcoihdhur.png" alt="API test" width="800" height="233"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Y en la DynamoDB …&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuleiliukq4mtc9flm0wu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuleiliukq4mtc9flm0wu.png" alt="DynamoDB" width="800" height="273"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusiones y comentarios
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Historia
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Diciembre de 2022&lt;/strong&gt;: AWS presentó CodeCatalyst en versión &lt;em&gt;preview&lt;/em&gt; durante el congreso &lt;strong&gt;re:Invent 2022&lt;/strong&gt;, anunciándose como una herramienta todo en uno para flujo DevOps en AWS&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://aws.amazon.com/blogs/aws/announcing-amazon-codecatalyst-preview-a-unified-software-development-service" rel="noopener noreferrer"&gt;announcing-amazon-codecatalyst-preview-a-unified-software-development-service&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;20 de abril de 2023&lt;/strong&gt;: CodeCatalyst alcanzó la &lt;strong&gt;disponibilidad general (GA)&lt;/strong&gt;. Desde entonces, soporta workflows con AWS Graviton, integración de repositorios GitHub o GitLab, gestión de issues vinculadas a pull requests y mucho más&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://aws.amazon.com/about-aws/whats-new/2023/04/general-availability-amazon-codecatalyst/" rel="noopener noreferrer"&gt;general-availability-amazon-codecatalyst&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Actualidad
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;En &lt;strong&gt;julio de 2025&lt;/strong&gt;, se lanzó la imagen universal &lt;code&gt;Universal image 5.0&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/codecatalyst/latest/userguide/doc-history.html" rel="noopener noreferrer"&gt;https://docs.aws.amazon.com/codecatalyst/latest/userguide/doc-history.html&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;También se añadieron mejoras en temas de &lt;strong&gt;privacidad de datos&lt;/strong&gt;, roles, y eliminación de Blueprints obsoletos.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/codecatalyst/latest/userguide/doc-history.html" rel="noopener noreferrer"&gt;Documentación de AWS&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Conclusiones
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Hemos conectado AWS CodeCatalyst a través del &lt;strong&gt;Builder ID&lt;/strong&gt;, conectando una cuenta de destino y desplegando un Blueprint correctamente.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Es un servicio integral que permite centralizar todos los servicios que un equipo de desarrollo necesita para gestionar el ciclo de vida de las aplicaciones en AWS.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Hay mucho camino para probar y desarrollar utilizando las features que CodeCatalyst tiene para ofrecer.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Gracias por leer, Saludos…&lt;/p&gt;

</description>
      <category>aws</category>
      <category>codecatalyst</category>
      <category>awsbuilderid</category>
    </item>
    <item>
      <title>🚀 Golang + ⚡ Lambda + 🏗️Terraform</title>
      <dc:creator>olcortesb</dc:creator>
      <pubDate>Wed, 16 Jul 2025 12:39:04 +0000</pubDate>
      <link>https://dev.to/aws-espanol/golang-lambda-terraform-4o5d</link>
      <guid>https://dev.to/aws-espanol/golang-lambda-terraform-4o5d</guid>
      <description>&lt;p&gt;Source: &lt;a href="https://olcortesb.hashnode.dev/golang-lambda-terraform" rel="noopener noreferrer"&gt;https://olcortesb.hashnode.dev/golang-lambda-terraform&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;Como siempre por estos caminos estoy hablando de Serverless y sus derivados, en esta ocasión contaré un poco sobre como es desplegar funciones &lt;strong&gt;AWS Lambda en Go&lt;/strong&gt; con &lt;a href="https://www.terraform.io/" rel="noopener noreferrer"&gt;Terraform&lt;/a&gt; no es complejo, pero tenemos que tener consideraciones.&lt;/p&gt;

&lt;p&gt;En este artículo te muestro cómo crear una función Lambda básica en &lt;code&gt;Golang&lt;/code&gt; usando el &lt;code&gt;runtime&lt;/code&gt; &lt;code&gt;provided.al2023&lt;/code&gt; y por qué es la mejor opción actualmente.&lt;/p&gt;

&lt;h2&gt;
  
  
  Estructura del proyecto
&lt;/h2&gt;

&lt;p&gt;Aquí &lt;a href="https://github.com/olcortesb/golang-serverless-terraform" rel="noopener noreferrer"&gt;Link&lt;/a&gt; dejaré el repositorio con el código completo.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="m"&gt;00&lt;/span&gt;&lt;span class="n"&gt;_GST_lambda&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;
&lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="n"&gt;src&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;   &lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="n"&gt;go&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mod&lt;/span&gt;          &lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="n"&gt;M&lt;/span&gt;&lt;span class="err"&gt;ó&lt;/span&gt;&lt;span class="n"&gt;dulo&lt;/span&gt; &lt;span class="n"&gt;Go&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;   &lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;go&lt;/span&gt;         &lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="err"&gt;ó&lt;/span&gt;&lt;span class="n"&gt;digo&lt;/span&gt; &lt;span class="n"&gt;fuente&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;   &lt;span class="err"&gt;└──&lt;/span&gt; &lt;span class="n"&gt;bootstrap&lt;/span&gt;       &lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="n"&gt;Binario&lt;/span&gt; &lt;span class="nf"&gt;compilado&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;generado&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tf&lt;/span&gt;             &lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="n"&gt;Recursos&lt;/span&gt; &lt;span class="n"&gt;Terraform&lt;/span&gt;
&lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="n"&gt;variables&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tf&lt;/span&gt;        &lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="n"&gt;Variables&lt;/span&gt; &lt;span class="n"&gt;configurables&lt;/span&gt;
&lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="n"&gt;outputs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tf&lt;/span&gt;          &lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="n"&gt;Outputs&lt;/span&gt; &lt;span class="n"&gt;del&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="err"&gt;ó&lt;/span&gt;&lt;span class="n"&gt;dulo&lt;/span&gt;
&lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="n"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tf&lt;/span&gt;        &lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="n"&gt;Configuraci&lt;/span&gt;&lt;span class="err"&gt;ó&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="n"&gt;de&lt;/span&gt; &lt;span class="n"&gt;providers&lt;/span&gt;
&lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="n"&gt;backend&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tf&lt;/span&gt;          &lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="n"&gt;Backend&lt;/span&gt; &lt;span class="n"&gt;de&lt;/span&gt; &lt;span class="n"&gt;Terraform&lt;/span&gt;
&lt;span class="err"&gt;└──&lt;/span&gt; &lt;span class="n"&gt;README&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;md&lt;/span&gt;           &lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="n"&gt;Documentaci&lt;/span&gt;&lt;span class="err"&gt;ó&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  El código Go
&lt;/h2&gt;

&lt;p&gt;Primero creamos una función Lambda básica en Go. Lo importante aquí es usar la librería oficial &lt;code&gt;aws-lambda-go&lt;/code&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="c"&gt;// main.go&lt;/span&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="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;()&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="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="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"Hello λ!"&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="c"&gt;// Make the handler available for Remote Procedure Call by AWS Lambda&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;hello&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;Y nuestro &lt;code&gt;go.mod&lt;/code&gt; debe tener un nombre diferente al de la dependencia:&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="n"&gt;module&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;function&lt;/span&gt;

&lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="m"&gt;1.23&lt;/span&gt;

&lt;span class="n"&gt;require&lt;/span&gt; &lt;span class="n"&gt;github&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&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;aws&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;lambda&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="n"&gt;v1&lt;/span&gt;&lt;span class="m"&gt;.49.0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Importante&lt;/strong&gt;: El nombre del módulo no puede ser &lt;code&gt;github.com/aws/aws-lambda-go&lt;/code&gt; porque entraría en conflicto con la dependencia.&lt;/p&gt;

&lt;h2&gt;
  
  
  La infraestructura Terraform
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Compilación automática
&lt;/h3&gt;

&lt;p&gt;El truco está en usar un &lt;code&gt;data source external&lt;/code&gt; que compile automáticamente nuestro código Go:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="n"&gt;Crear&lt;/span&gt; &lt;span class="n"&gt;archivo&lt;/span&gt; &lt;span class="n"&gt;ZIP&lt;/span&gt; &lt;span class="n"&gt;con&lt;/span&gt; &lt;span class="n"&gt;el&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="err"&gt;ó&lt;/span&gt;&lt;span class="n"&gt;digo&lt;/span&gt; &lt;span class="n"&gt;compilado&lt;/span&gt;
&lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="s"&gt;"external"&lt;/span&gt; &lt;span class="s"&gt;"build_lambda"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;program&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"bash"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"-c"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"cd src &amp;amp;&amp;amp; env GOOS=linux GOARCH=arm64 go build -o bootstrap main.go &amp;amp;&amp;amp; echo '{\"filename\":\"bootstrap\"}'"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="s"&gt;"archive_file"&lt;/span&gt; &lt;span class="s"&gt;"lambda_zip"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"zip"&lt;/span&gt;
  &lt;span class="n"&gt;source_file&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"${path.module}/src/bootstrap"&lt;/span&gt;
  &lt;span class="n"&gt;output_path&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"${path.module}/lambda_function.zip"&lt;/span&gt;
  &lt;span class="n"&gt;depends_on&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;external&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;build_lambda&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;Clave&lt;/strong&gt;: El ejecutable debe llamarse &lt;code&gt;bootstrap&lt;/code&gt;, no &lt;code&gt;main&lt;/code&gt;. El runtime &lt;code&gt;provided.al2023&lt;/code&gt; busca específicamente este nombre.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkrr2lqwkho9jp1tf113m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkrr2lqwkho9jp1tf113m.png" alt="GST_1" width="195" height="110"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Rol IAM y función Lambda
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="n"&gt;Rol&lt;/span&gt; &lt;span class="n"&gt;IAM&lt;/span&gt; &lt;span class="n"&gt;para&lt;/span&gt; &lt;span class="n"&gt;Lambda&lt;/span&gt;
&lt;span class="n"&gt;resource&lt;/span&gt; &lt;span class="s"&gt;"aws_iam_role"&lt;/span&gt; &lt;span class="s"&gt;"lambda_role"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;role_name&lt;/span&gt;

  &lt;span class="n"&gt;assume_role_policy&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;jsonencode&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="n"&gt;Version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"2012-10-17"&lt;/span&gt;
    &lt;span class="n"&gt;Statement&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="n"&gt;Action&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"sts:AssumeRole"&lt;/span&gt;
        &lt;span class="n"&gt;Effect&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Allow"&lt;/span&gt;
        &lt;span class="n"&gt;Principal&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="n"&gt;Service&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"lambda.amazonaws.com"&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="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="n"&gt;Funci&lt;/span&gt;&lt;span class="err"&gt;ó&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="n"&gt;Lambda&lt;/span&gt;
&lt;span class="n"&gt;resource&lt;/span&gt; &lt;span class="s"&gt;"aws_lambda_function"&lt;/span&gt; &lt;span class="s"&gt;"go_lambda"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;filename&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;archive_file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lambda_zip&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;output_path&lt;/span&gt;
  &lt;span class="n"&gt;function_name&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"go-hello-serverless-lambda"&lt;/span&gt;
  &lt;span class="n"&gt;role&lt;/span&gt;             &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;aws_iam_role&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lambda_role&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;arn&lt;/span&gt;
  &lt;span class="n"&gt;handler&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"main"&lt;/span&gt;
  &lt;span class="n"&gt;runtime&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"provided.al2023"&lt;/span&gt;
  &lt;span class="n"&gt;architectures&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"arm64"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="n"&gt;source_code_hash&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;archive_file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lambda_zip&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;output_base64sha256&lt;/span&gt;

  &lt;span class="n"&gt;depends_on&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="n"&gt;aws_iam_role_policy_attachment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lambda_basic&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;archive_file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lambda_zip&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;h2&gt;
  
  
  🤔 ¿ Por qué usar &lt;code&gt;provided.al2023&lt;/code&gt; en lugar de &lt;code&gt;go1.x&lt;/code&gt;?
&lt;/h2&gt;

&lt;p&gt;Si has trabajado con Lambda en Go anteriormente, probablemente usaste el runtime &lt;code&gt;go1.x&lt;/code&gt;. Sin embargo, AWS &lt;strong&gt;deprecó todos los runtimes nativos de Go&lt;/strong&gt; en diciembre de 2023.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The Go 1.x managed runtime for Lambda is &lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html#runtime-support-policy" rel="noopener noreferrer"&gt;deprecated&lt;/a&gt; If you have functions that use the Go 1.x runtime, you must migrate your functions to &lt;code&gt;provided.al 2023&lt;/code&gt; or &lt;code&gt;provided.al 2&lt;/code&gt;. The &lt;code&gt;provided.al 2023&lt;/code&gt; and &lt;code&gt;provided.al 2&lt;/code&gt; runtimes offer several advantages over &lt;code&gt;go1.x&lt;/code&gt;, including support for the arm64 architecture (AWS Graviton2 processors), smaller binaries, and slightly faster invoke times.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/lambda-golang.html?utm_source=chatgpt.com" rel="noopener noreferrer"&gt;https://docs.aws.amazon.com/lambda/latest/dg/lambda-golang.html?utm_source=chatgpt.com&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Como se ve la recomendación oficial es usar &lt;code&gt;provided.al2023&lt;/code&gt; por estas razones:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Soporte ARM64&lt;/strong&gt;: Arquitectura Graviton2 que es &lt;strong&gt;50% más barata&lt;/strong&gt; que x86_64&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Control total&lt;/strong&gt;: Siempre usas la última versión de Go que necesites&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Mejor rendimiento&lt;/strong&gt;: Binario nativo sin overhead del runtime&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Sin deprecación&lt;/strong&gt;: AWS no puede deprecar tu versión de Go&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Despliegue
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Inicializar Terraform&lt;/span&gt;
terraform init

&lt;span class="c"&gt;# Ver plan de despliegue&lt;/span&gt;
terraform plan

&lt;span class="c"&gt;# Aplicar cambios&lt;/span&gt;
terraform apply
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Verificamos el despliegue
&lt;/h2&gt;

&lt;p&gt;En nuestra consola de AWS verificamos que la función se desplegó correctamente:&lt;/p&gt;

&lt;p&gt;Podemos ver que:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Runtime: &lt;code&gt;provided.al2023&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Arquitectura: &lt;code&gt;arm64&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Handler: &lt;code&gt;bootstrap&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw78ipw50sjpcpkk7icl8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw78ipw50sjpcpkk7icl8.png" alt="GST_02" width="800" height="180"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Probando nuestra función Lambda
&lt;/h2&gt;

&lt;p&gt;Para probar la función podemos usar la consola de AWS o el CLI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws lambda invoke &lt;span class="nt"&gt;--function-name&lt;/span&gt; go-hello-serverless-lambda response.json
&lt;span class="nb"&gt;cat &lt;/span&gt;response.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;El resultado debería ser:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;more response.json
&lt;span class="c"&gt;# "Hello λ!"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi81kclsl695yz7b4s81a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi81kclsl695yz7b4s81a.png" alt="GST_03" width="800" height="320"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Errores conocidos:
&lt;/h2&gt;

&lt;p&gt;Si ves este error … :&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;"errorType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Runtime.InvalidEntrypoint"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"errorMessage"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Couldn't find valid bootstrap(s): [/var/task/bootstrap /opt/bootstrap]"&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;Es porque el ejecutable no se llama &lt;code&gt;bootstrap&lt;/code&gt;. Asegúrate de compilar con:&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;env &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 main.go
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusiones
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Usar &lt;code&gt;provided.al2023&lt;/code&gt; para funciones Lambda en Go nos da:&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;El proceso de despliegue con Terraform es directo una vez que entiendes los detalles del runtime y la compilación automática.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;¡Gracias por leer, saludos!&lt;/p&gt;

&lt;h2&gt;
  
  
  Referencias
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/lambda-golang.html" rel="noopener noreferrer"&gt;https://docs.aws.amazon.com/lambda/latest/dg/lambda-golang.html&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/aws/aws-lambda-go" rel="noopener noreferrer"&gt;https://github.com/aws/aws-lambda-go&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.terraform.io/" rel="noopener noreferrer"&gt;https://www.terraform.io/&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lambda_function" rel="noopener noreferrer"&gt;https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lambda_function&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>go</category>
      <category>serverless</category>
      <category>terraform</category>
    </item>
    <item>
      <title>▶️ Habilitar MFA (Multi-Factor Authentication)🔐 en AWS Cognito usando Terraform</title>
      <dc:creator>olcortesb</dc:creator>
      <pubDate>Tue, 01 Jul 2025 13:10:10 +0000</pubDate>
      <link>https://dev.to/aws-espanol/-habilitar-mfa-multi-factor-authentication-en-aws-cognito-usando-terraform-2akk</link>
      <guid>https://dev.to/aws-espanol/-habilitar-mfa-multi-factor-authentication-en-aws-cognito-usando-terraform-2akk</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Source: &lt;a href="https://olcortesb.hashnode.dev/habilitar-mfa-multi-factor-authentication-en-aws-cognito-usando-terraform" rel="noopener noreferrer"&gt;https://olcortesb.hashnode.dev/habilitar-mfa-multi-factor-authentication-en-aws-cognito-usando-terraform&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;En un post &lt;a href="https://olcortesb.hashnode.dev/desplegar-aws-cognito-y-una-aplicacion-cliente-con-terraform" rel="noopener noreferrer"&gt;anterior&lt;/a&gt;, comentaba como desplegar &lt;strong&gt;AWS Cognito&lt;/strong&gt; y una aplicación cliente utilizando Terraform. Este artículo es parte de la serie que comente realizaría sobre &lt;em&gt;Cognito + Terraform&lt;/em&gt;. En el post de hoy nos centraremos en ver como habilitar el &lt;strong&gt;Multi Factor de Autenticación&lt;/strong&gt; en Cognito con Terraform y realizaremos algunas pruebas.&lt;/p&gt;

&lt;h2&gt;
  
  
  Estructura del repositorio:
&lt;/h2&gt;

&lt;p&gt;Similar al repositorio anterior, pero ahora en una nueva rama “&lt;a href="https://github.com/olcortesb/cognito-terraform/tree/feat/add-mfa-configuration" rel="noopener noreferrer"&gt;add-mfa-configuration&lt;/a&gt;” tenemos el código de Terraform para desplegar nuestra infraestructura.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://github.com/olcortesb/cognito-terraform/tree/feat/add-mfa-configuration" rel="noopener noreferrer"&gt;https://github.com/olcortesb/cognito-terraform/tree/feat/add-mfa-configuration&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;cognito.tf – Main Cognito resources configuration&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;config.tf – Terraform backend configuration&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;variables.tf – Input variable definition&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;maint.tf – AWS provider configuration&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;sns_sms.tf Role and policy for SMS&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;terraform.tfvars – Variable values for deployment&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Habilitando el MFA:
&lt;/h2&gt;

&lt;p&gt;Para habilitar el &lt;strong&gt;MFA&lt;/strong&gt; es necesario agregar dentro de la definición del Cognito, además de activar el &lt;code&gt;mfa_configuration&lt;/code&gt; es necesario activar el &lt;code&gt;software_token_mfa_configuration&lt;/code&gt;, acorde a las definiciones y documentación de Terraform:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;  &lt;span class="c1"&gt;// cognito.tf&lt;/span&gt;
&lt;span class="n"&gt;resource&lt;/span&gt; &lt;span class="s"&gt;"aws_cognito_user_pool"&lt;/span&gt; &lt;span class="s"&gt;"this"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
  &lt;span class="n"&gt;mfa_configuration&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"ON"&lt;/span&gt;
  &lt;span class="n"&gt;sms_authentication_message&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Your code is {####}"&lt;/span&gt;

  &lt;span class="n"&gt;sms_configuration&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;external_id&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"cognito-external-id"&lt;/span&gt;
    &lt;span class="n"&gt;sns_caller_arn&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;aws_iam_role&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cognito_sms_role&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;arn&lt;/span&gt;
    &lt;span class="n"&gt;sns_region&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;region&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="n"&gt;software_token_mfa_configuration&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;enabled&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&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;h2&gt;
  
  
  Creando Roles y Policy
&lt;/h2&gt;

&lt;p&gt;El archivo &lt;code&gt;sns_sms.tf&lt;/code&gt; tiene los roles y Policy necesarios para el Cognito.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="n"&gt;IAM&lt;/span&gt; &lt;span class="n"&gt;Role&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;Cognito&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;send&lt;/span&gt; &lt;span class="n"&gt;SMS&lt;/span&gt; &lt;span class="n"&gt;via&lt;/span&gt; &lt;span class="n"&gt;SNS&lt;/span&gt;
&lt;span class="n"&gt;resource&lt;/span&gt; &lt;span class="s"&gt;"aws_iam_role"&lt;/span&gt; &lt;span class="s"&gt;"cognito_sms_role"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"cognito-sms-role"&lt;/span&gt;

  &lt;span class="n"&gt;assume_role_policy&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;jsonencode&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="n"&gt;Version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"2012-10-17"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Statement&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="n"&gt;Effect&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Principal&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="n"&gt;Service&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"cognito-idp.amazonaws.com"&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="n"&gt;Action&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"sts:AssumeRole"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Condition&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="n"&gt;StringEquals&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="s"&gt;"sts:ExternalId"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"cognito-external-id"&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="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="n"&gt;IAM&lt;/span&gt; &lt;span class="n"&gt;Policy&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;Cognito&lt;/span&gt; &lt;span class="n"&gt;SMS&lt;/span&gt; &lt;span class="n"&gt;Role&lt;/span&gt;
&lt;span class="n"&gt;resource&lt;/span&gt; &lt;span class="s"&gt;"aws_iam_role_policy"&lt;/span&gt; &lt;span class="s"&gt;"cognito_sms_policy"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"cognito-sms-policy"&lt;/span&gt;
  &lt;span class="n"&gt;role&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;aws_iam_role&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cognito_sms_role&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;

  &lt;span class="n"&gt;policy&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;jsonencode&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="n"&gt;Version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"2012-10-17"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Statement&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="n"&gt;Effect&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Action&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
          &lt;span class="s"&gt;"sns:Publish"&lt;/span&gt;
        &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="n"&gt;Resource&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"*"&lt;/span&gt; &lt;span class="c1"&gt;// Remplazar por el resources para ambientes productivos&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Configurando el Sandbox para el envío de mensajes:
&lt;/h2&gt;

&lt;p&gt;Es necesario para las pruebas habilitar los números de teléfono en un ambiente de sandbox controlado los teléfonos a los que permitiremos el envío de mensaje, es recomendado probar primero en sandbox antes de pasa a un esquema productivo&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq23qwmrkim7c6zvdxsde.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq23qwmrkim7c6zvdxsde.png" alt="Cognito MFA Terraform 1"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Es necesario dar de alta los teléfonos que vamos a utilizar en las pruebas.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fetpevk4gzm6l1cdnlj0w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fetpevk4gzm6l1cdnlj0w.png" alt="Cognito MFA Terraform 1.1"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Damos de alta un usuario:
&lt;/h2&gt;

&lt;p&gt;En esta configuración no habilitamos la autoverificación del teléfono para asegurarnos que el usuario cargue un teléfono válido&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fud2r8s3d1w17knr7o8lb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fud2r8s3d1w17knr7o8lb.png" alt="Cognito MFA Terraform 2"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6tke8pg2id1ixgho09yp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6tke8pg2id1ixgho09yp.png" alt="Cognito MFA Terraform 3"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Prueba de Login:
&lt;/h2&gt;

&lt;p&gt;A continuación un video de ejemplo de como funciona el doble factor de autenticación en Cognito:&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/trigdzzkFFY"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusiones
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Hemos habilitado el MFA con Terraform agregando una configuración al Cognito y un par de Policy y roles.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Hemos habilitado el Sandbox, con números de teléfono específicos para las pruebas del Cognito.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;⚠️ Importante para remover el MFA por seguridad es necesario dar de baja el Cognito, tener en cuenta para ambientes productivos.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Referencias
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://youtu.be/trigdzzkFFY" rel="noopener noreferrer"&gt;https://youtu.be/trigdzzkFFY&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://olcortesb.hashnode.dev/desplegar-aws-cognito-y-una-aplicacion-cliente-con-terraform" rel="noopener noreferrer"&gt;https://olcortesb.hashnode.dev/desplegar-aws-cognito-y-una-aplicacion-cliente-con-terraform&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cognito_user_pool" rel="noopener noreferrer"&gt;https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cognito_user_pool&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>aws</category>
      <category>cognito</category>
      <category>terraform</category>
    </item>
    <item>
      <title>🚀 Desplegar AWS Cognito 🔑 y una Aplicación Cliente Con Terraform.</title>
      <dc:creator>olcortesb</dc:creator>
      <pubDate>Tue, 17 Jun 2025 20:26:46 +0000</pubDate>
      <link>https://dev.to/aws-espanol/desplegar-aws-cognito-y-una-aplicacion-cliente-con-terraform-1ka6</link>
      <guid>https://dev.to/aws-espanol/desplegar-aws-cognito-y-una-aplicacion-cliente-con-terraform-1ka6</guid>
      <description>&lt;p&gt;Fuente: &lt;a href="https://olcortesb.hashnode.dev/desplegar-aws-cognito-y-una-aplicacion-cliente-con-terraform" rel="noopener noreferrer"&gt;Post&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;En una entrada anterior contaba un poco sobre que es AWS Cognito Link, he estado realizando muchas pruebas con nuevas funcionalidades e identificando las posibilidades que tenemos para integrarla con nuestras aplicaciones.&lt;/p&gt;

&lt;p&gt;He decidido armar una serie de artículos con distintas configuraciones e integraciones para entender en profundidad este servicio de AWS, y como siempre es mejor empezar por el principio.&lt;/p&gt;

&lt;p&gt;Vamos a Desplegar AWS Cognito + una aplicación Cliente utilizando Terraform, Aquí &lt;a href="https://github.com/olcortesb/cognito-terraform" rel="noopener noreferrer"&gt;Link&lt;/a&gt; dejaré el repositorio con el código, recuerda cambiar el nombre de los buckets y las aplicaciones.&lt;/p&gt;

&lt;h2&gt;
  
  
  Estructura del repositorio:
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://github.com/olcortesb/cognito-terraform" rel="noopener noreferrer"&gt;https://github.com/olcortesb/cognito-terraform&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;cognito.tf&lt;/strong&gt; – Main Cognito resources configuration&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;config.tf&lt;/strong&gt; – Terraform backend configuration&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;variables.tf&lt;/strong&gt; – Input variables definition&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;maint.tf&lt;/strong&gt; – AWS provider configuration&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;terraform.tfvars&lt;/strong&gt; – Variable values for deployment&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  El “User pool”
&lt;/h2&gt;

&lt;p&gt;Para definir el user pool utilizamos el recurso &lt;code&gt;"aws_cognito_user_pool"&lt;/code&gt; de Terraform, importante definir los atributos del usuario (recordar que Cognito tiene unos atributos predefinidos) y los atributos custom que se configura con un esquema más, tenemos hasta 50 atributos custom para agregar. &lt;a href="https://docs.aws.amazon.com/es_es/cognito/latest/developerguide/user-pool-settings-attributes.html" rel="noopener noreferrer"&gt;Ref&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="n"&gt;User&lt;/span&gt; &lt;span class="n"&gt;Pool&lt;/span&gt;
&lt;span class="n"&gt;resource&lt;/span&gt; &lt;span class="s"&gt;"aws_cognito_user_pool"&lt;/span&gt; &lt;span class="s"&gt;"this"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;name&lt;/span&gt;                     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"olcb-community-user-pool"&lt;/span&gt;
  &lt;span class="n"&gt;username_attributes&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="n"&gt;auto_verified_attributes&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

  &lt;span class="n"&gt;admin_create_user_config&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;allow_admin_create_user_only&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="n"&gt;Configuraciones&lt;/span&gt; &lt;span class="n"&gt;del&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt;
  &lt;span class="n"&gt;password_policy&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;minimum_length&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;8&lt;/span&gt;
    &lt;span class="n"&gt;require_lowercase&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;
    &lt;span class="n"&gt;require_uppercase&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;
    &lt;span class="n"&gt;require_numbers&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;
    &lt;span class="n"&gt;require_symbols&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="n"&gt;habilitar&lt;/span&gt; &lt;span class="n"&gt;attributo&lt;/span&gt; &lt;span class="n"&gt;predefinido&lt;/span&gt;  
  &lt;span class="n"&gt;schema&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;                &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"email"&lt;/span&gt;
    &lt;span class="n"&gt;attribute_data_type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"String"&lt;/span&gt;
    &lt;span class="n"&gt;required&lt;/span&gt;            &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;
    &lt;span class="n"&gt;mutable&lt;/span&gt;             &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="n"&gt;Atrributo&lt;/span&gt; &lt;span class="n"&gt;custom&lt;/span&gt;
  &lt;span class="n"&gt;schema&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;                &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"custom_attr"&lt;/span&gt;
    &lt;span class="n"&gt;attribute_data_type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Number"&lt;/span&gt;
    &lt;span class="n"&gt;required&lt;/span&gt;            &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;
    &lt;span class="n"&gt;mutable&lt;/span&gt;             &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;
    &lt;span class="n"&gt;number_attribute_constraints&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;min_value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
      &lt;span class="n"&gt;max_value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;100&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;Esta parte del código crea nuestro User Pool&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9a46ogx4stsydo4oadbo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9a46ogx4stsydo4oadbo.png" alt="User Pool" width="800" height="212"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Una vez configurado el &lt;em&gt;User Pool&lt;/em&gt; necesitamos crear una aplicación Cliente que Sirva de integración con las aplicaciones externas que se conectaran a través de Cognito.&lt;/p&gt;

&lt;h2&gt;
  
  
  La “App Cliente”
&lt;/h2&gt;

&lt;p&gt;La aplicación cliente la desplegamos utilizando el recurso &lt;code&gt;"aws_cognito_user_pool_client"&lt;/code&gt;, los parámetros más importantes son la referencia al &lt;em&gt;User Pool&lt;/em&gt; que creamos en el paso anterior, &lt;code&gt;user_pool_id,&lt;/code&gt; los flujos que aceptaremos &lt;code&gt;allowed_auth_flows&lt;/code&gt; y el proveedor de los que tengamos en Cognito que soportará esta aplicación &lt;code&gt;supported_identity_providers&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="n"&gt;App&lt;/span&gt; &lt;span class="n"&gt;Client&lt;/span&gt;
&lt;span class="n"&gt;resource&lt;/span&gt; &lt;span class="s"&gt;"aws_cognito_user_pool_client"&lt;/span&gt; &lt;span class="s"&gt;"this"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;name&lt;/span&gt;                                 &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"olcb-app-client"&lt;/span&gt;
  &lt;span class="n"&gt;user_pool_id&lt;/span&gt;                         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;aws_cognito_user_pool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;
  &lt;span class="n"&gt;generate_secret&lt;/span&gt;                      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;
  &lt;span class="n"&gt;allowed_oauth_flows_user_pool_client&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;
  &lt;span class="n"&gt;allowed_oauth_flows&lt;/span&gt;                  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"code"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"implicit"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="n"&gt;allowed_oauth_scopes&lt;/span&gt;                 &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"openid"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"profile"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="n"&gt;explicit_auth_flows&lt;/span&gt;                  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"ADMIN_NO_SRP_AUTH"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"USER_PASSWORD_AUTH"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="n"&gt;callback_urls&lt;/span&gt;                        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"https://example.com/callback"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="n"&gt;logout_urls&lt;/span&gt;                          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"https://example.com/logout"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

  &lt;span class="n"&gt;supported_identity_providers&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"COGNITO"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="n"&gt;refresh_token_validity&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;30&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Este recurso generará la aplicación dentro de nuestro Cognito user pool&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn1iw8uctq9uprgsbc549.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn1iw8uctq9uprgsbc549.png" alt="App Client" width="800" height="257"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  El “User Pool Domain”
&lt;/h2&gt;

&lt;p&gt;Finalmente, asignamos un dominio para que nuestras URLs de login y alta de usuarios y LogOut:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="n"&gt;User&lt;/span&gt; &lt;span class="n"&gt;Pool&lt;/span&gt; &lt;span class="n"&gt;Domain&lt;/span&gt;
&lt;span class="n"&gt;resource&lt;/span&gt; &lt;span class="s"&gt;"aws_cognito_user_pool_domain"&lt;/span&gt; &lt;span class="s"&gt;"this"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;domain&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"olcb-app-domain"&lt;/span&gt;
  &lt;span class="n"&gt;user_pool_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;aws_cognito_user_pool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;En el apartado domain de nuestro &lt;em&gt;Cognito User Pool&lt;/em&gt; tendremos la URI que hemos definido en el Terraform.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2cm19ir9bo34xuhp0xfs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2cm19ir9bo34xuhp0xfs.png" alt="Domain" width="800" height="196"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Pantalla de Login
&lt;/h2&gt;

&lt;p&gt;Finalmente, tendremos operativo nuestro Cognito con un login “UI Clásico”, y el dominio que hemos configurado.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frtryx4sdv1dnxv44k17r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frtryx4sdv1dnxv44k17r.png" alt="Login" width="800" height="369"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;En siguientes entrada mostraré como es el circuito de alta de usuario, conexión desde una app en Python y varias características más,&lt;/p&gt;

&lt;p&gt;gracias por leer, Saludos&lt;/p&gt;

&lt;h2&gt;
  
  
  Referencias
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/olcortesb/cognito-terraform" rel="noopener noreferrer"&gt;https://github.com/olcortesb/cognito-terraform&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://aws.amazon.com/es/blogs/security/use-the-hosted-ui-or-create-a-custom-ui-in-amazon-cognito/" rel="noopener noreferrer"&gt;https://aws.amazon.com/es/blogs/security/use-the-hosted-ui-or-create-a-custom-ui-in-amazon-cognito/&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-cognito-userpool.html#cfn-cognito-userpool-userpoolname" rel="noopener noreferrer"&gt;https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-cognito-userpool.html#cfn-cognito-userpool-userpoolname&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://olcortesb.hashnode.dev/aws-cognito-lambda-triggers" rel="noopener noreferrer"&gt;https://olcortesb.hashnode.dev/aws-cognito-lambda-triggers&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cognito_user_pool" rel="noopener noreferrer"&gt;https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cognito_user_pool&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>cognito</category>
      <category>terraform</category>
      <category>aws</category>
    </item>
    <item>
      <title>📝 Como listar la concurrencia reservada y aprovisionada de nuestras Lambdas ⚡.</title>
      <dc:creator>olcortesb</dc:creator>
      <pubDate>Wed, 28 May 2025 09:05:24 +0000</pubDate>
      <link>https://dev.to/aws-espanol/como-listar-la-concurrencia-reservada-y-aprovisionada-de-nuestras-lambdas--j33</link>
      <guid>https://dev.to/aws-espanol/como-listar-la-concurrencia-reservada-y-aprovisionada-de-nuestras-lambdas--j33</guid>
      <description>&lt;p&gt;Source: &lt;a href="https://olcortesb.hashnode.dev/como-listar-la-concurrencia-reservada-y-aprovisionada-de-nuestras-lambdas" rel="noopener noreferrer"&gt;https://olcortesb.hashnode.dev/como-listar-la-concurrencia-reservada-y-aprovisionada-de-nuestras-lambdas&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;La concurrencia en funciones lambdas se refiere al número de solicitudes que una función puede manejar simultáneamente. Existen dos tipos principales de control de concurrencia:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Concurrencia reservada&lt;/strong&gt;: Establece un límite máximo de instancias simultáneas para una función específica, garantizando que otras funciones no utilicen esa capacidad. No tiene costos y no mejora el Cold start. El límite por cuenta de la concurrencia reservada es de 1000, se puede aumentar a nivel de cuenta.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Concurrencia aprovisionada&lt;/strong&gt;: Mantiene un número fijo de entornos de ejecución pre inicializados para una función, asegurando que estén listos para manejar solicitudes sin demora. Tiene un costo 💵adicional y ayuda a mejorar el Cold Start ❄️. Son Lambdas que están siempre encendidas ⚠️.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Bien, con esto en mente supongamos un escenario donde tenemos una región con más de 70 lambdas desplegadas y por alguna razón, no tenemos control sobre como está distribuida la concurrencia entre estas lambdas.&lt;/p&gt;

&lt;p&gt;Con este escenario podemos intentar ir a &lt;a href="https://aws.amazon.com/es/cli/" rel="noopener noreferrer"&gt;AWS cli&lt;/a&gt;, o crear un Script de Python (u otro lenguaje) para consultar a través del SDK (&lt;a href="https://boto3.amazonaws.com/v1/documentation/api/latest/index.html" rel="noopener noreferrer"&gt;Boto3&lt;/a&gt;), y en ese momento nos daremos cuenta de que posiblemente nos retorne cero cuando el panel de Amazon y el costo nos está indicando que tenemos concurrencia reservada:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv6snuxive6ysabpzse44.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv6snuxive6ysabpzse44.png" alt=" " width="800" height="116"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;💡 Bien, lo que sucede es que no podemos realizar la consulta si el rol o el usuario que estamos usando para consultar no tiene permisos(🔏)para acceder a esta información de las lambdas.&lt;/p&gt;

&lt;h2&gt;
  
  
  Solución Alternativa y Reutilizable
&lt;/h2&gt;

&lt;p&gt;Como no podría ser de otra manera para quien me ha leído antes, la solución propuesta es una Lambda 😀que haga este trabajo, desplegado con &lt;a href="https://aws.amazon.com/es/serverless/sam/" rel="noopener noreferrer"&gt;AWS Sam&lt;/a&gt;, y que podemos desplegar y destruir de manera rápida y en distintas regiones.&lt;/p&gt;

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

&lt;blockquote&gt;
&lt;h3&gt;
  
  
  &lt;a href="https://github.com/olcortesb/list-lambda-provisioned-reserved" rel="noopener noreferrer"&gt;https://github.com/olcortesb/list-lambda-provisioned-reserved&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Repositorio con todo el codigo que se presenta en este post&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Los permisos:
&lt;/h3&gt;

&lt;p&gt;El primer punto es definir la infraestructura y definir los permisos correspondientes.&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="c1"&gt;# https://github.com/olcortesb/list-lambda-provisioned-reserved/blob/main/template.yaml &lt;/span&gt;
&lt;span class="na"&gt;AWSTemplateFormatVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;2010-09-09'&lt;/span&gt;
&lt;span class="na"&gt;Transform&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::Serverless-2016-10-31&lt;/span&gt;
&lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;List Lambda provisioned reserved&lt;/span&gt;

&lt;span class="na"&gt;Resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;                
  &lt;span class="na"&gt;ListFunction&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::Serverless::Function&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;CodeUri&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;src/&lt;/span&gt;
      &lt;span class="na"&gt;Handler&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;lambda.lambda_handler&lt;/span&gt;
      &lt;span class="na"&gt;Runtime&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;python3.10&lt;/span&gt;
      &lt;span class="na"&gt;Architectures&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;arm64&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;Timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;900&lt;/span&gt;
      &lt;span class="na"&gt;Policies&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;2012-10-17'&lt;/span&gt;
          &lt;span class="na"&gt;Statement&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Effect&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Allow&lt;/span&gt;
              &lt;span class="na"&gt;Action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;lambda:GetFunctionConcurrency&lt;/span&gt;
                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;lambda:ListFunctions&lt;/span&gt;
                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;lambda:ListProvisionedConcurrencyConfigs&lt;/span&gt;
              &lt;span class="na"&gt;Resource&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;*"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Los permisos relacionados con &lt;code&gt;Provicioned&lt;/code&gt; y &lt;code&gt;concurrency&lt;/code&gt; son los que permiten acceder a los atributos.&lt;/p&gt;

&lt;h3&gt;
  
  
  El código:
&lt;/h3&gt;

&lt;p&gt;El código está basado en &lt;a href="https://boto3.amazonaws.com/v1/documentation/api/latest/index.html" rel="noopener noreferrer"&gt;Boto3&lt;/a&gt; y lo que hacemos es consultar por todas las lambdas y revisar cuáles tienen configuraciones de concurrencia.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# https://github.com/olcortesb/list-lambda-provisioned-reserved/tree/main/src
# Base on https://repost.aws/knowledge-center/lambda-provisioned-reserved-concurrency
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;lambda_handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;...&lt;/span&gt;
            &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;lambda_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_function_concurrency&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="n"&gt;FunctionName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;function_name&lt;/span&gt;
                &lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ReservedConcurrentExecutions&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="ow"&gt;in&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;count&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
                    &lt;span class="n"&gt;reserved_concurrency&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ReservedConcurrentExecutions&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
                    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;. Function Name: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;function_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;, Reserved Concurrency: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;reserved_concurrency&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
            &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;lambda_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exceptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ResourceNotFoundException&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;pass&lt;/span&gt;
            &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Error retrieving concurrency for &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;function_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;lambda_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;list_provisioned_concurrency_configs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="n"&gt;FunctionName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;function_name&lt;/span&gt;
                &lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ProvisionedConcurrencyConfigs&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ProvisionedConcurrencyConfigs&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
                    &lt;span class="n"&gt;provisioned_concurrency&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ProvisionedConcurrencyConfigs&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;RequestedProvisionedConcurrentExecutions&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
                    &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
                    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;. Function Name: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;function_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;, Provisioned Concurrency: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;provisioned_concurrency&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;lambda_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exceptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ResourceNotFoundException&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;pass&lt;/span&gt;
            &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Error retrieving provisioned concurrency for &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;function_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Resultados
&lt;/h3&gt;

&lt;p&gt;Ale ejecutar la función recorrerá todas las lambdas que tenemos en la región donde la hemos desplegado y validara si tienen configurada concurrencia reservada o provisionada respectivamente, dándonos un listado de las funciones y la cantidad de concurrencia. Para este ejemplo lo sacamos en un &lt;em&gt;log&lt;/em&gt;, pero se puede obtener vía API o enviarlo a algún lugar que concentre los reportes de nuestra infraestructura&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Function Logs:
START RequestId: 253fe873-d35c-44e2-8593-f6a43b9cecc3 Version: &lt;span class="nv"&gt;$LATEST&lt;/span&gt;
Total functions 75
1. Function Name: fibonacci-x86-n4, Reserved Concurrency: 5
2. Function Name: fibonacci-x86-n4, Provisioned Concurrency: 1
END RequestId: 253fe873-d35c-44e2-8593-f6a43b9cecc3
REPORT RequestId: 253fe873-d35c-44e2-8593-f6a43b9cecc3  Duration: 8857.61 ms ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Probamos como poder obtener los datos de nuestra concurrencia en una región específica.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Identificamos los permisos necesarios para que la lambda pueda consultar por estos permisos&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Sacamos en un &lt;em&gt;log&lt;/em&gt; los resultados como prueba básica, pudiendo enviarlos a distintos destinos o devolviendo mediante una API de ser necesario&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Referencias
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/olcortesb/list-lambda-provisioned-reserved" rel="noopener noreferrer"&gt;https://github.com/olcortesb/list-lambda-provisioned-reserved&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/es_es/lambda/latest/dg/provisioned-concurrency.html" rel="noopener noreferrer"&gt;https://docs.aws.amazon.com/es_es/lambda/latest/dg/provisioned-concurrency.html&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://repost.aws/knowledge-center/lambda-provisioned-reserved-concurrency" rel="noopener noreferrer"&gt;https://repost.aws/knowledge-center/lambda-provisioned-reserved-concurrency&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>aws</category>
      <category>serverless</category>
      <category>lambda</category>
    </item>
  </channel>
</rss>
