<?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>Forem: Clariza Look</title>
    <description>The latest articles on Forem by Clariza Look (@clarizalooktech).</description>
    <link>https://forem.com/clarizalooktech</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%2F1904682%2F9b7c0b36-278a-40b4-b829-7ebe67970e20.png</url>
      <title>Forem: Clariza Look</title>
      <link>https://forem.com/clarizalooktech</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/clarizalooktech"/>
    <language>en</language>
    <item>
      <title>Building a Cross-Account S3 to Azure Backup System with ECS Fargate</title>
      <dc:creator>Clariza Look</dc:creator>
      <pubDate>Mon, 06 Oct 2025 07:27:10 +0000</pubDate>
      <link>https://forem.com/clarizalooktech/building-a-cross-account-s3-to-azure-backup-system-with-ecs-fargate-3dbh</link>
      <guid>https://forem.com/clarizalooktech/building-a-cross-account-s3-to-azure-backup-system-with-ecs-fargate-3dbh</guid>
      <description>&lt;h2&gt;
  
  
  The Challenge
&lt;/h2&gt;

&lt;p&gt;When you need to transfer large database backups (85GB+) from AWS S3 to Azure Blob Storage as part of the Disaster Recovery measure, AWS Lambda's 15-minute timeout becomes a significant constraint. The backup files take 60-90 minutes to transfer, making Lambda unsuitable for this use case.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;REPORT RequestId: b000000b6a-000-0000-0000-0a0f0bf0c000 Duration: 900000.00 ms Billed Duration: 901344 ms Memory Size: 1024 MB Max Memory Used: 156 MB Init Duration: 1343.19 ms Status: Task timed out
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Additionally, in enterprise environments with multi-account AWS setups, you often need to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Build and store Docker images in a central CICD account&lt;/li&gt;
&lt;li&gt;Deploy infrastructure to separate environment accounts (dev, staging, prod)&lt;/li&gt;
&lt;li&gt;Maintain proper security boundaries between accounts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This article walks through building a production-ready backup offloading system that handles these challenges using ECS Fargate and cross-account deployment patterns.&lt;/p&gt;

&lt;h2&gt;
  
  
  Architecture Overview
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Account A: CICD Account]
├── ECR Repository (Docker Images)
├── CodeBuild (CI/CD)
└── CodePipeline

[Account B: Target Account for Deployment]
├── S3 Bucket (Backup Files)
├── SQS Queue
├── Lambda (ECS Trigger)
├── ECS Fargate (Transfer Task)
└── Secrets Manager (Azure Credentials)

Flow:
S3 → Event Notification → SQS → Lambda → ECS Fargate → Azure Blob Storage
&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%2F2uqemhoh6wc4lh510cz6.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%2F2uqemhoh6wc4lh510cz6.png" alt="S3 → Event Notification → SQS → Lambda → ECS Fargate → Azure Blob Storage" width="800" height="419"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Why This Architecture?
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;ECS Fargate over Lambda:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No timeout limits (Lambda: 15 min max)&lt;/li&gt;
&lt;li&gt;Handles files of any size&lt;/li&gt;
&lt;li&gt;Streaming transfer (no local storage limits)&lt;/li&gt;
&lt;li&gt;Better for long-running tasks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Multi-Account Pattern:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Centralized Docker image management&lt;/li&gt;
&lt;li&gt;Clear separation of concerns&lt;/li&gt;
&lt;li&gt;Reusable images across environments&lt;/li&gt;
&lt;li&gt;Enhanced security boundaries&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Implementation Guide
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Part 1: Docker Container for Backup Processing
&lt;/h3&gt;

&lt;p&gt;The heart of the system is a Python container that streams files from S3 to Azure:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;docker/backup_processor/Dockerfile:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; python:3.12-slim&lt;/span&gt;

&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; requirements.txt .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--no-cache-dir&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; app.py .&lt;/span&gt;

&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["python", "app.py"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;docker/backup_processor/requirements.txt:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;azure-storage-blob==12.19.0
boto3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Key features of the transfer application:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Retrieves Azure credentials from AWS Secrets Manager&lt;/li&gt;
&lt;li&gt;Streams data from S3 (no local storage needed)&lt;/li&gt;
&lt;li&gt;Parallel uploads to Azure (8x concurrency)&lt;/li&gt;
&lt;li&gt;Emits CloudWatch metrics for monitoring&lt;/li&gt;
&lt;li&gt;Handles errors gracefully&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Part 2: Multi-Account Docker Image Management
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;In Account A (CICD account) - Build and Push:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The CI/CD pipeline builds the Docker image and pushes it to ECR in Account A:&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;# buildspec.yml&lt;/span&gt;
&lt;span class="na"&gt;pre_build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;commands&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# Login to Account A ECR&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;aws ecr get-login-password --region $REGION | docker login --username AWS --password-stdin $ACCOUNT_A.dkr.ecr.$REGION.amazonaws.com&lt;/span&gt;

    &lt;span class="c1"&gt;# Build and push image&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;cd docker/backup_processor&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;docker build -t backup-processor:$IMAGE_TAG .&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;docker tag backup-processor:$IMAGE_TAG $ACCOUNT_A.dkr.ecr.$REGION.amazonaws.com/backup-processor:$IMAGE_TAG&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;docker push $ACCOUNT_A.dkr.ecr.$REGION.amazonaws.com/backup-processor:$IMAGE_TAG&lt;/span&gt;

    &lt;span class="c1"&gt;# Set image URI for deployment&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;export DOCKER_IMAGE_URI="$ACCOUNT_A.dkr.ecr.$REGION.amazonaws.com/backup-processor:$IMAGE_TAG"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;ECR Repository Policy in Account A:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Allow Account B to pull images:&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;"Version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2008-10-17"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Statement"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Sid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"AllowAccountBPull"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Principal"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"AWS"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:iam::ACCOUNT_B:root"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"ecr:GetDownloadUrlForLayer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"ecr:BatchGetImage"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"ecr:BatchCheckLayerAvailability"&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;
  
  
  Part 3: ECS Infrastructure in Account B
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;CDK Stack Structure:&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="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BackupOffloaderStack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Stack&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;construct_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nf"&gt;super&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;construct_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# Get Docker image URI from environment (set by buildspec)
&lt;/span&gt;        &lt;span class="n"&gt;docker_image_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="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;DOCKER_IMAGE_URI&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# Create ECS cluster
&lt;/span&gt;        &lt;span class="n"&gt;cluster&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ecs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Cluster&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;BackupCluster&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;cluster_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;backup-processor-cluster&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;vpc&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vpc&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# Create task definition
&lt;/span&gt;        &lt;span class="n"&gt;task_definition&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ecs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;FargateTaskDefinition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;BackupTaskDef&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;memory_limit_mib&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2048&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;cpu&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;task_role&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;task_role&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;execution_role&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;execution_role&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# Add container referencing cross-account image
&lt;/span&gt;        &lt;span class="n"&gt;container&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;task_definition&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_container&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;BackupContainer&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ecs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ContainerImage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_registry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;docker_image_uri&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ecs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LogDrivers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;aws_logs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;stream_prefix&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;backup-processor&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;log_retention&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;logs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RetentionDays&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ONE_MONTH&lt;/span&gt;
            &lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;environment&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;AZURE_SECRET_NAME&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;azure-credentials&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;LOG_LEVEL&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;INFO&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;Critical: Cross-Account ECR Permissions&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The ECS Task Execution Role in Account B needs permission to pull from Account A's ECR:&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;execution_role&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;iam&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Role&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ExecutionRole&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;assumed_by&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;iam&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ServicePrincipal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ecs-tasks.amazonaws.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;managed_policies&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="n"&gt;iam&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ManagedPolicy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_aws_managed_policy_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;service-role/AmazonECSTaskExecutionRolePolicy&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="n"&gt;inline_policies&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;CrossAccountECRAccess&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;iam&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;PolicyDocument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;statements&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="n"&gt;iam&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;PolicyStatement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="n"&gt;effect&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;iam&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="n"&gt;ALLOW&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;actions&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;ecr:GetAuthorizationToken&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                    &lt;span class="n"&gt;resources&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;*&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="n"&gt;iam&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;PolicyStatement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="n"&gt;effect&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;iam&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="n"&gt;ALLOW&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;actions&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;ecr:BatchCheckLayerAvailability&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;ecr:GetDownloadUrlForLayer&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;ecr:BatchGetImage&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
                    &lt;span class="p"&gt;],&lt;/span&gt;
                    &lt;span class="n"&gt;resources&lt;/span&gt;&lt;span class="o"&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;arn:aws:ecr:region:ACCOUNT_A:repository/backup-processor&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="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;h3&gt;
  
  
  Part 4: Event-Driven Trigger System
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;S3 → SQS → Lambda → ECS Flow:&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;# Lambda function to trigger ECS tasks
&lt;/span&gt;&lt;span class="n"&gt;trigger_code&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
import boto3
import json

ecs_client = boto3.client(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ecs&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;)

def lambda_handler(event, context):
    for record in event.get(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Records&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;, []):
        message = json.loads(record[&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="s"&gt;])

        # Extract S3 details
        s3_event = message[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Records&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;][0]
        bucket = s3_event[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s3&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="s"&gt;bucket&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="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;]
        key = s3_event[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s3&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="s"&gt;object&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="s"&gt;key&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;]

        # Trigger ECS task
        ecs_client.run_task(
            cluster=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;backup-processor-cluster&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;,
            taskDefinition=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;BackupTaskDef&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;,
            launchType=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;FARGATE&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;,
            networkConfiguration={
                &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;awsvpcConfiguration&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="s"&gt;subnets&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;: SUBNET_IDS,
                    &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;assignPublicIp&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="s"&gt;ENABLED&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;
                }
            },
            overrides={
                &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;containerOverrides&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="s"&gt;name&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="s"&gt;BackupContainer&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="s"&gt;environment&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="s"&gt;name&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="s"&gt;S3_BUCKET&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="s"&gt;value&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;: bucket},
                        {&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;name&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="s"&gt;S3_KEY&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="s"&gt;value&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;: key}
                    ]
                }]
            }
        )
&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Part 5: Deployment Pipeline
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Complete CI/CD Flow:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Pre-build Phase (Account A):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Build Docker image&lt;/li&gt;
&lt;li&gt;Push to Account A ECR&lt;/li&gt;
&lt;li&gt;Set DOCKER_IMAGE_URI environment variable&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Assume Role to Account B:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Assume deployment role in Account B&lt;/li&gt;
&lt;li&gt;Switch AWS credentials&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Build Phase (Account B):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CDK synth (reads DOCKER_IMAGE_URI)&lt;/li&gt;
&lt;li&gt;CDK deploy infrastructure&lt;/li&gt;
&lt;li&gt;ECS tasks pull image from Account A ECR&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Key Configuration:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# buildspec.yml&lt;/span&gt;
&lt;span class="na"&gt;phases&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;pre_build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;commands&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="c1"&gt;# Build in Account A&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;docker build -t $IMAGE_NAME .&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;docker push $ACCOUNT_A_ECR/$IMAGE_NAME:$TAG&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;export DOCKER_IMAGE_URI="$ACCOUNT_A_ECR/$IMAGE_NAME:$TAG"&lt;/span&gt;

      &lt;span class="c1"&gt;# Assume role to Account B&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;aws sts assume-role --role-arn $ACCOUNT_B_ROLE ...&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;export AWS_ACCESS_KEY_ID=...&lt;/span&gt;

  &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;commands&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="c1"&gt;# Deploy to Account B (uses DOCKER_IMAGE_URI)&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;cdk deploy&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Monitoring and Operations
&lt;/h2&gt;

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

&lt;p&gt;Track transfer success and performance:&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;cloudwatch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;put_metric_data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;Namespace&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;BackupOffloader&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;MetricData&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&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;MetricName&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;BackupsCopied&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;Value&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Unit&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;Count&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;MetricName&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;TransferDuration&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;Value&lt;/span&gt;&lt;span class="sh"&gt;'&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Unit&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;Seconds&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;MetricName&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;BytesTransferred&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;Value&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Unit&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;Bytes&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="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Log Groups
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;ECS Task Logs:&lt;/strong&gt; &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Lambda Trigger Logs:&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Troubleshooting Cross-Account Issues
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;CannotPullContainerError (403 Forbidden):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Verify ECR repository policy in Account A allows Account B&lt;/li&gt;
&lt;li&gt;Check execution role has cross-account ECR permissions&lt;/li&gt;
&lt;li&gt;Confirm image exists and tag is correct&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Security Considerations
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Credentials Management:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Azure credentials in Secrets Manager (encrypted)&lt;/li&gt;
&lt;li&gt;IAM roles for AWS resource access (no long-term keys)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Network Security:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ECS tasks in private subnets with NAT gateway&lt;/li&gt;
&lt;li&gt;Security groups restrict traffic&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Cross-Account Access:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Least-privilege IAM policies&lt;/li&gt;
&lt;li&gt;Specific ECR repository access only&lt;/li&gt;
&lt;li&gt;Regular credential rotation&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;ECS Fargate removes Lambda timeout constraints&lt;/strong&gt; for long-running data transfers&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multi-account patterns enhance security&lt;/strong&gt; and enable centralized image management&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cross-account ECR access requires proper permissions&lt;/strong&gt; on both accounts&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Event-driven architecture scales automatically&lt;/strong&gt; with backup frequency&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Infrastructure as Code&lt;/strong&gt; makes the system reproducible and maintainable&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;This architecture demonstrates how to build production-ready, cross-account data transfer systems on AWS. By combining ECS Fargate's flexibility with proper multi-account IAM configuration, you can handle large-scale data movements that exceed Lambda's limitations while maintaining security boundaries between environments.&lt;/p&gt;

&lt;p&gt;The pattern shown here is applicable beyond backup systems - any long-running data processing task that needs to span AWS accounts can benefit from this approach.&lt;/p&gt;

</description>
      <category>disasterecovery</category>
      <category>programming</category>
      <category>infrastructureascode</category>
    </item>
    <item>
      <title>Understanding `curl`: When to Choose Silent Automation Over Verbose Debugging</title>
      <dc:creator>Clariza Look</dc:creator>
      <pubDate>Mon, 11 Aug 2025 07:16:09 +0000</pubDate>
      <link>https://forem.com/clarizalooktech/understanding-curl-when-to-choose-silent-automation-over-verbose-debugging-1bec</link>
      <guid>https://forem.com/clarizalooktech/understanding-curl-when-to-choose-silent-automation-over-verbose-debugging-1bec</guid>
      <description>&lt;p&gt;When working with APIs and web services, &lt;code&gt;curl&lt;/code&gt; is often your best friend. But did you know that how you use curl can dramatically change both what you see and what you can do with the results? Today, let's explore two completely different approaches to the same HTTP request and understand when to use each.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is API Gateway?
&lt;/h2&gt;

&lt;p&gt;Before diving into curl commands, let's quickly understand what we're testing. &lt;strong&gt;Amazon API Gateway&lt;/strong&gt; is a fully managed service that makes it easy for developers to create, publish, maintain, monitor, and secure APIs at any scale. Think of it as the front door to your applications - it handles all the HTTP requests and routes them to your backend services.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Benefits:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Traffic Management&lt;/strong&gt; - Handle millions of concurrent API calls&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security&lt;/strong&gt; - Built-in authorization, authentication, and API keys&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Monitoring&lt;/strong&gt; - CloudWatch integration for metrics and logging&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Caching&lt;/strong&gt; - Reduce latency and backend load&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Throttling&lt;/strong&gt; - Protect your backend from traffic spikes&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Creating API Gateway with CDK Python
&lt;/h2&gt;

&lt;p&gt;Here's how you can create an API Gateway using AWS CDK with Python:&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;from&lt;/span&gt; &lt;span class="n"&gt;aws_cdk&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;Stack&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;aws_apigateway&lt;/span&gt; &lt;span class="k"&gt;as&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;aws_lambda&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;_lambda&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;CfnOutput&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;constructs&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Construct&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ApiGatewayStack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Stack&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Construct&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;construct_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;super&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;construct_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# Create a Lambda function (your backend)
&lt;/span&gt;        &lt;span class="n"&gt;api_lambda&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_lambda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ApiLambda&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;runtime&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;_lambda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Runtime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PYTHON_3_9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;lambda_function.lambda_handler&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;_lambda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Code&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_asset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;lambda&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;# Create the API Gateway
&lt;/span&gt;        &lt;span class="n"&gt;api&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;apigateway&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;RestApi&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;MyApi&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;rest_api_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;My Service API&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;This service serves my application.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;default_cors_preflight_options&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;apigateway&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;CorsOptions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;allow_origins&lt;/span&gt;&lt;span class="o"&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;Cors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ALL_ORIGINS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;allow_methods&lt;/span&gt;&lt;span class="o"&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;Cors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ALL_METHODS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# Create API Gateway integration with Lambda
&lt;/span&gt;        &lt;span class="n"&gt;lambda_integration&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;apigateway&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;LambdaIntegration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;api_lambda&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;request_templates&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;application/json&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;{ &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="s"&gt;: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;200&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="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# Add a resource and method
&lt;/span&gt;        &lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_method&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;GET&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lambda_integration&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# Add a resource path
&lt;/span&gt;        &lt;span class="n"&gt;items&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_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;items&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_method&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;GET&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lambda_integration&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_method&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;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;lambda_integration&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# Output the API Gateway URL - This is what we'll test!
&lt;/span&gt;        &lt;span class="nc"&gt;CfnOutput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ApiGatewayUrl&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;API Gateway endpoint URL&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;export_name&lt;/span&gt;&lt;span class="o"&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;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stack_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;-ApiGatewayUrl&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;p&gt;This CDK code creates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A Lambda function as your backend service&lt;/li&gt;
&lt;li&gt;An API Gateway that routes requests to the Lambda&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Most importantly&lt;/strong&gt;: An output called &lt;code&gt;ApiGatewayUrl&lt;/code&gt; that exports the endpoint&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This exported URL is exactly what our curl commands will be testing!&lt;/p&gt;

&lt;h2&gt;
  
  
  The Setup: Testing an API Gateway
&lt;/h2&gt;

&lt;p&gt;Now that you understand what we're working with, imagine you're building a CI/CD pipeline that needs to verify your API Gateway is working after deployment. You have two options for testing the endpoint:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Option 1: The Silent Operator&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="nv"&gt;response_code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; /dev/null &lt;span class="nt"&gt;-w&lt;/span&gt; &lt;span class="s2"&gt;"%{http_code}"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$API_URL&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;--connect-timeout&lt;/span&gt; 10 &lt;span class="nt"&gt;--max-time&lt;/span&gt; 30 2&amp;gt;/dev/null &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"000"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Option 2: The Verbose Detective&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Same endpoint, completely different philosophies. Let's dive into what makes each special.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Silent Operator: Automation's Best Friend
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What It Does
&lt;/h3&gt;

&lt;p&gt;The silent approach is like a ninja—it gets in, gets the job done, and gets out without making a fuss. Here's what each part does:&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;-s&lt;/span&gt;                    &lt;span class="c"&gt;# Silent mode (no progress bars or chatter)&lt;/span&gt;
     &lt;span class="nt"&gt;-o&lt;/span&gt; /dev/null         &lt;span class="c"&gt;# Throw away the response body&lt;/span&gt;
     &lt;span class="nt"&gt;-w&lt;/span&gt; &lt;span class="s2"&gt;"%{http_code}"&lt;/span&gt;    &lt;span class="c"&gt;# Only output the HTTP status code&lt;/span&gt;
     &lt;span class="nt"&gt;--connect-timeout&lt;/span&gt; 10 &lt;span class="c"&gt;# Give up connecting after 10 seconds&lt;/span&gt;
     &lt;span class="nt"&gt;--max-time&lt;/span&gt; 30        &lt;span class="c"&gt;# Total operation timeout of 30 seconds&lt;/span&gt;
     2&amp;gt;/dev/null          &lt;span class="c"&gt;# Hide any error messages&lt;/span&gt;
     &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"000"&lt;/span&gt;        &lt;span class="c"&gt;# If everything fails, return "000"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What You Get
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;404
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. Just a clean, three-digit HTTP status code that tells you exactly what happened:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;200&lt;/code&gt; = Success&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;404&lt;/code&gt; = Not found (but API Gateway is working)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;403&lt;/code&gt; = Forbidden (API Gateway working, needs auth)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;000&lt;/code&gt; = Complete failure (connection issues)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Perfect For:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;✅ &lt;strong&gt;CI/CD Pipelines&lt;/strong&gt; - Clean, parseable output&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Health Check Scripts&lt;/strong&gt; - Just need pass/fail&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Monitoring Systems&lt;/strong&gt; - Programmatic status checking&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Automation&lt;/strong&gt; - When you need to make decisions based on response codes&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Verbose Detective: When You Need Answers
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What It Does
&lt;/h3&gt;

&lt;p&gt;The verbose approach is like a chatty detective who tells you everything they're thinking. It shows you the entire conversation between your computer and the server.&lt;/p&gt;

&lt;h3&gt;
  
  
  What You Get
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;* Trying 203.0.113.42:443...
* Connected to example.execute-api.us-east-1.amazonaws.com (203.0.113.42) port 443 (#0)
* ALPN: offers h2
* ALPN: offers http/1.1
*  CAfile: /etc/ssl/certs/ca-certificates.crt
*  CApath: /etc/ssl/certs
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
* TLSv1.3 (IN), TLS handshake, Finished (20):
* TLSv1.3 (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / TLS_AES_128_GCM_SHA256
* ALPN: server accepted http/1.1
* Server certificate:
*  subject: CN=*.execute-api.us-east-1.amazonaws.com
*  start date: Dec  5 00:00:00 2023 GMT
*  expire date: Jan  3 23:59:59 2025 GMT
*  subjectAltName: host "example.execute-api.us-east-1.amazonaws.com" matched cert's "*.execute-api.us-east-1.amazonaws.com"
*  issuer: C=US; O=Amazon; CN=Amazon RSA 2048 M02
*  SSL certificate verify ok.
&amp;gt; GET /prod/ HTTP/1.1
&amp;gt; Host: example.execute-api.us-east-1.amazonaws.com
&amp;gt; User-Agent: curl/7.81.0
&amp;gt; Accept: */*
&amp;gt; 
* Mark bundle as not supporting multiuse
&amp;lt; HTTP/1.1 404 Not Found
&amp;lt; Date: Sun, 11 Aug 2025 04:30:00 GMT
&amp;lt; Content-Type: application/json
&amp;lt; Content-Length: 36
&amp;lt; Connection: keep-alive
&amp;lt; x-amzn-RequestId: example-request-id
&amp;lt; x-amzn-ErrorType: UnknownOperationException
&amp;lt; x-amz-apigw-id: example-apigw-id
&amp;lt; 
{"message":"Missing Authentication Token"}
* Connection #0 to host example.execute-api.us-east-1.amazonaws.com left intact
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Perfect For:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;✅ &lt;strong&gt;Debugging SSL/TLS Issues&lt;/strong&gt; - See certificate details and handshake&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Network Troubleshooting&lt;/strong&gt; - See DNS resolution and connection details&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;API Development&lt;/strong&gt; - Understand exactly what headers are being sent/received&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Security Analysis&lt;/strong&gt; - Verify certificates and encryption protocols&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Learning&lt;/strong&gt; - Understand how HTTP really works under the hood&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Real-World Example: CI/CD Pipeline
&lt;/h2&gt;

&lt;p&gt;Let's see how this plays out in a real AWS CodeBuild pipeline that tests an API Gateway after deployment:&lt;/p&gt;

&lt;h3&gt;
  
  
  The Pipeline Context
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# In your CI/CD pipeline, after deploying your API Gateway:&lt;/span&gt;
&lt;span class="c1"&gt;# 1. Deploy CloudFormation stack with API Gateway&lt;/span&gt;
&lt;span class="c1"&gt;# 2. Deploy application to ECS&lt;/span&gt;
&lt;span class="c1"&gt;# 3. Test if API Gateway is reachable ← We are here&lt;/span&gt;
&lt;span class="c1"&gt;# 4. Continue to production (or fail if test fails)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Using the Silent Approach
&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;# Get just the status code for automated decision making&lt;/span&gt;
&lt;span class="nv"&gt;response_code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; /dev/null &lt;span class="nt"&gt;-w&lt;/span&gt; &lt;span class="s2"&gt;"%{http_code}"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$API_URL&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;--connect-timeout&lt;/span&gt; 10 &lt;span class="nt"&gt;--max-time&lt;/span&gt; 30 2&amp;gt;/dev/null &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"000"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nv"&gt;$response_code&lt;/span&gt; &lt;span class="k"&gt;in
    &lt;/span&gt;200&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"✅ SUCCESS: API Gateway working perfectly"&lt;/span&gt;
        &lt;span class="p"&gt;;;&lt;/span&gt;
    404&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"✅ SUCCESS: API Gateway deployed (just no root handler)"&lt;/span&gt;
        &lt;span class="p"&gt;;;&lt;/span&gt;
    403|401&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"✅ SUCCESS: API Gateway deployed (needs authentication)"&lt;/span&gt;
        &lt;span class="p"&gt;;;&lt;/span&gt;
    000&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"❌ FAILURE: Cannot reach API Gateway"&lt;/span&gt;
        &lt;span class="nb"&gt;exit &lt;/span&gt;1  &lt;span class="c"&gt;# Fail the pipeline&lt;/span&gt;
        &lt;span class="p"&gt;;;&lt;/span&gt;
    &lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"✅ SUCCESS: API Gateway responding (HTTP &lt;/span&gt;&lt;span class="nv"&gt;$response_code&lt;/span&gt;&lt;span class="s2"&gt;)"&lt;/span&gt;
        &lt;span class="p"&gt;;;&lt;/span&gt;
&lt;span class="k"&gt;esac&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives you clean, actionable results:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;✅ SUCCESS: API Gateway deployed (just no root handler)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  When You'd Use Verbose Instead
&lt;/h3&gt;

&lt;p&gt;If your pipeline fails and you need to understand why:&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;# Run this manually to debug&lt;/span&gt;
curl &lt;span class="nt"&gt;-v&lt;/span&gt; https://your-api-gateway-url.amazonaws.com/prod/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You might discover:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;DNS resolution issues&lt;/li&gt;
&lt;li&gt;SSL certificate problems&lt;/li&gt;
&lt;li&gt;Firewall blocking connections&lt;/li&gt;
&lt;li&gt;Incorrect URL construction&lt;/li&gt;
&lt;li&gt;Authentication header requirements&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Key Takeaway
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Silent curl&lt;/strong&gt; is your automation workhorse—fast, reliable, and perfect for scripts that need to make decisions based on HTTP status codes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Verbose curl&lt;/strong&gt; is your debugging superhero—it tells you everything that's happening so you can diagnose and fix issues.&lt;/p&gt;

&lt;h3&gt;
  
  
  Quick Decision Matrix
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Use Case&lt;/th&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Why&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;CI/CD health checks&lt;/td&gt;
&lt;td&gt;Silent&lt;/td&gt;
&lt;td&gt;Clean output, fast execution, easy to parse&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Monitoring scripts&lt;/td&gt;
&lt;td&gt;Silent&lt;/td&gt;
&lt;td&gt;Programmatic status checking&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;API debugging&lt;/td&gt;
&lt;td&gt;Verbose&lt;/td&gt;
&lt;td&gt;See full request/response details&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SSL troubleshooting&lt;/td&gt;
&lt;td&gt;Verbose&lt;/td&gt;
&lt;td&gt;Certificate and handshake details&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Network diagnostics&lt;/td&gt;
&lt;td&gt;Verbose&lt;/td&gt;
&lt;td&gt;DNS, connection, and timing info&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Learning HTTP&lt;/td&gt;
&lt;td&gt;Verbose&lt;/td&gt;
&lt;td&gt;See what's really happening&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Pro Tips
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Combine them&lt;/strong&gt;: Use silent in automation, verbose for debugging when things go wrong&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Add timeouts&lt;/strong&gt;: Always use &lt;code&gt;--connect-timeout&lt;/code&gt; and &lt;code&gt;--max-time&lt;/code&gt; in production scripts&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Handle failures&lt;/strong&gt;: Use &lt;code&gt;|| echo "000"&lt;/code&gt; to get a predictable response on complete failures&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Parse carefully&lt;/strong&gt;: Clean up response codes with &lt;code&gt;tr -d '\n'&lt;/code&gt; if you get extra characters&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The next time you reach for curl, ask yourself: "Do I need to make a decision, or do I need to understand what's happening?" The answer will guide you to the right approach.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>platformengineer</category>
      <category>automation</category>
      <category>programming</category>
    </item>
    <item>
      <title>Embracing CDK: Use Case to Move from CloudFormation YAML to CDK?</title>
      <dc:creator>Clariza Look</dc:creator>
      <pubDate>Wed, 06 Aug 2025 02:33:26 +0000</pubDate>
      <link>https://forem.com/clarizalooktech/embracing-cdk-use-case-to-move-from-cloudformation-yaml-to-cdk-27n3</link>
      <guid>https://forem.com/clarizalooktech/embracing-cdk-use-case-to-move-from-cloudformation-yaml-to-cdk-27n3</guid>
      <description>&lt;h2&gt;
  
  
  The Dilemma Every Engineering Team Faces
&lt;/h2&gt;

&lt;p&gt;Picture this scenario: Your team has been successfully running AWS infrastructure for years using CloudFormation YAML templates. They work, they're familiar, and everyone knows how to maintain them. Meanwhile, your newer projects are built with AWS CDK, complete with comprehensive unit testing, type safety, and modern development practices.&lt;/p&gt;

&lt;p&gt;Sound familiar? You're not alone. Many engineering teams find themselves at this crossroads, managing a hybrid infrastructure landscape where legacy CloudFormation templates coexist with modern CDK applications. The question isn't whether both approaches work—they do. The question is whether this fragmentation is serving your team's long-term interests.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding the Players: CloudFormation vs CDK
&lt;/h2&gt;

&lt;p&gt;Before diving into the migration story, let's clarify what we're talking about.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is AWS CloudFormation?
&lt;/h3&gt;

&lt;p&gt;AWS CloudFormation is Amazon's native Infrastructure as Code (IaC) service that allows you to define your AWS resources using JSON or YAML templates. Think of it as a blueprint that tells AWS exactly what infrastructure to create.&lt;/p&gt;

&lt;p&gt;Here's a simple CloudFormation YAML example creating an S3 bucket:&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;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;Description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Simple&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;S3&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;bucket&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;example'&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;MyS3Bucket&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::S3::Bucket&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;BucketName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my-example-bucket&lt;/span&gt;
      &lt;span class="na"&gt;VersioningConfiguration&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;Status&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Enabled&lt;/span&gt;
      &lt;span class="na"&gt;PublicAccessBlockConfiguration&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;BlockPublicAcls&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
        &lt;span class="na"&gt;BlockPublicPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

&lt;span class="na"&gt;Outputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;BucketName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Name&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;of&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;the&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;S3&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;bucket'&lt;/span&gt;
    &lt;span class="na"&gt;Value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;MyS3Bucket&lt;/span&gt;
    &lt;span class="na"&gt;Export&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;Name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;MyS3BucketName&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;CloudFormation Characteristics:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Declarative&lt;/strong&gt;: You describe what you want, not how to create it&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;YAML/JSON syntax&lt;/strong&gt;: Human-readable but can become verbose&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AWS native&lt;/strong&gt;: Built specifically for AWS resources&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Template-based&lt;/strong&gt;: Static files that AWS reads and executes&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  What is AWS CDK?
&lt;/h3&gt;

&lt;p&gt;AWS CDK (Cloud Development Kit) is a newer approach that lets you define cloud infrastructure using familiar programming languages like TypeScript, Python, Java, or C#. Instead of writing YAML or JSON, you write actual code.&lt;/p&gt;

&lt;p&gt;Here's the same S3 bucket example using CDK (TypeScript):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Stack&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;StackProps&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws-cdk-lib&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Bucket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;BlockPublicAccess&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;BucketAccessControl&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws-cdk-lib/aws-s3&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;CfnOutput&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws-cdk-lib&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Construct&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;constructs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyInfrastructureStack&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Stack&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Construct&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;StackProps&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Create S3 bucket with sensible defaults&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;myBucket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Bucket&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;MyS3Bucket&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;bucketName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;my-example-bucket&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;versioned&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;blockPublicAccess&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;BlockPublicAccess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;BLOCK_ALL&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="c1"&gt;// Export bucket name for other stacks&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CfnOutput&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;BucketName&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;myBucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bucketName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;exportName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;MyS3BucketName&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Name of the S3 bucket&lt;/span&gt;&lt;span class="dl"&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;p&gt;&lt;strong&gt;CDK Characteristics:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Programmatic&lt;/strong&gt;: Uses actual programming languages with IDE support&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Higher-level abstractions&lt;/strong&gt;: Smart defaults and best practices built-in&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Type safety&lt;/strong&gt;: Compile-time error checking&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Testable&lt;/strong&gt;: Full unit testing capabilities like any other code&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Generates CloudFormation&lt;/strong&gt;: CDK compiles down to CloudFormation templates&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Key Differences at a Glance
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;CloudFormation YAML&lt;/th&gt;
&lt;th&gt;AWS CDK&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Language&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;YAML/JSON markup&lt;/td&gt;
&lt;td&gt;TypeScript, Python, Java, C#&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;IDE Support&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Basic syntax highlighting&lt;/td&gt;
&lt;td&gt;Full IntelliSense, autocomplete, refactoring&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Testing&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Limited to template validation&lt;/td&gt;
&lt;td&gt;Full unit testing with mocking&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Linting&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;cfn-lint for template validation&lt;/td&gt;
&lt;td&gt;ESLint, Prettier, language-specific linters&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Reusability&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Copy/paste templates&lt;/td&gt;
&lt;td&gt;Object-oriented inheritance and composition&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Learning Curve&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Learn YAML syntax + AWS resources&lt;/td&gt;
&lt;td&gt;Use existing programming skills&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Error Detection&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Runtime (during deployment)&lt;/td&gt;
&lt;td&gt;Compile-time + runtime&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  A More Complex Example: The Difference Becomes Clear
&lt;/h3&gt;

&lt;p&gt;Let's look at a more realistic example—creating an API Gateway that triggers a Lambda function and stores data in an S3 bucket. This shows how the complexity gap widens with real infrastructure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CloudFormation YAML approach:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;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;Description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;API&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Gateway&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;with&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Lambda&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;and&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;S3'&lt;/span&gt;

&lt;span class="na"&gt;Resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# S3 Bucket for data storage&lt;/span&gt;
  &lt;span class="na"&gt;DataBucket&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::S3::Bucket&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;BucketName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my-api-data-bucket&lt;/span&gt;
      &lt;span class="na"&gt;VersioningConfiguration&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;Status&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Enabled&lt;/span&gt;
      &lt;span class="na"&gt;PublicAccessBlockConfiguration&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;BlockPublicAcls&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
        &lt;span class="na"&gt;BlockPublicPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
        &lt;span class="na"&gt;IgnorePublicAcls&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
        &lt;span class="na"&gt;RestrictPublicBuckets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

  &lt;span class="c1"&gt;# IAM Role for Lambda execution&lt;/span&gt;
  &lt;span class="na"&gt;LambdaExecutionRole&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::IAM::Role&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;AssumeRolePolicyDocument&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;Principal&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;Service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;lambda.amazonaws.com&lt;/span&gt;
            &lt;span class="na"&gt;Action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sts:AssumeRole&lt;/span&gt;
      &lt;span class="na"&gt;ManagedPolicyArns&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole&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;PolicyName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;S3Access&lt;/span&gt;
          &lt;span class="na"&gt;PolicyDocument&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;s3:GetObject&lt;/span&gt;
                  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;s3:PutObject&lt;/span&gt;
                  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;s3:DeleteObject&lt;/span&gt;
                &lt;span class="na"&gt;Resource&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Sub&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;${DataBucket}/*'&lt;/span&gt;

  &lt;span class="c1"&gt;# Lambda Function&lt;/span&gt;
  &lt;span class="na"&gt;ApiLambdaFunction&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::Lambda::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;FunctionName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;api-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;nodejs18.x&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;index.handler&lt;/span&gt;
      &lt;span class="na"&gt;Role&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!GetAtt&lt;/span&gt; &lt;span class="s"&gt;LambdaExecutionRole.Arn&lt;/span&gt;
      &lt;span class="na"&gt;Code&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;ZipFile&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;exports.handler = async (event) =&amp;gt; {&lt;/span&gt;
            &lt;span class="s"&gt;return {&lt;/span&gt;
              &lt;span class="s"&gt;statusCode: 200,&lt;/span&gt;
              &lt;span class="s"&gt;body: JSON.stringify({ message: 'Hello from Lambda!' })&lt;/span&gt;
            &lt;span class="s"&gt;};&lt;/span&gt;
          &lt;span class="s"&gt;};&lt;/span&gt;
      &lt;span class="na"&gt;Environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;Variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;BUCKET_NAME&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;DataBucket&lt;/span&gt;

  &lt;span class="c1"&gt;# Permission for API Gateway to invoke Lambda&lt;/span&gt;
  &lt;span class="na"&gt;LambdaInvokePermission&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::Lambda::Permission&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;FunctionName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;ApiLambdaFunction&lt;/span&gt;
      &lt;span class="na"&gt;Action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;lambda:InvokeFunction&lt;/span&gt;
      &lt;span class="na"&gt;Principal&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apigateway.amazonaws.com&lt;/span&gt;
      &lt;span class="na"&gt;SourceArn&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Sub&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;${ApiGateway}/*/*'&lt;/span&gt;

  &lt;span class="c1"&gt;# API Gateway REST API&lt;/span&gt;
  &lt;span class="na"&gt;ApiGateway&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::ApiGateway::RestApi&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;Name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;MyAPI&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;API for Lambda integration&lt;/span&gt;
      &lt;span class="na"&gt;EndpointConfiguration&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;Types&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;REGIONAL&lt;/span&gt;

  &lt;span class="c1"&gt;# API Gateway Resource&lt;/span&gt;
  &lt;span class="na"&gt;ApiResource&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::ApiGateway::Resource&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;RestApiId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;ApiGateway&lt;/span&gt;
      &lt;span class="na"&gt;ParentId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!GetAtt&lt;/span&gt; &lt;span class="s"&gt;ApiGateway.RootResourceId&lt;/span&gt;
      &lt;span class="na"&gt;PathPart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;data&lt;/span&gt;

  &lt;span class="c1"&gt;# API Gateway Method&lt;/span&gt;
  &lt;span class="na"&gt;ApiMethod&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::ApiGateway::Method&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;RestApiId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;ApiGateway&lt;/span&gt;
      &lt;span class="na"&gt;ResourceId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;ApiResource&lt;/span&gt;
      &lt;span class="na"&gt;HttpMethod&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;POST&lt;/span&gt;
      &lt;span class="na"&gt;AuthorizationType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;NONE&lt;/span&gt;
      &lt;span class="na"&gt;Integration&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_PROXY&lt;/span&gt;
        &lt;span class="na"&gt;IntegrationHttpMethod&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;POST&lt;/span&gt;
        &lt;span class="na"&gt;Uri&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Sub&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ApiLambdaFunction.Arn}/invocations'&lt;/span&gt;

  &lt;span class="c1"&gt;# API Gateway Deployment&lt;/span&gt;
  &lt;span class="na"&gt;ApiDeployment&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::ApiGateway::Deployment&lt;/span&gt;
    &lt;span class="na"&gt;DependsOn&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;ApiMethod&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;RestApiId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;ApiGateway&lt;/span&gt;
      &lt;span class="na"&gt;StageName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;prod&lt;/span&gt;

&lt;span class="na"&gt;Outputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;ApiGatewayUrl&lt;/span&gt;&lt;span class="pi"&gt;:&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;API Gateway endpoint URL&lt;/span&gt;
    &lt;span class="na"&gt;Value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Sub&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;https://${ApiGateway}.execute-api.${AWS::Region}.amazonaws.com/prod'&lt;/span&gt;
    &lt;span class="na"&gt;Export&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;Name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ApiGatewayUrl&lt;/span&gt;

  &lt;span class="na"&gt;BucketName&lt;/span&gt;&lt;span class="pi"&gt;:&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;S3 bucket name&lt;/span&gt;
    &lt;span class="na"&gt;Value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;DataBucket&lt;/span&gt;
    &lt;span class="na"&gt;Export&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;Name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;DataBucketName&lt;/span&gt;

  &lt;span class="na"&gt;LambdaFunctionArn&lt;/span&gt;&lt;span class="pi"&gt;:&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;Lambda function ARN&lt;/span&gt;
    &lt;span class="na"&gt;Value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!GetAtt&lt;/span&gt; &lt;span class="s"&gt;ApiLambdaFunction.Arn&lt;/span&gt;
    &lt;span class="na"&gt;Export&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;Name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;LambdaFunctionArn&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;CDK approach (TypeScript):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Stack&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;StackProps&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;CfnOutput&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws-cdk-lib&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;RestApi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;LambdaIntegration&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws-cdk-lib/aws-apigateway&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;Function&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Runtime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Code&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws-cdk-lib/aws-lambda&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Bucket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;BlockPublicAccess&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws-cdk-lib/aws-s3&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Construct&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;constructs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ApiStack&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Stack&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Construct&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;StackProps&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// S3 Bucket for data storage&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dataBucket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Bucket&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;DataBucket&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;bucketName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;my-api-data-bucket&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;versioned&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;blockPublicAccess&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;BlockPublicAccess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;BLOCK_ALL&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="c1"&gt;// Lambda Function&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;apiFunction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Function&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ApiLambdaFunction&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;functionName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;api-handler&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Runtime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NODEJS_18_X&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;index.handler&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;code&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Code&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromInline&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`
        exports.handler = async (event) =&amp;gt; {
          return {
            statusCode: 200,
            body: JSON.stringify({ message: 'Hello from Lambda!' })
          };
        };
      `&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;BUCKET_NAME&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;dataBucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bucketName&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="c1"&gt;// Grant Lambda permissions to access S3 bucket&lt;/span&gt;
    &lt;span class="nx"&gt;dataBucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;grantReadWrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;apiFunction&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// API Gateway&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;api&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;RestApi&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ApiGateway&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;restApiName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;MyAPI&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;API for Lambda integration&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="c1"&gt;// Create API resource and method&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dataResource&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addResource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;data&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;dataResource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addMethod&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;LambdaIntegration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;apiFunction&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="c1"&gt;// Outputs&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CfnOutput&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ApiGatewayUrl&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;exportName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ApiGatewayUrl&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;API Gateway endpoint URL&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CfnOutput&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;BucketName&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;dataBucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bucketName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;exportName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;DataBucketName&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;S3 bucket name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CfnOutput&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;LambdaFunctionArn&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;apiFunction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;functionArn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;exportName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;LambdaFunctionArn&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Lambda function ARN&lt;/span&gt;&lt;span class="dl"&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;p&gt;&lt;strong&gt;The difference is striking:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;CloudFormation&lt;/strong&gt;: ~105 lines of YAML with manual IAM role creation, permissions, and resource linking&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CDK&lt;/strong&gt;: ~50 lines of TypeScript with automatic IAM role generation and permission management&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The CDK version automatically creates the Lambda execution role, sets up the proper IAM policies for S3 access, configures API Gateway permissions, and handles the deployment stage. The &lt;code&gt;dataBucket.grantReadWrite(apiFunction)&lt;/code&gt; line alone replaces 20+ lines of IAM policy configuration in CloudFormation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Deployment Process
&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%2Fmm7bknhcuip6m26i6qbi.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%2Fmm7bknhcuip6m26i6qbi.png" alt=" " width="800" height="381"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Hidden Costs of Infrastructure Fragmentation
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Two Worlds, Double the Overhead
&lt;/h3&gt;

&lt;p&gt;When your infrastructure spans both CloudFormation YAML and CDK, you're essentially maintaining two different technology stacks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Dual toolchains&lt;/strong&gt;: Different testing frameworks, deployment pipelines, and development workflows&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Split expertise&lt;/strong&gt;: Team members specializing in either YAML or CDK, creating knowledge silos&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Inconsistent practices&lt;/strong&gt;: Different approaches to testing, monitoring, and troubleshooting&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Onboarding complexity&lt;/strong&gt;: New team members need to learn both approaches&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Testing Gap
&lt;/h3&gt;

&lt;p&gt;Here's where the pain really shows. Your CDK projects likely have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Comprehensive unit tests validating resource configurations&lt;/li&gt;
&lt;li&gt;Integration tests ensuring components work together&lt;/li&gt;
&lt;li&gt;Automated validation in CI/CD pipelines&lt;/li&gt;
&lt;li&gt;Rapid feedback loops for developers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Meanwhile, your CloudFormation YAML templates probably have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Manual deployment processes&lt;/li&gt;
&lt;li&gt;Limited or no automated testing&lt;/li&gt;
&lt;li&gt;Post-deployment validation (if any)&lt;/li&gt;
&lt;li&gt;Slower feedback cycles when issues arise&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This isn't just a technical debt issue—it's a reliability and velocity problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Strategic Case for CDK Migration
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Standardization as a Force Multiplier
&lt;/h3&gt;

&lt;p&gt;When every project follows the same patterns, magic happens:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Knowledge Transfer Accelerates&lt;/strong&gt;: A developer who masters testing on one CDK project can immediately apply that knowledge to any other project. No context switching between YAML syntax and TypeScript/Python.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best Practices Propagate&lt;/strong&gt;: That clever testing pattern you developed for your API Gateway? It can be instantly applied across all projects, not just the CDK ones.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tooling Investment Pays Off&lt;/strong&gt;: Every improvement to your CDK testing framework, every CI/CD optimization, benefits your entire infrastructure portfolio.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Operational Excellence Through Consistency
&lt;/h3&gt;

&lt;p&gt;Consider these operational benefits:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Unified Monitoring&lt;/strong&gt;: All projects can leverage the same observability patterns, making it easier to understand system behavior across your entire infrastructure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Consistent Debugging&lt;/strong&gt;: When something goes wrong at 2 AM, you want your on-call engineer using familiar tools and approaches, regardless of which project is failing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Predictable Maintenance&lt;/strong&gt;: Security updates, dependency management, and refactoring become routine when you're working with a single technology stack.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. The Compound Effect of Testing and Code Quality
&lt;/h3&gt;

&lt;p&gt;Here's what comprehensive testing and linting really delivers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// CDK with TypeScript linting and testing&lt;/span&gt;
&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;API Gateway integrates correctly with Load Balancer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;template&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Template&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromStack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;template&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hasResourceProperties&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;AWS::ApiGateway::VpcLink&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;TargetArns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;Ref&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;NetworkLoadBalancer&lt;/span&gt;&lt;span class="dl"&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="c1"&gt;// ESLint catches issues before deployment&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;api&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;RestApi&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;MyApi&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;restApiName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;my-api&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;// ESLint enforces naming conventions&lt;/span&gt;
  &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;API for Lambda integration&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;  &lt;span class="c1"&gt;// Required by custom linting rules&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;CloudFormation Linting:&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;# Basic template validation&lt;/span&gt;
cfn-lint template.yml
&lt;span class="c"&gt;# Checks for syntax errors and basic AWS resource validation&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;CDK Linting &amp;amp; Quality Tools:&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;# TypeScript compilation catches type errors&lt;/span&gt;
npm run build

&lt;span class="c"&gt;# ESLint for code quality and style&lt;/span&gt;
npm run lint

&lt;span class="c"&gt;# Prettier for consistent formatting&lt;/span&gt;
npm run format

&lt;span class="c"&gt;# Unit tests with coverage&lt;/span&gt;
npm run &lt;span class="nb"&gt;test&lt;/span&gt;

&lt;span class="c"&gt;# CDK-specific validation&lt;/span&gt;
cdk synth  &lt;span class="c"&gt;# Validates and generates CloudFormation&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you can validate your infrastructure changes before deployment with comprehensive linting, type checking, and testing, you move faster. When you move faster with confidence, you innovate more. When you innovate more, you deliver better outcomes for your users.&lt;/p&gt;

&lt;h2&gt;
  
  
  Making the Migration Case: A Real-World Example
&lt;/h2&gt;

&lt;p&gt;Let's walk through a practical scenario. Imagine you have a legacy CloudFormation template managing your API infrastructure. It's deployed manually, tested manually, and updated carefully.&lt;/p&gt;

&lt;p&gt;Now consider the CDK equivalent:&lt;/p&gt;

&lt;h3&gt;
  
  
  Before: CloudFormation YAML
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# 200+ lines of YAML&lt;/span&gt;
&lt;span class="c1"&gt;# Manual testing&lt;/span&gt;
&lt;span class="c1"&gt;# No type safety&lt;/span&gt;
&lt;span class="c1"&gt;# Documentation in separate files&lt;/span&gt;
&lt;span class="c1"&gt;# Manual parameter management&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  After: CDK with Testing
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ApiInfrastructureStack&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Stack&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Construct&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;StackProps&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Self-documenting, type-safe infrastructure&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cluster&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;ecs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Cluster&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ApiCluster&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;vpc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vpc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;containerInsights&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;loadBalancer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;elbv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;NetworkLoadBalancer&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ApiNLB&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;vpc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vpc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;internetFacing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="c1"&gt;// Outputs that dependent stacks can safely import&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CfnOutput&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;LoadBalancerDNS&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;loadBalancer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;loadBalancerDnsName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;exportName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&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="nx"&gt;stackName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-LoadBalancerDNS`&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;The CDK version isn't just more testable—it's more readable, more maintainable, and less error-prone.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Migration Strategy That Actually Works
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Phase 1: Parallel Implementation (Weeks 1-2)
&lt;/h3&gt;

&lt;p&gt;Create the CDK equivalent while keeping your existing CloudFormation template running. This isn't about big-bang replacement—it's about building confidence.&lt;/p&gt;

&lt;h3&gt;
  
  
  Phase 2: Test Everything (Week 3)
&lt;/h3&gt;

&lt;p&gt;Implement the comprehensive testing that makes CDK valuable:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Unit tests for resource configuration&lt;/li&gt;
&lt;li&gt;Integration tests for component interaction&lt;/li&gt;
&lt;li&gt;End-to-end tests for user-facing functionality&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Phase 3: Validate in Non-Production (Weeks 4-5)
&lt;/h3&gt;

&lt;p&gt;Deploy the CDK version alongside your existing infrastructure in non-production environments. Compare outputs, validate behavior, and build team confidence.&lt;/p&gt;

&lt;h3&gt;
  
  
  Phase 4: Production Cutover (Week 6)
&lt;/h3&gt;

&lt;p&gt;With comprehensive testing and non-production validation complete, the production migration becomes a low-risk operation.&lt;/p&gt;

&lt;h2&gt;
  
  
  ROI: When Does Migration Pay Off?
&lt;/h2&gt;

&lt;p&gt;The mathematics are compelling:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Initial Investment&lt;/strong&gt;: 4-6 weeks of development time&lt;br&gt;
&lt;strong&gt;Ongoing Benefits&lt;/strong&gt;: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;50% faster infrastructure changes (thanks to testing confidence)&lt;/li&gt;
&lt;li&gt;75% reduction in deployment-related incidents&lt;/li&gt;
&lt;li&gt;100% improvement in developer onboarding speed for infrastructure work&lt;/li&gt;
&lt;li&gt;Infinite improvement in sleep quality for your on-call engineers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But the real ROI isn't just in time saved—it's in opportunities created. When your infrastructure is reliable and rapidly changeable, your team can focus on building features instead of fighting deployment issues.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Counterarguments (And Why They Don't Hold Up)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;"Our CloudFormation templates work fine"&lt;/strong&gt;: Yes, they work. But working and optimal are different things. Your flip phone worked fine too, but you probably upgraded for good reasons.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Migration is risky"&lt;/strong&gt;: True, but so is maintaining fragmented infrastructure. The gradual migration approach outlined above minimizes risk while maximizing learning.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"We don't have time"&lt;/strong&gt;: You don't have time not to do this. Every day you spend maintaining two different infrastructure approaches is time you're not spending on features that matter to your users.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Bottom Line
&lt;/h2&gt;

&lt;p&gt;Infrastructure standardization isn't just about choosing between CloudFormation and CDK—it's about choosing between fragmentation and focus. When your team can channel all their infrastructure expertise into a single, well-tested approach, everyone wins.&lt;/p&gt;

&lt;p&gt;Your legacy CloudFormation templates served you well, but they don't have to define your future. The question isn't whether you can afford to migrate to CDK—it's whether you can afford not to.&lt;/p&gt;

&lt;p&gt;The path forward is clear: embrace standardization, invest in testing, and build infrastructure that accelerates your team rather than slowing it down. Your future self (and your on-call schedule) will thank you.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Ready to start your CDK migration journey? Begin with a single, non-critical CloudFormation template and apply the gradual migration strategy outlined above. Small steps, big wins.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>devops</category>
      <category>security</category>
      <category>api</category>
      <category>programming</category>
    </item>
    <item>
      <title>Troubleshooting AWS API Gateway VPC Link with Network Load Balancer</title>
      <dc:creator>Clariza Look</dc:creator>
      <pubDate>Fri, 11 Jul 2025 11:30:35 +0000</pubDate>
      <link>https://forem.com/clarizalooktech/troubleshooting-aws-api-gateway-vpc-link-with-network-load-balancer-4ln8</link>
      <guid>https://forem.com/clarizalooktech/troubleshooting-aws-api-gateway-vpc-link-with-network-load-balancer-4ln8</guid>
      <description>&lt;p&gt;Building secure, private API architectures on AWS can be deceptively complex. What appears to be a straightforward integration between API Gateway, VPC Links, and Network Load Balancers often reveals multiple layers of configuration challenges that aren't immediately obvious from the documentation.&lt;/p&gt;

&lt;p&gt;This post chronicles a real-world troubleshooting journey that started with what seemed like a simple infrastructure deployment and evolved into a deep investigation across Lambda runtimes, API Gateway authentication, and AWS networking internals.&lt;/p&gt;

&lt;p&gt;Whether setting up our first private API integration or debugging existing infrastructure, this troubleshooting story will help us understand common pitfalls and their solutions in AWS private networking architectures.&lt;/p&gt;

&lt;h2&gt;
  
  
  Goal
&lt;/h2&gt;

&lt;p&gt;The goal was to create a secure, private API architecture with the following requirements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Private API Gateway&lt;/strong&gt; accessible only within the VPC A&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Secure authentication&lt;/strong&gt; using API keys and custom authorization logic&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;High availability&lt;/strong&gt; through load balancing across multiple containers&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Private networking&lt;/strong&gt; with no internet exposure&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scalable backend&lt;/strong&gt; using Fargate containerized application&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Target Architecture
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────────────────────────────────────────────────────────────┐
│                           VPC                                   │
│                                                                 │
│  ┌─────────────────┐    ┌──────────────────┐                    │
│  │   EC2 Client    │    │  API Gateway     │                    │
│  │   (Testing)     │───▶│  using VPC link  │                    │
│  └─────────────────┘    └──────────────────┘                    │
│                                   │                             │
│                          ┌────────▼────────┐                    │
│                          │  Private API    │                    │
│                          │    Gateway      │                    │
│                          │                 │                    │
│                          │ ┌─────────────┐ │                    │
│                          │ │   Lambda    │ │                    │
│                          │ │ Authorizer  │ │                    │
│                          │ └─────────────┘ │                    │
│                          └────────┬────────┘                    │
│                                   │                             │
│                              ┌────▼────┐                        │
│                              │VPC Link │                        │
│                              └────┬────┘                        │
│                                   │                             │
│                          ┌────────▼────────┐                    │
│                          │Internal Network │                    │
│                          │ Load Balancer   │                    │
│                          │   (Port 80)     │                    │
│                          └────────┬────────┘                    │
│                                   │                             │
│              ┌────────────────────┼────────────────────┐        │
│              │                    │                    │        │
│    ┌─────────▼─────────┐ ┌────────▼────────┐ ┌────────▼────────┐│
│    │   Fargate Task    │ │   Fargate Task  │ │   Fargate Task  ││
│    │   (Container 1)   │ │   (Container 2) │ │   (Container 3) ││
│    │                   │ │                 │ │                 ││
│    │ ┌───────────────┐ │ │ ┌─────────────┐ │ │ ┌─────────────┐ ││
│    │ │ Application   │ │ │ │Application  │ │ │ │Application  │ ││
│    │ │   Server      │ │ │ │   Server    │ │ │ │   Server    │ ││
│    │ └───────────────┘ │ │ └─────────────┘ │ │ └─────────────┘ ││
│    └───────────────────┘ └─────────────────┘ └─────────────────┘│
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

External Internet ❌ (No direct access - Private API only)
Public subnet access ❌ (No direct access - except who are inside the private subnets of the vpc)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The target architecture involved:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Private API Gateway&lt;/strong&gt; (accessible only within VPC)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;VPC Link&lt;/strong&gt; for private integration&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Internal Network Load Balancer&lt;/strong&gt; &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fargate containers&lt;/strong&gt; as targets&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Custom Lambda authorizer&lt;/strong&gt; for API key validation used by the API Gw&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Challenge
&lt;/h2&gt;

&lt;p&gt;What seemed like a straightforward setup quickly revealed multiple layers of complexity. Despite following AWS documentation and best practices, I still encountered a series of issues that required deep troubleshooting across different AWS services.&lt;/p&gt;

&lt;p&gt;Initially, our infrastructure appeared to work perfectly during development. Direct access to the NLB returned responses immediately, target groups were healthy, and everything looked correct.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; curl -v NLB-1xxxxx234567897.elb.ap-southeast-2.amazonaws.com

* Host NLB-1xxxxx234567897.elb.ap-southeast-2.amazonaws.com:80 was resolved.

* IPv6: (none)
* IPv4: xx.xxx.xxx.00, xx.xxx.x.111
*   Trying xx.xxx.x.111:80...
* Connected to NLB-1xxxxx234567897.elb.ap-southeast-2.amazonaws.com (xx.xxx.xx.00) port 80
* using HTTP/1.x
&amp;gt; GET / HTTP/1.1
&amp;gt; Host: NLB-1xxxxx234567897.elb.ap-southeast-2.amazonaws.com 
&amp;gt; User-Agent: curl/x.x.xx
&amp;gt; Accept: */*
&amp;gt; 
* Request completely sent off
&amp;lt; HTTP/1.1 200 OK
&amp;lt; x-correlation-id: aas19175-1b77-3333-0000-123456787
&amp;lt; X-Powered-By: Express
&amp;lt; Content-Type: text/html; charset=utf-8
&amp;lt; Content-Length: 5
&amp;lt; ETag: W/"5-fvYUSdz1234556789"
&amp;lt; Date: Fri, 11 Jul 2025 08:48:13 GMT
&amp;lt; Connection: keep-alive
&amp;lt; Keep-Alive: timeout=5
&amp;lt; 
* Connection #0 to host NLB-1xxxxx234567897.elb.ap-southeast-2.amazonaws.com  left intact
hello

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, when tying to access the API GW through the VPC Link integration (I created an EC2 instance that can access the private subnets of the VPC and curl from there), I encountered various issues that weren't immediately obvious.&lt;/p&gt;

&lt;h2&gt;
  
  
  The First Challenge: Lambda Runtime Issues
&lt;/h2&gt;

&lt;p&gt;Our Lambda authorizer had been working reliably for over three years, originally built with Node.js 12.x when the infrastructure was first deployed. However, when I needed to redeploy the stack, I encountered an immediate issue:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Error: Cannot find module 'aws-sdk'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The Root Cause:&lt;/strong&gt; AWS had deprecated Node.js 12.x runtime, and our deployment pipeline automatically upgraded to Node.js 18.x. However, Node.js 18.x runtime doesn't include AWS SDK v2 by default - a breaking change that affected our existing Lambda code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Legacy Code (Node.js 12.x - worked for 3 years):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;AWS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws-sdk&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;ssm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;AWS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;SSM&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="na"&gt;region&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ap-southeast-2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;paramValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ssm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getParameter&lt;/span&gt;&lt;span class="p"&gt;({...}).&lt;/span&gt;&lt;span class="nf"&gt;promise&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;Updated Code (Node.js 18.x - required migration):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;SSMClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;GetParameterCommand&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@aws-sdk/client-ssm&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ssmClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;SSMClient&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;region&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ap-southeast-2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;paramValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ssmClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;GetParameterCommand&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;Lesson Learned:&lt;/strong&gt; AWS runtime deprecations can affect long-running infrastructure. When Lambda runtimes are deprecated, existing functions continue to work, but new deployments require code updates to use supported runtimes and SDK versions.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Second Challenge: API Key Configuration
&lt;/h2&gt;

&lt;p&gt;Even after fixing the Lambda runtime issue, I continued getting &lt;code&gt;403 Forbidden&lt;/code&gt; responses.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; curl -H "x-api-key: xxxxxx" -H "accept: application/json" -X GET "https://12345apigateway.execute-api.ap-southeast-2.amazonaws.com/" -i


HTTP/1.1 403 Forbidden
Server: Server
Date: Thu, 10 Jul 2025 06:05:15 GMT
Content-Type: application/json
Content-Length: 24
Connection: keep-alive
x-amzn-RequestId: 123ff3234-0000-xxx
x-amzn-ErrorType: ForbiddenException
x-amz-apigw-id: 1er12344556=

{"message":"Forbidden"}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Lambda authorizer logs showed it was finding the API key and returning an "Allow" policy, but API Gateway was still rejecting requests.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Missing Piece:&lt;/strong&gt; API Usage Plans require explicit API key associations!&lt;/p&gt;

&lt;p&gt;While I had created both the Usage Plan and API Key using the Cloudformation template, I was missing the crucial link between them:&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;ApiUsagePlanKey&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::ApiGateway::UsagePlanKey&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;KeyId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;ApiKey&lt;/span&gt;
    &lt;span class="na"&gt;KeyType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;API_KEY&lt;/span&gt;
    &lt;span class="na"&gt;UsagePlanId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;UsagePlan&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a common gotcha that many developers encounter when setting up API Gateway authentication.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Third Challenge: VPC Link Connectivity
&lt;/h2&gt;

&lt;p&gt;With authentication working, I faced a new issue: VPC Link was timing out after 11 seconds with "Internal server error" messages.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; curl -H "x-api-key: xxxxxx" -H "accept: application/json" -X GET "https://12345apigateway.execute-api.ap-southeast-2.amazonaws.com/" -i


HTTP/1.1 500 Internal Server Error
Server: Server
Date: Fri, 11 Jul 2025 08:03:15 GMT
Content-Type: application/json
Content-Length: 36
Connection: keep-alive
x-amzn-RequestId: 34d5467890-15db-4a40-9acb-dr123456789
x-amzn-ErrorType: InternalServerErrorException
x-amz-apigw-id: EEgTmDq5556tYRwd=

{"message": "Internal server error"}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This was particularly puzzling because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Direct NLB access worked perfectly&lt;/li&gt;
&lt;li&gt;✅ VPC Link showed as "AVAILABLE"&lt;/li&gt;
&lt;li&gt;✅ Target groups were healthy&lt;/li&gt;
&lt;li&gt;✅ Security groups seemed correct&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Mysterious Timeout Pattern
&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%2Fqnnxq2a8kp97fknu973s.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%2Fqnnxq2a8kp97fknu973s.png" alt="Direct NLB access worked perfectly" width="730" height="660"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The pattern was clear: Direct connectivity worked flawlessly, but VPC Link couldn't reach the same NLB endpoints.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Deep Dive into Network Security
&lt;/h2&gt;

&lt;p&gt;I spent considerable time investigating network-level issues:&lt;/p&gt;

&lt;h3&gt;
  
  
  Security Group Configuration
&lt;/h3&gt;

&lt;p&gt;Initially, I configured the NLB security group to allow traffic from specific subnet CIDR blocks where the NLB was deployed. This seemed logical but didn't work.&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;# This approach failed&lt;/span&gt;
&lt;span class="na"&gt;SecurityGroupIngress&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;IpProtocol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;tcp&lt;/span&gt;
    &lt;span class="na"&gt;FromPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;
    &lt;span class="na"&gt;ToPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;
    &lt;span class="na"&gt;CidrIp&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;xx.x.x.0/22&lt;/span&gt;  &lt;span class="c1"&gt;# NLB subnet 1&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;IpProtocol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;tcp&lt;/span&gt;
    &lt;span class="na"&gt;FromPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;
    &lt;span class="na"&gt;ToPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;
    &lt;span class="na"&gt;CidrIp&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;xx.x.x.0/22&lt;/span&gt;  &lt;span class="c1"&gt;# NLB subnet 2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The Debugging Process
&lt;/h3&gt;

&lt;p&gt;I systematically verified:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Network ACLs:&lt;/strong&gt; Properly configured with allow rules&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DNS Resolution:&lt;/strong&gt; NLB resolved to correct private IPs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Target Health:&lt;/strong&gt; All Fargate containers responding&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;VPC Endpoints:&lt;/strong&gt; API Gateway VPC endpoint working correctly&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Direct Connectivity:&lt;/strong&gt; Manual curl tests from EC2 instances succeeded&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Breakthrough Discovery
&lt;/h3&gt;

&lt;p&gt;After extensive troubleshooting, I discovered that only allowing &lt;code&gt;0.0.0.0/0&lt;/code&gt; in the NLB security group made VPC Link work. This was concerning from a security perspective but provided a crucial clue about the traffic flow.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Real Solution: AWS Documentation
&lt;/h2&gt;

&lt;p&gt;While preparing to contact AWS Support about this networking puzzle, I discovered the official solution in AWS documentation:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For NLBs used with VPC Link, you should disable security group evaluation.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is documented in the &lt;a href="https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-nlb-for-vpclink-using-console.html" rel="noopener noreferrer"&gt;AWS API Gateway Developer Guide&lt;/a&gt; under point 4b.&lt;/p&gt;

&lt;p&gt;So I updated my CDK for the creating the NLB:&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;#PrivateLink traffic enforcement
&lt;/span&gt;&lt;span class="n"&gt;NetworkLoadBalancer&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="n"&gt;AWS&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ElasticLoadBalancingV2&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;LoadBalancer&lt;/span&gt;
  &lt;span class="n"&gt;Properties&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="err"&gt;!&lt;/span&gt;&lt;span class="n"&gt;Join&lt;/span&gt; &lt;span class="p"&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;span class="err"&gt;!&lt;/span&gt;&lt;span class="n"&gt;Ref&lt;/span&gt; &lt;span class="n"&gt;ServiceName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;NetworkLoadBalancer&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="n"&gt;network&lt;/span&gt;
    &lt;span class="n"&gt;Scheme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;internal&lt;/span&gt;
    &lt;span class="n"&gt;Subnets&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;!&lt;/span&gt;&lt;span class="n"&gt;Split&lt;/span&gt;
      &lt;span class="o"&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="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;Fn&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ImportValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;!&lt;/span&gt;&lt;span class="n"&gt;Sub&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;VpcStack&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;PrivateSubnetIDs&lt;/span&gt;
    &lt;span class="n"&gt;SecurityGroups&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="err"&gt;!&lt;/span&gt;&lt;span class="n"&gt;Ref&lt;/span&gt; &lt;span class="n"&gt;NetworkLoadBalancerSecurityGroup&lt;/span&gt;
    &lt;span class="c1"&gt;# This property controls PrivateLink traffic enforcement
&lt;/span&gt;    &lt;span class="n"&gt;EnforceSecurityGroupInboundRulesOnPrivateLinkTraffic&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;off&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;LoadBalancerAttributes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;load_balancing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cross_zone&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;enabled&lt;/span&gt;
        &lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;true&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
      &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;access_logs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;enabled&lt;/span&gt;
        &lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;false&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
      &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;deletion_protection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;enabled&lt;/span&gt;
        &lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;false&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;How it looks like in the console.&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%2F66y42vrj4x7hhem6mrz8.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%2F66y42vrj4x7hhem6mrz8.png" alt="Network load balancer" width="800" height="303"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Why This Works
&lt;/h3&gt;

&lt;p&gt;VPC Link uses AWS-managed infrastructure that operates outside your VPC's IP ranges. Rather than trying to guess or discover these IP ranges, AWS recommends disabling security group evaluation for NLBs used in VPC Link integrations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Security is still maintained through:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Private subnet placement (no internet gateway routes)&lt;/li&gt;
&lt;li&gt;API Gateway authentication layers&lt;/li&gt;
&lt;li&gt;Network ACLs (if configured restrictively)&lt;/li&gt;
&lt;li&gt;Application-level security controls&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. AWS SDK Version Compatibility
&lt;/h3&gt;

&lt;p&gt;When upgrading Lambda runtimes, be aware of SDK version changes. Node.js 18.x requires AWS SDK v3.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. API Gateway Usage Plan Associations
&lt;/h3&gt;

&lt;p&gt;Creating an API key and usage plan isn't enough - you must explicitly associate them with &lt;code&gt;AWS::ApiGateway::UsagePlanKey&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. VPC Link Security Groups
&lt;/h3&gt;

&lt;p&gt;For NLBs used with VPC Link, follow AWS guidance and disable security group evaluation rather than trying to configure specific IP ranges.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Systematic Troubleshooting
&lt;/h3&gt;

&lt;p&gt;When facing complex networking issues:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Verify each component independently&lt;/li&gt;
&lt;li&gt;Test direct connectivity vs. service-mediated connectivity&lt;/li&gt;
&lt;li&gt;Check AWS documentation for service-specific configuration patterns&lt;/li&gt;
&lt;li&gt;Consider contacting AWS Support for service-level investigations&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  5. Documentation First
&lt;/h3&gt;

&lt;p&gt;Before implementing complex workarounds, always check the official AWS documentation. The solution to our VPC Link issue was documented but easy to miss.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Architecture
&lt;/h2&gt;

&lt;p&gt;Our final working configuration:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;API Gateway&lt;/strong&gt; with proper Usage Plan and API key associations&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lambda authorizer&lt;/strong&gt; using AWS SDK v3&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;VPC Link&lt;/strong&gt; connecting to NLB with security group evaluation disabled&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Internal NLB&lt;/strong&gt; in private subnets&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fargate containers&lt;/strong&gt; handling application logic&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This setup provides a secure, scalable private API architecture that follows AWS best practices.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Complex AWS networking scenarios often require understanding the interaction between multiple services. What seemed like a simple VPC Link + NLB integration revealed several layers of configuration requirements, from Lambda runtime compatibility to service-specific security patterns.&lt;/p&gt;

&lt;p&gt;The key to successful troubleshooting is methodical verification of each component, careful reading of AWS documentation, and understanding that AWS services sometimes have specific configuration patterns that differ from general networking principles.&lt;/p&gt;

&lt;p&gt;When in doubt, the AWS documentation and support team are invaluable resources for understanding the intended architecture patterns for complex service integrations.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>programming</category>
      <category>infosec</category>
      <category>cloudnetworking</category>
    </item>
    <item>
      <title>Building a Scalable WAF Log Pipeline: From AWS WAF to Cortex XDR with CDK</title>
      <dc:creator>Clariza Look</dc:creator>
      <pubDate>Tue, 24 Jun 2025 02:22:44 +0000</pubDate>
      <link>https://forem.com/clarizalooktech/building-a-scalable-waf-log-pipeline-from-aws-waf-to-cortex-xdr-with-cdk-2jo6</link>
      <guid>https://forem.com/clarizalooktech/building-a-scalable-waf-log-pipeline-from-aws-waf-to-cortex-xdr-with-cdk-2jo6</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;As organizations increasingly rely on AWS Web Application Firewall (WAF) to protect their applications, the need for effective log management and security analytics becomes critical. In this post, we'll walk through building a complete, production-ready pipeline that automatically processes WAF logs from CloudWatch to S3 and integrates with Palo Alto Cortex XDR for advanced security analytics.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Cortex XDR?
&lt;/h2&gt;

&lt;p&gt;Cortex XDR (Extended Detection and Response) is Palo Alto Networks' cloud-native security platform that provides comprehensive threat detection, investigation, and response capabilities. It's designed to:&lt;/p&gt;

&lt;h3&gt;
  
  
  Unified Security Operations:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Correlate data across endpoints, networks, and cloud environments&lt;/li&gt;
&lt;li&gt;Detect advanced threats using machine learning and behavioral analytics&lt;/li&gt;
&lt;li&gt;Automate incident response with playbooks and orchestration&lt;/li&gt;
&lt;li&gt;Provide centralized visibility into your entire security ecosystem&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why Cortex XDR for WAF Logs:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Web attack detection&lt;/strong&gt;: Identifies application-layer attacks, bot traffic, and malicious patterns&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Threat intelligence integration&lt;/strong&gt;: Correlates WAF events with global threat data&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automated blocking&lt;/strong&gt;: Can trigger automated firewall rules based on WAF patterns&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compliance support&lt;/strong&gt;: Helps meet security logging and monitoring requirements&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For organizations serious about application security, integrating WAF logs with Cortex XDR transforms raw log data into actionable security intelligence.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Challenge
&lt;/h2&gt;

&lt;p&gt;WAF logs provide invaluable security insights, but managing them effectively presents several challenges:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Volume&lt;/strong&gt;: WAF can generate massive amounts of log data&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost&lt;/strong&gt;: Storing logs in CloudWatch can become expensive at scale&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Analysis&lt;/strong&gt;: Raw logs need to be processed and analyzed for security insights&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Integration&lt;/strong&gt;: Security teams need logs in their SIEM/SOAR platforms&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Our Solution Architecture
&lt;/h2&gt;

&lt;p&gt;We'll build an automated pipeline using AWS CDK that:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Captures&lt;/strong&gt; WAF logs from CloudWatch Logs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Processes&lt;/strong&gt; logs via Lambda function&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stores&lt;/strong&gt; compressed logs in S3 with lifecycle management&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Notifies&lt;/strong&gt; Cortex XDR via SQS for real-time analysis&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Provides&lt;/strong&gt; secure access through IAM roles&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Architecture Diagram
&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%2F2kskaku76x43ubwttxpc.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%2F2kskaku76x43ubwttxpc.png" alt="WAF logs from CloudWatch Logs" width="800" height="285"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Data Flow:
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;WAF generates security logs from web traffic&lt;/li&gt;
&lt;li&gt;CloudWatch Logs captures and stores log events&lt;/li&gt;
&lt;li&gt;Lambda processes and compresses logs for efficient storage&lt;/li&gt;
&lt;li&gt;S3 stores compressed logs with automatic lifecycle transitions&lt;/li&gt;
&lt;li&gt;SQS notifies Cortex XDR of new log files for real-time analysis&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Implementation with AWS CDK
&lt;/h2&gt;

&lt;p&gt;Let's build this infrastructure using AWS CDK in Python:&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Architectural Decisions
&lt;/h3&gt;

&lt;p&gt;Before diving into the implementation, it's worth explaining a crucial design choice we made regarding log processing timing.&lt;/p&gt;

&lt;h4&gt;
  
  
  Real-time vs Batch Processing
&lt;/h4&gt;

&lt;p&gt;When configuring CloudWatch Logs subscription filters, you have two main options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Real-time processing&lt;/strong&gt;: Logs are sent to Lambda immediately as they arrive&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Batch processing&lt;/strong&gt;: Logs are buffered and sent in 5-minute intervals&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;We chose real-time processing for several critical reasons:&lt;/strong&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  The Hidden Cost Most Analyses Miss
&lt;/h4&gt;

&lt;p&gt;Many cost comparisons focus only on Lambda execution costs, but there's a crucial piece missing from traditional analysis - &lt;strong&gt;CloudWatch Logs Insights charges&lt;/strong&gt;! This oversight completely changes the cost equation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What Traditional Analysis Misses:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The 5-minute batch approach requires CloudWatch Logs Insights queries to retrieve and batch the logs before sending to Lambda. Most analyses only compare:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Lambda invocation costs&lt;/li&gt;
&lt;li&gt;✅ Lambda compute costs
&lt;/li&gt;
&lt;li&gt;✅ S3 PUT request costs&lt;/li&gt;
&lt;li&gt;❌ &lt;strong&gt;CloudWatch Logs Insights scanning charges&lt;/strong&gt; (the biggest expense!)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;5-Minute Batch Approach (Hidden Costs):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Lambda invocations: 8,640/month × $0.0000002 = $0.001&lt;/li&gt;
&lt;li&gt;Lambda compute: Lower cost ✅&lt;/li&gt;
&lt;li&gt;S3 PUTs: Fewer PUTs ✅ &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CloudWatch Logs Insights: 8,640 queries × data scanned&lt;/strong&gt; ❌&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Real-time Subscription Approach:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Lambda invocations: 720,000/month × $0.0000002 = $0.14&lt;/li&gt;
&lt;li&gt;Lambda compute: Higher cost ❌&lt;/li&gt;
&lt;li&gt;S3 PUTs: More PUTs ❌&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CloudWatch Logs Insights: $0&lt;/strong&gt; ✅&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Security Response Time&lt;/strong&gt;: WAF logs contain potential security threats and attack patterns. In cybersecurity, every minute counts. Real-time processing ensures that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Suspicious activity is detected immediately&lt;/li&gt;
&lt;li&gt;Security teams can respond to active attacks quickly&lt;/li&gt;
&lt;li&gt;Cortex XDR can trigger automated responses without delay&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Operational Visibility&lt;/strong&gt;: Real-time logs provide immediate insight into:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Application performance issues&lt;/li&gt;
&lt;li&gt;Traffic anomalies&lt;/li&gt;
&lt;li&gt;DDoS attack patterns&lt;/li&gt;
&lt;li&gt;Bot detection events&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Cost vs. Value Trade-off:
&lt;/h3&gt;

&lt;p&gt;When you factor in CloudWatch Logs Insights charges, real-time processing becomes significantly MORE cost-effective than batch processing. Here's the actual cost breakdown:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Estimated Daily Savings&lt;/strong&gt;&lt;br&gt;
For moderate WAF traffic (1GB logs/day):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Approach&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Lambda Invocations&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Lambda Duration&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;CW API&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Total/Day&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Real-time&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;$0.005&lt;/td&gt;
&lt;td&gt;$0.015&lt;/td&gt;
&lt;td&gt;$0&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$0.02&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Hourly&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;$0.0001&lt;/td&gt;
&lt;td&gt;$0.008&lt;/td&gt;
&lt;td&gt;$0.50&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$0.51&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h2&gt;
  
  
  Key Insights:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;CloudWatch API Penalty: Hourly batching requires CloudWatch Logs API calls to query and retrieve logs, costing $0.50/day - 25x more than the entire real-time processing cost.&lt;/li&gt;
&lt;li&gt;Real-time processing eliminates API costs by streaming logs directly via subscription filters, avoiding expensive CloudWatch queries entirely.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The analysis shows real-time processing is 96% cheaper ($0.02 vs $0.51/day) while providing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Immediate threat detection&lt;/li&gt;
&lt;li&gt;Real-time security response capabilities&lt;/li&gt;
&lt;li&gt;Better error isolation and recovery&lt;/li&gt;
&lt;li&gt;No CloudWatch API scanning charges&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Monthly savings: $14.70 ($0.60 vs $15.30) - real-time pays for itself and saves money!&lt;/p&gt;
&lt;h3&gt;
  
  
  Cortex XDR Integration:
&lt;/h3&gt;

&lt;p&gt;Security platforms like Cortex XDR are designed for real-time threat detection. Delayed log ingestion can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Miss time-sensitive attack patterns&lt;/li&gt;
&lt;li&gt;Reduce the effectiveness of machine learning models&lt;/li&gt;
&lt;li&gt;Impact incident response capabilities&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This architectural choice reflects our security-first approach, prioritizing threat detection speed over minor cost optimizations.&lt;/p&gt;
&lt;h2&gt;
  
  
  Project Structure
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;waf-log-pipeline/
├── app.py
├── requirements.txt
├── waf_log_pipeline/
│   ├── __init__.py
│   └── waf_log_pipeline_stack.py
└── tests/
    └── unit/
        └── test_waf_log_pipeline_stack.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Core CDK Stack
&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;import&lt;/span&gt; &lt;span class="n"&gt;aws_cdk&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;cdk&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;aws_cdk&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;Stack&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;aws_s3&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;aws_lambda&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;_lambda&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;aws_iam&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;iam&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;aws_logs&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;logs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;aws_sqs&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;sqs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;aws_s3_notifications&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;s3n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;CfnOutput&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="n"&gt;RemovalPolicy&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;WafLogPipelineStack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Stack&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Construct&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;construct_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;super&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;construct_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# S3 Bucket for log storage with lifecycle management
&lt;/span&gt;        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;waf_logs_bucket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Bucket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;WafLogsBucket&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;bucket_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;company-waf-logs-for-cortex&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;encryption&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BucketEncryption&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;S3_MANAGED&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;versioning&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;block_public_access&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BlockPublicAccess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BLOCK_ALL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;lifecycle_rules&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;LifecycleRule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;waf-logs-lifecycle&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;enabled&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;transitions&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
                        &lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Transition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                            &lt;span class="n"&gt;storage_class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StorageClass&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;INFREQUENT_ACCESS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                            &lt;span class="n"&gt;transition_after&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;days&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                        &lt;span class="p"&gt;),&lt;/span&gt;
                        &lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Transition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                            &lt;span class="n"&gt;storage_class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StorageClass&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GLACIER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                            &lt;span class="n"&gt;transition_after&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;days&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;90&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                        &lt;span class="p"&gt;),&lt;/span&gt;
                        &lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Transition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                            &lt;span class="n"&gt;storage_class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StorageClass&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DEEP_ARCHIVE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                            &lt;span class="n"&gt;transition_after&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;days&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;365&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;expiration&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;days&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2555&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# 7 years
&lt;/span&gt;                &lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="n"&gt;removal_policy&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;RemovalPolicy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DESTROY&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# Dead Letter Queue for failed messages
&lt;/span&gt;        &lt;span class="n"&gt;cortex_dlq&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="nc"&gt;Queue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CortexXDRNotificationDLQ&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;queue_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cortex-xdr-waf-logs-notifications-dlq&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;retention_period&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;days&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# SQS Queue for Cortex XDR notifications
&lt;/span&gt;        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cortex_notification_queue&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="nc"&gt;Queue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CortexXDRNotificationQueue&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;queue_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cortex-xdr-waf-logs-notifications&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;visibility_timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;minutes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;dead_letter_queue&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="nc"&gt;DeadLetterQueue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;max_receive_count&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;queue&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;cortex_dlq&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# Lambda function for log processing
&lt;/span&gt;        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;waf_log_processor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_lambda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;WafLogProcessor&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;runtime&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;_lambda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Runtime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PYTHON_3_12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;index.handler&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;_lambda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Code&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_inline&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_get_lambda_code&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
            &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;minutes&lt;/span&gt;&lt;span class="p"&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;memory_size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;512&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;environment&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;S3_BUCKET&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;waf_logs_bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bucket_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;S3_PREFIX&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;raw&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;# IAM permissions for Lambda
&lt;/span&gt;        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;waf_logs_bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;grant_write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;waf_log_processor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# CloudWatch Logs subscription filter
&lt;/span&gt;        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;subscription_filter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;logs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;SubscriptionFilter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;WafLogSubscriptionFilter&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;log_group&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;logs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LogGroup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_log_group_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;WafLogGroup&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;aws-waf-logs-company&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;destination&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;logs_destinations&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;LambdaDestination&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;waf_log_processor&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;filter_pattern&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;logs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FilterPattern&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all_events&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# S3 bucket notifications to SQS
&lt;/span&gt;        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;waf_logs_bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_event_notification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;EventType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OBJECT_CREATED&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;s3n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;SqsDestination&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cortex_notification_queue&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;NotificationKeyFilter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;raw/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;suffix&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.jsonl.gz&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;# IAM role for Cortex XDR access
&lt;/span&gt;        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cortex_xdr_role&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;iam&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Role&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CortexXDRRole&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;role_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CortexXDR-WAFLogs-AssumedRole&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;assumed_by&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;iam&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ServicePrincipal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cortex.paloaltonetworks.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;external_ids&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;your-external-id-here&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="n"&gt;inline_policies&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;CortexXDRS3Access&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;iam&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;PolicyDocument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="n"&gt;statements&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
                        &lt;span class="n"&gt;iam&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;PolicyStatement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                            &lt;span class="n"&gt;effect&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;iam&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="n"&gt;ALLOW&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                            &lt;span class="n"&gt;actions&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;s3:GetObject&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                            &lt;span class="n"&gt;resources&lt;/span&gt;&lt;span class="o"&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="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;waf_logs_bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bucket_arn&lt;/span&gt;&lt;span class="si"&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="p"&gt;),&lt;/span&gt;
                        &lt;span class="n"&gt;iam&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;PolicyStatement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                            &lt;span class="n"&gt;effect&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;iam&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="n"&gt;ALLOW&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                            &lt;span class="n"&gt;actions&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;s3:ListBucket&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                            &lt;span class="n"&gt;resources&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;waf_logs_bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bucket_arn&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
                        &lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="p"&gt;]&lt;/span&gt;
                &lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CortexXDRSQSAccess&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;iam&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;PolicyDocument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="n"&gt;statements&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
                        &lt;span class="n"&gt;iam&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;PolicyStatement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                            &lt;span class="n"&gt;effect&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;iam&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="n"&gt;ALLOW&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                            &lt;span class="n"&gt;actions&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;sqs:ReceiveMessage&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;sqs:DeleteMessage&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;sqs:ChangeMessageVisibility&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
                            &lt;span class="p"&gt;],&lt;/span&gt;
                            &lt;span class="n"&gt;resources&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cortex_notification_queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;queue_arn&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
                        &lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="p"&gt;]&lt;/span&gt;
                &lt;span class="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;def&lt;/span&gt; &lt;span class="nf"&gt;_get_lambda_code&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;'''&lt;/span&gt;&lt;span class="s"&gt;
import json
import boto3
import gzip
import base64
from datetime import datetime
import os

s3_client = boto3.client(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s3&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;)

def handler(event, context):
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Process CloudWatch Logs and store in S3.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;

    s3_bucket = os.environ[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;S3_BUCKET&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;]
    s3_prefix = os.environ[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;S3_PREFIX&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;]

    # Parse CloudWatch Logs event
    cw_data = event[&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="s"&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="s"&gt;]
    compressed_payload = base64.b64decode(cw_data)
    uncompressed_payload = gzip.decompress(compressed_payload)
    log_data = json.loads(uncompressed_payload)

    # Process log events
    processed_logs = []
    for log_event in log_data[&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="s"&gt;]:
        try:
            # Parse WAF log (already in JSON format)
            waf_log = json.loads(log_event[&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="s"&gt;])
            processed_logs.append(json.dumps(waf_log))
        except json.JSONDecodeError:
            # Handle non-JSON log entries
            processed_logs.append(json.dumps({
                &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="s"&gt;: log_event[&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="s"&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="s"&gt;: log_event[&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="s"&gt;]
            }))

    # Create filename with timestamp
    timestamp = datetime.utcnow().strftime(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;%Y%m%d-%H%M%S-%f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;)[:-3]
    filename = f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{s3_prefix}/waf-logs-{timestamp}.jsonl.gz&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;

    # Compress and upload to S3
    if processed_logs:
        content = &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s"&gt;n&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;.join(processed_logs)
        compressed_content = gzip.compress(content.encode(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;))

        s3_client.put_object(
            Bucket=s3_bucket,
            Key=filename,
            Body=compressed_content,
            ContentType=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;application/gzip&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;,
            ContentEncoding=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;gzip&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;
        )

        print(f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Uploaded {len(processed_logs)} log entries to s3://{s3_bucket}/{filename}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;)

    return {
        &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="s"&gt;: 200,
        &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="s"&gt;: json.dumps(f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Processed {len(processed_logs)} log entries&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Testing Infrastructure
&lt;/h3&gt;

&lt;p&gt;Create comprehensive but simple tests to validate your infrastructure:&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;aws_cdk&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;core&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;aws_cdk&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;assertions&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;waf_log_pipeline.waf_log_pipeline_stack&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;WafLogPipelineStack&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_s3_bucket_exists&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Test that S3 bucket is created.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;core&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;stack&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;WafLogPipelineStack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;test-stack&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;template&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;assertions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Template&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_stack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;template&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resource_count_is&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;AWS::S3::Bucket&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_lambda_function_exists&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Test that Lambda function is created.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;core&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;stack&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;WafLogPipelineStack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;test-stack&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;template&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;assertions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Template&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_stack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;template&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resource_count_is&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;AWS::Lambda::Function&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# Main + CDK helper
&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_sqs_queues_exist&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Test that SQS queues are created.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;core&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;stack&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;WafLogPipelineStack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;test-stack&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;template&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;assertions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Template&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_stack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;template&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resource_count_is&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;AWS::SQS::Queue&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# Main + DLQ
&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_cortex_role_exists&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Test that Cortex XDR role is created.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;core&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;stack&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;WafLogPipelineStack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;test-stack&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;template&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;assertions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Template&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_stack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;template&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has_resource_properties&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;AWS::IAM::Role&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;RoleName&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;CortexXDR-WAFLogs-AssumedRole&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;
  
  
  Deployment
&lt;/h2&gt;

&lt;p&gt;Deploy your infrastructure with these simple commands:&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;# Install dependencies&lt;/span&gt;
pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt

&lt;span class="c"&gt;# Bootstrap CDK (first time only)&lt;/span&gt;
cdk bootstrap

&lt;span class="c"&gt;# Deploy the stack&lt;/span&gt;
cdk deploy

&lt;span class="c"&gt;# Run tests&lt;/span&gt;
python &lt;span class="nt"&gt;-m&lt;/span&gt; pytest tests/ &lt;span class="nt"&gt;-v&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Cortex XDR Integration
&lt;/h2&gt;

&lt;p&gt;Once deployed, configure Cortex XDR to consume the logs:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Get Integration Details
&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;# Retrieve configuration values&lt;/span&gt;
aws cloudformation describe-stacks &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--stack-name&lt;/span&gt; waf-log-pipeline &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s1"&gt;'Stacks[0].Outputs'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Configure in Cortex XDR
&lt;/h3&gt;

&lt;p&gt;Navigate to: &lt;strong&gt;Settings → Data Sources → Add Data Source → Amazon S3&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%2Fuwpu1yhqg73rz6lkqpg1.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%2Fuwpu1yhqg73rz6lkqpg1.png" alt="Cortex XDR S3 Instance" width="488" height="557"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Configuration:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;SQS URL&lt;/strong&gt;: Use the queue URL from stack outputs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Role ARN&lt;/strong&gt;: Use the Cortex role ARN from outputs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;External ID&lt;/strong&gt;: Your configured external ID&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Log Type&lt;/strong&gt;: Generic&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Log Format&lt;/strong&gt;: JSON&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compression&lt;/strong&gt;: gzip&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vendor&lt;/strong&gt;: AWS&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Product&lt;/strong&gt;: WAF&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Key Features &amp;amp; Benefits
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Cost Optimization
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;S3 Lifecycle Management&lt;/strong&gt;: Automatically transitions logs to cheaper storage classes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compressed Storage&lt;/strong&gt;: Gzip compression reduces storage costs by ~70%&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CloudWatch Log Offloading&lt;/strong&gt;: Reduces expensive CloudWatch Logs storage&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Scalability
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Lambda Auto-scaling&lt;/strong&gt;: Handles varying log volumes automatically&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SQS Buffering&lt;/strong&gt;: Manages traffic spikes and processing delays&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;S3 Infinite Scale&lt;/strong&gt;: No storage capacity concerns&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Reliability
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Dead Letter Queue&lt;/strong&gt;: Captures failed message processing&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Error Handling&lt;/strong&gt;: Robust Lambda error handling and retries&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Infrastructure as Code&lt;/strong&gt;: Consistent, repeatable deployments&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Security
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;IAM Least Privilege&lt;/strong&gt;: Minimal required permissions for each component&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Encryption&lt;/strong&gt;: S3 server-side encryption enabled&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;VPC Integration&lt;/strong&gt;: Can be deployed in VPC for additional isolation&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Monitoring and Troubleshooting
&lt;/h2&gt;

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

&lt;ul&gt;
&lt;li&gt;Lambda function errors and duration&lt;/li&gt;
&lt;li&gt;SQS queue depth and message age&lt;/li&gt;
&lt;li&gt;S3 PUT/GET request metrics&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Common Issues and Solutions
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Lambda timeouts&lt;/strong&gt;: Increase memory allocation or timeout duration&lt;br&gt;
&lt;strong&gt;SQS message accumulation&lt;/strong&gt;: Check Lambda error logs and DLQ&lt;br&gt;
&lt;strong&gt;Missing logs in Cortex&lt;/strong&gt;: Verify IAM permissions and SQS configuration&lt;/p&gt;

&lt;h2&gt;
  
  
  Cost Analysis
&lt;/h2&gt;

&lt;p&gt;For a typical deployment processing 1GB of WAF logs daily:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;S3 Storage&lt;/strong&gt;: ~$0.50/month (with lifecycle transitions)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lambda Execution&lt;/strong&gt;: ~$2.00/month&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SQS Messages&lt;/strong&gt;: ~$0.10/month&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data Transfer&lt;/strong&gt;: Minimal within same region&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Total&lt;/strong&gt;: ~$2.60/month vs ~$15/month keeping logs in CloudWatch&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;This pipeline provides a robust, cost-effective solution for WAF log management and security analytics. By leveraging Infrastructure as Code with AWS CDK, we've created a maintainable, scalable system that integrates seamlessly with modern security platforms like Cortex XDR.&lt;/p&gt;

&lt;p&gt;The combination of automated processing, cost optimization, and real-time security analytics makes this architecture ideal for organizations serious about application security monitoring.&lt;/p&gt;




&lt;p&gt;References:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.paloaltonetworks.com.au/cortex/cortex-xdr" rel="noopener noreferrer"&gt;Cortext XDR&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://aws.amazon.com/cloudwatch/pricing/" rel="noopener noreferrer"&gt;Cloudwatch Pricing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs-cortex.paloaltonetworks.com/r/Cortex-XDR/Cortex-XDR-Documentation/Ingest-generic-logs-from-Amazon-S3" rel="noopener noreferrer"&gt;Ingest generic logs from Amazon S3&lt;/a&gt; 
&lt;em&gt;Ready to implement this in your environment?&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>programming</category>
      <category>devops</category>
      <category>cloudarchitecture</category>
      <category>infrastructuresecurity</category>
    </item>
    <item>
      <title>Troubleshooting SSL Certificate Chain Issues in Enterprise Integration Platforms: A Real-World Case Study</title>
      <dc:creator>Clariza Look</dc:creator>
      <pubDate>Tue, 17 Jun 2025 06:39:16 +0000</pubDate>
      <link>https://forem.com/clarizalooktech/troubleshooting-ssl-certificate-chain-issues-in-enterprise-integration-platforms-a-real-world-case-3i1l</link>
      <guid>https://forem.com/clarizalooktech/troubleshooting-ssl-certificate-chain-issues-in-enterprise-integration-platforms-a-real-world-case-3i1l</guid>
      <description>&lt;p&gt;How to diagnose and fix &lt;code&gt;PKIX certificate&lt;/code&gt; path building errors when integrating with external APIs.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem: A Mysterious SSL Error
&lt;/h2&gt;

&lt;p&gt;Picture this: You're working on an integration between your enterprise middleware platform and a third-party API endpoint (&lt;code&gt;api.partner-services.com&lt;/code&gt;), and suddenly you're hit with this cryptic error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;The request operation failed. Exception: com.ibm.jsse2.util.h: PKIX path building failed: 

java.security.cert.CertPathBuilderException: 

PKIXCertPathBuilderImpl could not build a valid CertPath.; 
internal cause is: java.security.cert.CertPathValidatorException: 

The certificate issued by 
CN=SSL.com EV Root Certification Authority RSA R2, O=SSL Corporation, L=Houston, ST=Texas, C=US is not trusted
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you've ever encountered this error, you know how frustrating it can be. The integration was working fine, but now it's failing with SSL certificate validation issues. Let's walk through how to diagnose and fix this step-by-step.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding the Error
&lt;/h2&gt;

&lt;p&gt;The error message tells us several key things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;PKIX path building failed&lt;/strong&gt;: The system can't build a complete certificate chain&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Certificate not trusted&lt;/strong&gt;: A specific SSL.com certificate isn't being trusted&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Root cause&lt;/strong&gt;: Missing or incomplete certificate chain in the trust store&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 1: Identify the Failing Endpoint
&lt;/h3&gt;

&lt;p&gt;First, we need to identify exactly which endpoint is causing the SSL error. This information can usually be found in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Platform logs or activity monitor&lt;/li&gt;
&lt;li&gt;Integration flow configuration&lt;/li&gt;
&lt;li&gt;HTTP connector settings&lt;/li&gt;
&lt;li&gt;Error details in the runtime logs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In our case, the failing endpoint was: &lt;code&gt;api.partner-services.com:443/oauth/token&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Analyze the Certificate Chain
&lt;/h3&gt;

&lt;p&gt;This is the crucial step that most troubleshooting guides skip. We need to see the actual certificate chain that the server is presenting. Use OpenSSL to inspect the complete chain:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;openssl s_client &lt;span class="nt"&gt;-showcerts&lt;/span&gt; &lt;span class="nt"&gt;-connect&lt;/span&gt; api.partner-services.com:443
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What the Output Reveals
&lt;/h3&gt;

&lt;p&gt;The OpenSSL output showed us a 4-level certificate chain:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Certificate chain
 0 s:C=US, ST=California, O=Partner Services Inc, CN=api.partner-services.com
   i:C=US, O=SSL Corporation, CN=Entrust OV TLS Issuing RSA CA 1

 1 s:C=US, O=SSL Corporation, CN=Entrust OV TLS Issuing RSA CA 1
   i:C=US, O=SSL Corporation, CN=SSL.com TLS RSA Root CA 2022

 2 s:C=US, O=SSL Corporation, CN=SSL.com TLS RSA Root CA 2022
   i:C=US, ST=Texas, L=Houston, O=SSL Corporation, CN=SSL.com EV Root Certification Authority RSA R2

 3 s:C=US, ST=Texas, L=Houston, O=SSL Corporation, CN=SSL.com EV Root Certification Authority RSA R2
   i:C=US, ST=Texas, L=Houston, O=SSL Corporation, CN=SSL.com EV Root Certification Authority RSA R2
&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%2Fdanzakn7v6j7x7fhgxwz.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%2Fdanzakn7v6j7x7fhgxwz.png" alt="fix PKIX certificate" width="800" height="346"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This revealed the complete trust chain:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;em&gt;End Certificate&lt;/em&gt;: &lt;code&gt;api.partner-services.com&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Intermediate CA&lt;/em&gt;: &lt;code&gt;Entrust OV TLS Issuing RSA CA 1&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Intermediate CA&lt;/em&gt;: &lt;code&gt;SSL.com TLS RSA Root CA 2022&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Root CA&lt;/em&gt;: &lt;code&gt;SSL.com EV Root Certification Authority RSA R2&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Step 3: Identify Missing Certificates
&lt;/h3&gt;

&lt;p&gt;Comparing what our integration platform had versus what was needed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ &lt;strong&gt;SSL.com EV Root Certification Authority RSA R2&lt;/strong&gt; (already imported)&lt;/li&gt;
&lt;li&gt;❌ &lt;strong&gt;SSL.com TLS RSA Root CA 2022&lt;/strong&gt; (missing)&lt;/li&gt;
&lt;li&gt;❌ &lt;strong&gt;Entrust OV TLS Issuing RSA CA 1&lt;/strong&gt; (missing)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The issue was clear - we had the root certificate but were missing the two intermediate certificates needed to complete the chain.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Locate and Download Missing Certificates
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Finding SSL.com Certificates&lt;/strong&gt;&lt;br&gt;
Navigate to &lt;a href="https://www.ssl.com/repository/" rel="noopener noreferrer"&gt;SSL.com's Certificate Repository&lt;/a&gt; and locate:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. SSL.com TLS RSA Root CA 2022&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;File: &lt;code&gt;SSLcom-TLS-Root-2022-RSA.pem&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;SHA1: &lt;code&gt;72:B2:76:44:BD:09:46:BF:3C:51:0C:00:0F:44:30:16:8F:E2:89:8A&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. Entrust OV TLS Issuing RSA CA 1&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Serial: &lt;code&gt;6FE63EDE5FC1C03AFB6D7A85BD3A156D&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;SHA1: &lt;code&gt;CA:AA:C3:E1:6B:D2:42:66:88:7C:C8:56:73:C0:18:F4:6A:13:9B:0F&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Download both certificates in PEM format.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5: Import Certificates in Your Integration Platform
&lt;/h3&gt;

&lt;p&gt;Import Process&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Navigate to your platform's SSL certificate management section&lt;/li&gt;
&lt;li&gt;Select "&lt;strong&gt;Import from Clipboard&lt;/strong&gt;" or similar option&lt;/li&gt;
&lt;li&gt;Paste the complete PEM certificate (including BEGIN/END lines)&lt;/li&gt;
&lt;li&gt;Set certificate type as "CA Certificate" or "Intermediate Certificate"&lt;/li&gt;
&lt;li&gt;Provide descriptive aliases:

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;SSL.com-TLS-Root-2022-RSA&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Entrust-OV-TLS-Issuing-RSA-CA-1&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Troubleshooting Import Issues
&lt;/h3&gt;

&lt;p&gt;If you encounter "Couldn't establish a chain of trust" during import:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Check "Trusted" or "Mark as trusted" during import&lt;/li&gt;
&lt;li&gt;✅ Import certificates in order (root to intermediate)&lt;/li&gt;
&lt;li&gt;✅ Use "CA Certificate" import option instead of "TrustStore Entry"&lt;/li&gt;
&lt;li&gt;✅ Ensure complete PEM format with proper BEGIN/END lines&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 6: Deploy and Test
&lt;/h2&gt;

&lt;p&gt;After importing both certificates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Deploy the configuration in your integration platform&lt;/li&gt;
&lt;li&gt;Wait 1-5 minutes for changes to take effect&lt;/li&gt;
&lt;li&gt;Test the integration that was previously failing&lt;/li&gt;
&lt;li&gt;Monitor logs for any remaining certificate errors&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Understanding Certificate Chains: Why Not Just One Certificate?
&lt;/h2&gt;

&lt;p&gt;You might wonder why we need multiple certificates instead of just one. Here's why certificate chains exist:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Security Benefits&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Root CA Protection: Root certificates are kept offline and ultra-secure&lt;/li&gt;
&lt;li&gt;Risk Limitation: If an intermediate is compromised, only that level needs replacement&lt;/li&gt;
&lt;li&gt;Compartmentalization: Different intermediates serve different purposes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Operational Benefits&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Scalability: One root can't physically sign millions of certificates&lt;/li&gt;
&lt;li&gt;Flexibility: Easier to revoke or replace intermediate certificates&lt;/li&gt;
&lt;li&gt;Specialization: Different validation levels and certificate types&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Trust Model&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Browser Trust: Browsers only trust ~150 pre-installed root CAs&lt;/li&gt;
&lt;li&gt;Delegation: Root CAs delegate trust to intermediate CAs&lt;/li&gt;
&lt;li&gt;Chain Validation: Each level vouches for the level below it&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Who Is Responsible for Implementing Certificate Chains?
&lt;/h2&gt;

&lt;p&gt;Understanding certificate chain responsibilities helps prevent issues and clarifies troubleshooting ownership:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Certificate Authorities (CAs) Design the Chain&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SSL.com, DigiCert, Let's Encrypt, etc. design and operate the certificate hierarchy&lt;/li&gt;
&lt;li&gt;They decide how many intermediate levels to use for security and scalability&lt;/li&gt;
&lt;li&gt;They create and manage the intermediate CAs&lt;/li&gt;
&lt;li&gt;They determine the chain structure for operational efficiency&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Service Providers Configure the Chain&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;API providers (like api.partner-services.com) configure their web servers to present the complete chain&lt;/li&gt;
&lt;li&gt;They install the end certificate + all intermediate certificates on their servers&lt;/li&gt;
&lt;li&gt;They're responsible for ensuring clients can validate the full chain&lt;/li&gt;
&lt;li&gt;They handle certificate renewals and chain updates&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Integration Teams Handle Missing Components&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Integration developers import any missing intermediate/root certificates into client systems&lt;/li&gt;
&lt;li&gt;Your middleware platform needs the complete chain to validate connections&lt;/li&gt;
&lt;li&gt;You troubleshoot and fix incomplete chains in your trust store&lt;/li&gt;
&lt;li&gt;You monitor for certificate-related integration failures&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Infrastructure Teams Maintain Trust Stores&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;IT/Security teams maintain the root certificate trust stores across the organization&lt;/li&gt;
&lt;li&gt;They ensure enterprise systems have necessary CA certificates pre-installed&lt;/li&gt;
&lt;li&gt;They handle certificate lifecycle management, renewals, and security policies&lt;/li&gt;
&lt;li&gt;They establish standards for certificate validation and trust&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Shared Responsibility Model&lt;/strong&gt;&lt;br&gt;
The ideal approach follows a shared responsibility model:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Service providers should present complete certificate chains&lt;/li&gt;
&lt;li&gt;Clients should be prepared to import missing intermediates when needed&lt;/li&gt;
&lt;li&gt;Both parties should monitor certificate health and expiration dates&lt;/li&gt;
&lt;li&gt;Organizations should have processes for handling certificate issues&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this Case Study:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SSL.com: Designed the 4-level certificate chain architecture&lt;/li&gt;
&lt;li&gt;Partner Services: Configured their server to present the complete chain&lt;/li&gt;
&lt;li&gt;Integration Team: Imported the missing intermediate certificates to fix the validation&lt;/li&gt;
&lt;li&gt;IT Infrastructure: Maintains the overall certificate trust infrastructure&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Remember:&lt;/strong&gt; While service providers should ideally present complete chains, integration teams must be prepared to handle missing intermediate certificates as part of robust API integration practices.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Certificate chains require ALL certificates from root to intermediate&lt;/li&gt;
&lt;li&gt;OpenSSL is your best friend for diagnosing certificate chain issues&lt;/li&gt;
&lt;li&gt;Import intermediate certificates even if you have the root&lt;/li&gt;
&lt;li&gt;Check certificate repositories of the issuing CA for missing certificates&lt;/li&gt;
&lt;li&gt;Mark certificates as trusted during import to avoid validation errors&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Prevention Tips
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Regularly audit your certificate trust store&lt;/li&gt;
&lt;li&gt;Monitor certificate expiration dates and renewal schedules&lt;/li&gt;
&lt;li&gt;Test integrations in staging environments before production&lt;/li&gt;
&lt;li&gt;Document certificate dependencies for your integrations&lt;/li&gt;
&lt;li&gt;Set up monitoring for SSL certificate validation errors&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;SSL certificate chain issues can be frustrating, but with the right diagnostic approach, they're entirely solvable. The key is understanding that certificate validation requires the complete chain of trust, not just the root certificate.&lt;/p&gt;

&lt;p&gt;By using tools like OpenSSL to inspect certificate chains and systematically importing missing intermediate certificates, you can resolve these issues quickly and prevent them from recurring.&lt;/p&gt;

&lt;p&gt;Remember: when in doubt, check the complete certificate chain first. It's often the missing piece that solves the puzzle!&lt;/p&gt;

&lt;p&gt;Have you encountered similar SSL certificate issues in your integrations? Share your experiences and solutions in the comments below.&lt;/p&gt;




&lt;h2&gt;
  
  
  Additional Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.ssl.com/repository/" rel="noopener noreferrer"&gt;SSL.com Certificate Repository&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.openssl.org/docs/man1.1.1/man1/openssl-s_client.html" rel="noopener noreferrer"&gt;OpenSSL Command Reference&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.ssl.com/article/what-is-the-ssl-certificate-chain/" rel="noopener noreferrer"&gt;Understanding Certificate Chains&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>programming</category>
      <category>devops</category>
      <category>websecurity</category>
      <category>systemadministration</category>
    </item>
    <item>
      <title>Building Serverless Application with AWS CDK: ECS Fargate &amp; DynamoDB</title>
      <dc:creator>Clariza Look</dc:creator>
      <pubDate>Tue, 10 Jun 2025 07:18:54 +0000</pubDate>
      <link>https://forem.com/clarizalooktech/building-serverless-application-with-aws-cdk-ecs-fargate-dynamodb-1eha</link>
      <guid>https://forem.com/clarizalooktech/building-serverless-application-with-aws-cdk-ecs-fargate-dynamodb-1eha</guid>
      <description>&lt;p&gt;AWS offers a powerful combination of services that enable developers to build serverless architectures without managing underlying infrastructure. In this guide, we'll explore how to build a robust serverless application using AWS CDK with ECS Fargate and DynamoDB.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Architecture Matters
&lt;/h2&gt;

&lt;p&gt;The serverless paradigm has transformed how we think about application deployment. By combining &lt;strong&gt;ECS Fargate&lt;/strong&gt; for containerized compute with &lt;strong&gt;DynamoDB&lt;/strong&gt; for managed NoSQL storage, we create an architecture that scales automatically, reduces operational overhead, and optimizes costs. &lt;/p&gt;

&lt;p&gt;The AWS CDK (Cloud Development Kit) makes this entire setup reproducible and maintainable through code.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/clarizalooktech/cdk-ecs-fargate-example" rel="noopener noreferrer"&gt;Github Repository Example to Build Serverless Application with AWS CDK: ECS Fargate &amp;amp; DynamoDB&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Architecture Deep Dive
&lt;/h2&gt;

&lt;p&gt;Our serverless architecture follows a clean, layered approach that separates concerns while maintaining high availability:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;             ┌───────────────────────────────────┐
             │      Internet Users               │
             └───────────────────────────────────┘
                             │
                             ▼
             ┌───────────────────────────────────┐
             │  Application Load Balancer (ALB)  │
             │       - Listens on Port 80        │
             │       - Routes to ECS Service     │
             └───────────────────────────────────┘
                             │
                             ▼
     ┌─────────────────────────────────────────────┐
     │          ECS Fargate Service (2 Tasks)      │
     │ ┌─────────────────────────────────────────┐ │
     │ │  Task Definition                        │ │
     │ │  - Nginx Container (Port 3000)          │ │
     │ │  - Logs to AWS CloudWatch               │ │
     │ │  - Reads/Writes to DynamoDB             │ │
     │ └─────────────────────────────────────────┘ │
     └─────────────────────────────────────────────┘
                             │
                             ▼
           ┌───────────────────────────────────┐
           │        DynamoDB Table             │
           │  - Stores messages data           │
           │  - PAY_PER_REQUEST mode           │
           └───────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;strong&gt;Traffic Flow Breakdown&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Request Journey&lt;/strong&gt;: Internet users send requests to the Application Load Balancer, which intelligently distributes traffic across multiple ECS Fargate tasks running in different availability zones. Each task runs an Nginx container that processes requests and interacts with DynamoDB for data persistence.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scaling Mechanism&lt;/strong&gt;: The ECS service automatically adjusts the number of running tasks based on CPU and memory utilization, while DynamoDB scales read/write capacity on-demand, ensuring consistent performance regardless of load.&lt;/p&gt;

&lt;h2&gt;
  
  
  Technology Stack Explained
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;AWS CDK (TypeScript)&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;The AWS CDK serves as our infrastructure-as-code foundation. Unlike traditional CloudFormation templates, CDK allows us to define cloud resources using familiar programming languages, making infrastructure more maintainable and testable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key Benefits:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Type safety and IDE support&lt;/li&gt;
&lt;li&gt;Reusable constructs and patterns
&lt;/li&gt;
&lt;li&gt;Automatic CloudFormation generation&lt;/li&gt;
&lt;li&gt;Built-in best practices and security&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Amazon ECS Fargate&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Fargate eliminates the need to provision and manage EC2 instances for containerized applications. It provides a serverless compute engine that automatically handles infrastructure scaling, patching, and security.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why Fargate Over Lambda?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Longer execution times (no 15-minute limit)&lt;/li&gt;
&lt;li&gt;Full control over runtime environment&lt;/li&gt;
&lt;li&gt;Better suited for HTTP services and APIs&lt;/li&gt;
&lt;li&gt;Container-based deployment flexibility&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Amazon DynamoDB&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;As a fully managed NoSQL database, DynamoDB provides single-digit millisecond latency at any scale. The pay-per-request billing model aligns perfectly with serverless principles.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;DynamoDB Advantages:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Automatic scaling without downtime&lt;/li&gt;
&lt;li&gt;Built-in security with encryption at rest&lt;/li&gt;
&lt;li&gt;Global secondary indexes for flexible querying&lt;/li&gt;
&lt;li&gt;Point-in-time recovery and backups&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Project Structure and Organization
&lt;/h2&gt;

&lt;p&gt;We are using AWS Cloud Development Kit (CDK) to deploy our infrastructure to AWS. The structure looks like below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;├── bin/                    # CDK entry point
├── lib/                    # CDK Stack Definition
│   ├── ecs-construct-example-stack.ts  # Main CDK Stack
├── cdk.json               # CDK Configuration
├── package.json           # Dependencies
├── tsconfig.json          # TypeScript Configuration
└── README.md             # Project Documentation
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The structure follows CDK best practices, separating the application entry point (&lt;code&gt;bin/&lt;/code&gt;) from the actual infrastructure definitions (&lt;code&gt;lib/&lt;/code&gt;). This organization makes the codebase maintainable and allows for easy extension with additional stacks or constructs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deployment Process
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Prerequisites Setup&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Before deploying, ensure the development environment is properly configured (in the terminal):&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;# Install Node.js 18.x or higher&lt;/span&gt;
node &lt;span class="nt"&gt;--version&lt;/span&gt;

&lt;span class="c"&gt;# Configure AWS CLI with appropriate permissions&lt;/span&gt;
aws configure

&lt;span class="c"&gt;# Install AWS CDK globally&lt;/span&gt;
npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; aws-cdk

&lt;span class="c"&gt;# Bootstrap CDK (first-time setup)&lt;/span&gt;
cdk bootstrap

&lt;span class="c"&gt;# Verify CDK installation&lt;/span&gt;
cdk &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To setup the first CDK template, follow the steps in &lt;a href="https://docs.aws.amazon.com/cdk/v2/guide/getting-started.html" rel="noopener noreferrer"&gt;CDK Setup template&lt;/a&gt; to get the CDK file structure format similar to the above.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Deployment Steps&lt;/strong&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Updating the &lt;code&gt;bin&lt;/code&gt; folder
&lt;/h3&gt;

&lt;p&gt;Update the file from the bin folder with &lt;code&gt;ecs-construct-example.ts&lt;/code&gt;. It contains the entry point files for your CDK application. These are the files that get run when you execute CDK commands like &lt;code&gt;cdk deploy&lt;/code&gt; or &lt;code&gt;cdk synth&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;As you can see from the script below, it is importing the files from the &lt;code&gt;lib&lt;/code&gt; folder called &lt;code&gt;EcsFargateStack&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// bin/ecs-construct-example.ts&lt;/span&gt;&lt;span class="cp"&gt;

#!/usr/bin/env node
&lt;/span&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;source-map-support/register&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;cdk&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws-cdk-lib&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;EcsFargateStack&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../lib/ecs-construct-example-stack&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;cdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;App&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;EcsFargateStack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;EcsFargateStack&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;projectName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SimpleEcs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Dev&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;account&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CDK_DEFAULT_ACCOUNT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;region&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CDK_DEFAULT_REGION&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Creating the stack definition in the &lt;code&gt;lib&lt;/code&gt; folder
&lt;/h3&gt;

&lt;p&gt;Let's go to the lib folder and update one of the files with &lt;code&gt;lib/ecs-construct-example-stack.ts&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/clarizalooktech/cdk-ecs-fargate-example/blob/master/lib/ecs-construct-example-stack.ts" rel="noopener noreferrer"&gt;Github Reference for the &lt;code&gt;lib&lt;/code&gt; folder to setup the ecs fargate stack&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// in lib/ecs-construct-example-stack.ts&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;cdk&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws-cdk-lib&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Construct&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;constructs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;ecs&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws-cdk-lib/aws-ecs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;ec2&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws-cdk-lib/aws-ec2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;iam&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws-cdk-lib/aws-iam&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;dynamodb&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws-cdk-lib/aws-dynamodb&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;RemovalPolicy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;CfnOutput&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws-cdk-lib&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;elasticloadbalancing&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws-cdk-lib/aws-elasticloadbalancingv2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;ECSStackProps&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;cdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;StackProps&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;projectName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;EcsFargateStack&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;cdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Stack&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Construct&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ECSStackProps&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;projectName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;projectName&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;projectPrefix&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;projectName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;props&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="s2"&gt;-server`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Create a Dynamo db table&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dynamoTable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;dynamodb&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;DynamoDbMessages&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;partitionKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;dynamodb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AttributeType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;STRING&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt; 
      &lt;span class="na"&gt;sortKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;created_at&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;dynamodb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AttributeType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NUMBER&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;billingMode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;dynamodb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;BillingMode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PAY_PER_REQUEST&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;removalPolicy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;RemovalPolicy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DESTROY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CfnOutput&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;TableName&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;dynamoTable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tableName&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="cm"&gt;/******** 
     * 1. Create a vpc for the ecs or Can also reference a vpc if 
     * it has been created prior this deployment
    *********/&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;vpc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;ec2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Vpc&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Vpc&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;vpcName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;primary-vpc&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;natGateways&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;subnetConfiguration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;  
          &lt;span class="na"&gt;cidrMask&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;subnetType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ec2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SubnetType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PUBLIC&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Public&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;  
          &lt;span class="na"&gt;cidrMask&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;subnetType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ec2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SubnetType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PRIVATE_WITH_EGRESS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Private&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt;
      &lt;span class="na"&gt;maxAzs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; 
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="c1"&gt;// Create a security group that allows HTTP traffic on port 80 for the ECS container&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ecsSecGroup&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;ec2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;SecurityGroup&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;EcsSecurityGroup&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;securityGroupName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;projectName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;props&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="s2"&gt;-SecGroup`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;vpc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;allowAllOutbound&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="nx"&gt;ecsSecGroup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addIngressRule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ec2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Peer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ipv4&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0.0.0.0/0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;ec2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Port&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tcp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="c1"&gt;// Create cluster&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cluster&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;ecs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Cluster&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="s2"&gt;`ECS-Cluster`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;clusterName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;projectPrefix&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-cluster`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;vpc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;executionRolePolicy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;  &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;iam&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;PolicyStatement&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;effect&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;iam&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Effect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ALLOW&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;*&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="na"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ecr:GetAuthorizationToken&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ecr:BatchCheckLayerAvailability&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ecr:GetDownloadUrlForLayer&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ecr:BatchGetImage&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;logs:CreateLogStream&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;logs:PutLogEvents&lt;/span&gt;&lt;span class="dl"&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;// Create a fargate task definition &lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fargateTaskDefinition&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;ecs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;FargateTaskDefinition&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;FargateTaskDefinition&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;memoryLimitMiB&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;512&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;cpu&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;256&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;fargateTaskDefinition&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addToExecutionRolePolicy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;executionRolePolicy&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

   &lt;span class="nx"&gt;fargateTaskDefinition&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addToTaskRolePolicy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;iam&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;PolicyStatement&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;effect&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;iam&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Effect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ALLOW&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;dynamoTable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tableArn&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="na"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dynamodb:*&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;}));&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ecsContainer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fargateTaskDefinition&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addContainer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;NginxServerContainer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ecs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ContainerImage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromRegistry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;nginx:latest&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;cpu&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="na"&gt;memoryLimitMiB&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;256&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;essential&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ecs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;LogDrivers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;awsLogs&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="na"&gt;streamPrefix&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;nginx-ecs-server&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}),&lt;/span&gt;
      &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
        &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;DYNAMODB_MESSAGES_TABLE&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;dynamoTable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tableName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;APP_ID&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;nginx-ecs-server&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="nx"&gt;ecsContainer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addPortMappings&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;containerPort&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;protocol&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ecs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Protocol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;TCP&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="c1"&gt;// Create the service&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ecsFargateService&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;ecs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;FargateService&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Ecs-Service&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;cluster&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;taskDefinition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;fargateTaskDefinition&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;desiredCount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;assignPublicIp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;securityGroups&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;ecsSecGroup&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;lb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;elasticloadbalancing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ApplicationLoadBalancer&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ALB&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;vpc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;internetFacing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;albListener&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;lb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;AlbListener&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="nx"&gt;albListener&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addTargets&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Target&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;targets&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;ecsFargateService&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="na"&gt;healthCheck&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="nx"&gt;albListener&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;connections&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;allowDefaultPortFromAnyIpv4&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Open to all&lt;/span&gt;&lt;span class="dl"&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;p&gt;This ECS Fargate stack sets up a load-balanced, containerized web application running on ECS Fargate with DynamoDB backend storage.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;DynamoDB Table&lt;/strong&gt; - Creates a table for messages with partition key 'id' and sort key 'created_at'&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;VPC Setup&lt;/strong&gt; - Creates a new VPC with public and private subnets across 2 availability zones&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security Group&lt;/strong&gt; - Allows inbound traffic on port 3000 for the ECS containers&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ECS Cluster&lt;/strong&gt; - Sets up a Fargate cluster to run containerized applications&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Task Definition&lt;/strong&gt; - Configures a Fargate task with 512 MiB memory and 256 CPU units&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Container&lt;/strong&gt; - Runs an nginx:latest container with DynamoDB table name as environment variable&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ECS Service&lt;/strong&gt; - Deploys 2 instances of the task in private subnets without public IPs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Load Balancer&lt;/strong&gt; - Creates an internet-facing ALB that routes traffic to the ECS service on port 80&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IAM Permissions&lt;/strong&gt; - Grants the task execution role ECR/CloudWatch permissions and task role DynamoDB access&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;We first install the node dependencies and then test the cdk&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;# Install project dependencies&lt;/span&gt;
npm &lt;span class="nb"&gt;install&lt;/span&gt;

&lt;span class="c"&gt;# Preview changes before deployment&lt;/span&gt;
cdk diff

&lt;span class="c"&gt;# Deploy the complete infrastructure&lt;/span&gt;
cdk deploy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The deployment process creates a comprehensive serverless infrastructure including VPC networking, security groups, IAM roles, and all necessary AWS resources. The entire process typically completes within 10-15 minutes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Real-World Use Cases
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;1. Chat API Backend&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;This architecture excels as a microservices backend for real-time messaging applications. The ALB efficiently routes WebSocket connections and HTTP requests to Fargate tasks, while DynamoDB stores messages, user sessions, and conversation metadata.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Implementation Flow:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Users send messages through the frontend application&lt;/li&gt;
&lt;li&gt;ALB routes requests to available ECS tasks&lt;/li&gt;
&lt;li&gt;Nginx processes the requests and validates user authentication&lt;/li&gt;
&lt;li&gt;Message data gets stored in DynamoDB with timestamps and metadata&lt;/li&gt;
&lt;li&gt;Real-time updates are pushed back to connected clients&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;2. Serverless Web Application Backend&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Perfect for supporting modern frontend frameworks like React, Angular, or Vue.js, this architecture provides a scalable API layer that handles user authentication, data processing, and business logic.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Typical API Endpoints:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/api/users&lt;/code&gt; - User management and profiles&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/api/messages&lt;/code&gt; - Message CRUD operations&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/api/analytics&lt;/code&gt; - Dashboard metrics and reporting&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/api/health&lt;/code&gt; - System health monitoring&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;3. Analytics Dashboard System&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;The architecture supports complex analytical workloads by leveraging DynamoDB's querying capabilities and ECS Fargate's processing power for data aggregation and report generation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Security and Best Practices
&lt;/h2&gt;

&lt;p&gt;This structure follows best practices for: &lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Network Security&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;VPC isolation with public and private subnets&lt;/li&gt;
&lt;li&gt;Security groups restricting access to necessary ports only&lt;/li&gt;
&lt;li&gt;ALB SSL termination for encrypted communication&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;IAM and Access Control&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Least privilege access principles&lt;/li&gt;
&lt;li&gt;Task-specific IAM roles for ECS services&lt;/li&gt;
&lt;li&gt;DynamoDB access limited to required operations only&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Monitoring and Observability&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;CloudWatch integration for comprehensive logging&lt;/li&gt;
&lt;li&gt;Application-level metrics and health checks&lt;/li&gt;
&lt;li&gt;Automated alerting for performance anomalies&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Cost Optimization
&lt;/h2&gt;

&lt;p&gt;This serverless architecture optimizes costs through several mechanisms:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pay-per-use pricing&lt;/strong&gt;: DynamoDB's on-demand billing and Fargate's per-second billing ensure you only pay for actual resource consumption.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Automatic scaling&lt;/strong&gt;: Resources scale down during low-traffic periods, significantly reducing costs compared to always-on infrastructure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No idle capacity&lt;/strong&gt;: Unlike traditional server-based approaches, there's no wasted capacity during off-peak hours.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The beauty of this architecture lies in its simplicity to deploy yet powerful enough to handle production workloads. Whether you're building a startup MVP or scaling an enterprise application, this serverless foundation provides the flexibility and reliability needed for modern cloud applications.&lt;/p&gt;

&lt;p&gt;Start by cloning the repository, following the deployment steps, and customizing the Nginx container to serve your specific application logic. The modular CDK structure makes it easy to extend with additional AWS services like ElastiCache for caching, RDS for relational data, or SNS for notifications.&lt;/p&gt;

&lt;p&gt;The future of cloud applications is serverless, and this architecture provides a proven foundation for building scalable, cost-effective solutions that grow with your business needs.&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>programming</category>
      <category>cdk</category>
      <category>serverless</category>
    </item>
    <item>
      <title>Building and Deploying a Nginx Web Application with Docker and AWS EC2: A Step-by-Step Guide</title>
      <dc:creator>Clariza Look</dc:creator>
      <pubDate>Thu, 05 Jun 2025 09:34:41 +0000</pubDate>
      <link>https://forem.com/clarizalooktech/building-and-deploying-a-nginx-web-application-with-docker-and-aws-ec2-a-complete-step-by-step-jk7</link>
      <guid>https://forem.com/clarizalooktech/building-and-deploying-a-nginx-web-application-with-docker-and-aws-ec2-a-complete-step-by-step-jk7</guid>
      <description>&lt;h3&gt;
  
  
  Introduction
&lt;/h3&gt;

&lt;p&gt;In this blog, I am gonna walk you through creating a simple yet robust web application deployment pipeline using Nginx, Docker, Amazon ECR, and AWS EC2. By the end of this tutorial, we'll have a fully automated deployment system that can serve as a foundation for more complex applications.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/clarizalooktech/simple-nginx-app-cicd/tree/main" rel="noopener noreferrer"&gt;Github repository for Building and Deploying a Nginx Web Application with Docker and AWS EC2&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  What We're Building
&lt;/h3&gt;

&lt;p&gt;This project demonstrates a complete deployment workflow featuring:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A lightweight Nginx web server serving static content&lt;/li&gt;
&lt;li&gt;Containerized application using Docker&lt;/li&gt;
&lt;li&gt;Cloud-based image storage with Amazon ECR&lt;/li&gt;
&lt;li&gt;Automated deployment on AWS EC2&lt;/li&gt;
&lt;li&gt;CI/CD pipeline for seamless updates&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Prerequisites&lt;/strong&gt;&lt;br&gt;
Before diving in, ensure we have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AWS CLI configured with appropriate permissions&lt;/li&gt;
&lt;li&gt;Docker installed on the local machine&lt;/li&gt;
&lt;li&gt;Basic understanding of Docker, AWS services, and command line operations&lt;/li&gt;
&lt;li&gt;An AWS account with ECR and EC2 access&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Project Architecture Overview
&lt;/h3&gt;

&lt;p&gt;The application follows a modern containerized deployment pattern:&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%2Fac8vpph67tchqde43rm2.jpg" 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%2Fac8vpph67tchqde43rm2.jpg" alt="Nginx application CI/CD pipeline Flow" width="800" height="488"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This project structure is organized for clarity and maintainability:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;nginx-app-project/
├── .github/workflows/
│   └── deploy.yml                   # CI/CD Pipeline
├── nginx/
│   └── nginx.conf                   # Web server configuration
├── infra/
│   └── infrastructure_stack.py      # AWS infrastructure setup
├── test/unit/
│   └── test_infra_stack.py         # Infrastructure tests
├── src/                            # Static web content
├── Dockerfile                      # Container definition
└── README.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 1: Setting Up The Local Development Environment
&lt;/h3&gt;

&lt;p&gt;Start by creating the project directory and basic file structure:&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="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;mkdir &lt;/span&gt;nginx-app-deployment
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;nginx-app-deployment
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; nginx src infra &lt;span class="nb"&gt;test&lt;/span&gt;/unit .github/workflows
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create the main HTML content in the &lt;code&gt;src/&lt;/code&gt; directory. This is where the static website files will live. For a simple example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- src/index.html --&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;My Nginx App&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Welcome to My Containerized Nginx Application&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Successfully deployed with Docker and AWS!&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Configuring Nginx
&lt;/h3&gt;

&lt;p&gt;Create a custom Nginx configuration file to optimize the web server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="k"&gt;events&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;worker_connections&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;http&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;include&lt;/span&gt; &lt;span class="n"&gt;/etc/nginx/mime.types&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;default_type&lt;/span&gt; &lt;span class="nc"&gt;application/octet-stream&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kn"&gt;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;server_name&lt;/span&gt; &lt;span class="s"&gt;localhost&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="n"&gt;/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kn"&gt;root&lt;/span&gt; &lt;span class="n"&gt;/usr/share/nginx/html&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="kn"&gt;index&lt;/span&gt; &lt;span class="s"&gt;index.html&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="kn"&gt;try_files&lt;/span&gt; &lt;span class="nv"&gt;$uri&lt;/span&gt; &lt;span class="nv"&gt;$uri&lt;/span&gt;&lt;span class="n"&gt;/&lt;/span&gt; &lt;span class="n"&gt;/index.html&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;# Health check endpoint&lt;/span&gt;
        &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="n"&gt;/health&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kn"&gt;access_log&lt;/span&gt; &lt;span class="no"&gt;off&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="kn"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt; &lt;span class="s"&gt;"healthy&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="s"&gt;n"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="kn"&gt;add_header&lt;/span&gt; &lt;span class="s"&gt;Content-Type&lt;/span&gt; &lt;span class="nc"&gt;text/plain&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;p&gt;This configuration provides a basic web server setup with a health check endpoint that's useful for monitoring.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Creating the Dockerfile
&lt;/h3&gt;

&lt;p&gt;The Dockerfile defines how the application will be containerized:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; nginx:alpine&lt;/span&gt;

&lt;span class="c"&gt;# Copy custom nginx configuration&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; nginx/nginx.conf /etc/nginx/nginx.conf&lt;/span&gt;

&lt;span class="c"&gt;# Copy static content&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; src/ /usr/share/nginx/html/&lt;/span&gt;

&lt;span class="c"&gt;# Expose port 80&lt;/span&gt;
&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 80&lt;/span&gt;

&lt;span class="c"&gt;# Nginx runs in foreground by default in the base image&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["nginx", "-g", "daemon off;"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This lightweight Alpine-based image keeps the container size minimal while providing all necessary functionality.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Building and Testing Locally
&lt;/h3&gt;

&lt;p&gt;Before deploying to the cloud, test the application locally. From the laptop's terminal:&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;# Build the Docker image&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; docker build &lt;span class="nt"&gt;-t&lt;/span&gt; nginx-app-local &lt;span class="nb"&gt;.&lt;/span&gt;

&lt;span class="c"&gt;# Run the container&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 8080:80 &lt;span class="nt"&gt;--name&lt;/span&gt; nginx-test nginx-app-local

&lt;span class="c"&gt;# Test the application&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; curl http://localhost:8080
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; curl http://localhost:8080/health
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Visit &lt;code&gt;http://localhost:8080&lt;/code&gt; in the browser to verify everything works correctly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5: Setting Up Amazon ECR
&lt;/h3&gt;

&lt;p&gt;Amazon Elastic Container Registry will store the Docker images:&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;# Create ECR repository&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; aws ecr create-repository &lt;span class="nt"&gt;--repository-name&lt;/span&gt; nginx-app &lt;span class="nt"&gt;--region&lt;/span&gt; the-region

&lt;span class="c"&gt;# Get login token and authenticate Docker&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; aws ecr get-login-password &lt;span class="nt"&gt;--region&lt;/span&gt; the-region | docker login &lt;span class="nt"&gt;--username&lt;/span&gt; AWS &lt;span class="nt"&gt;--password-stdin&lt;/span&gt; the-account-id.dkr.ecr.the-region.amazonaws.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 6: Building and Pushing to ECR
&lt;/h3&gt;

&lt;p&gt;Tag and push the image to ECR. In the laptop's terminal:&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;# Tag&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; docker tag nginx-app-local:latest the-account-id.dkr.ecr.the-region.amazonaws.com/nginx-app:latest

&lt;span class="c"&gt;# Push to ECR&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; docker push the-account-id.dkr.ecr.the-region.amazonaws.com/nginx-app:latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 7: Infrastructure Setup with Python CDK
&lt;/h3&gt;

&lt;p&gt;Create the infrastructure setup script. It will deploy a few AWS resources including ec2, security group, iam roles, etc. It will also setup the user data that will be the command to be executed when the ec2 instance is deployed.&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;# infra/infrastructure_stack.py
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;aws_cdk&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;Stack&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;aws_ec2&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;ec2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;aws_iam&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;iam&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="n"&gt;CfnOutput&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;constructs&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Construct&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;NginxCicdStack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Stack&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Construct&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;super&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# Use the default VPC
&lt;/span&gt;        &lt;span class="n"&gt;vpc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ec2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DefaultVpc&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;is_default&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="c1"&gt;# Define the security group
&lt;/span&gt;        &lt;span class="n"&gt;security_group&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ec2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;SecurityGroup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SecurityGroup&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;vpc&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;vpc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Allow SSH and HTTP access&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;allow_all_outbound&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;security_group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_ingress_rule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ec2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Peer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ipv4&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0.0.0.0/0&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;ec2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Port&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tcp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;22&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Allow SSH access&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;security_group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_ingress_rule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ec2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Peer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ipv4&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0.0.0.0/0&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;ec2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Port&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tcp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Allow HTTP access&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# Create a role for the EC2 instance with ECR access
&lt;/span&gt;        &lt;span class="n"&gt;instance_role&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;iam&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Role&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;InstanceRole&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;assumed_by&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;iam&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ServicePrincipal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ec2.amazonaws.com&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;# Add ECR policy to the role
&lt;/span&gt;        &lt;span class="n"&gt;instance_role&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_managed_policy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;iam&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ManagedPolicy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_aws_managed_policy_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;AmazonEC2ContainerRegistryFullAccess&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;# Define the EC2 instance with the custom role
&lt;/span&gt;        &lt;span class="n"&gt;ec2_instance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ec2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Instance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Instance&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;instance_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ec2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;InstanceType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;t2.micro&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;machine_image&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ec2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MachineImage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;latest_amazon_linux2&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="n"&gt;vpc&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;vpc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;security_group&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;security_group&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;key_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;rsakey&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;role&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;instance_role&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# Assign the role with ECR permissions
&lt;/span&gt;            &lt;span class="n"&gt;user_data&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ec2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UserData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;custom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_get_user_data&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# Add a Name Tag to EC2 instance
&lt;/span&gt;        &lt;span class="n"&gt;Tags&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;of&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ec2_instance&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Name&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;NginxInstance&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# Output the instance ID and public IP
&lt;/span&gt;        &lt;span class="nc"&gt;CfnOutput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;InstanceId&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ec2_instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;instance_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nc"&gt;CfnOutput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;InstancePublicIp&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ec2_instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;instance_public_ip&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_get_user_data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;#!/bin/bash
# Update system packages
yum update -y

# Install Docker
amazon-linux-extras install docker -y
systemctl start docker
systemctl enable docker
usermod -a -G docker ec2-user

# Install AWS CLI v2 if needed
curl &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; -o &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;awscliv2.zip&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;
unzip -q awscliv2.zip
./aws/install --update
rm -rf aws awscliv2.zip

# Create a status file to signal instance is ready
touch /tmp/instance_ready

# Log completion
echo &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Instance setup complete&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 8: Deploying Infrastructure
&lt;/h3&gt;

&lt;p&gt;Deploy the infrastructure using CDK:&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;# Install CDK if not already installed&lt;/span&gt;
npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; aws-cdk

&lt;span class="c"&gt;# Bootstrap CDK (first time only)&lt;/span&gt;
cdk bootstrap

&lt;span class="c"&gt;# Deploy the stack&lt;/span&gt;
cdk deploy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 9: Automated Deployment with GitHub Actions
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Why Create a CI/CD Pipeline?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Before diving into the implementation, it's important to understand why we're creating a Continuous Integration/Continuous Deployment (CI/CD) pipeline and how it transforms our development workflow.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Problem Without CI/CD:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Without automation, deploying updates to the application involves numerous manual steps below (that are prone to human error):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Manually building Docker images on your local machine&lt;/li&gt;
&lt;li&gt;Remembering to tag images with the correct version&lt;/li&gt;
&lt;li&gt;Manually pushing images to ECR&lt;/li&gt;
&lt;li&gt;SSH-ing into EC2 instances to pull new images&lt;/li&gt;
&lt;li&gt;Stopping and starting containers manually&lt;/li&gt;
&lt;li&gt;Risk of deploying different versions across environments&lt;/li&gt;
&lt;li&gt;No rollback strategy if something goes wrong&lt;/li&gt;
&lt;li&gt;Time-consuming process that discourages frequent deployments&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What CI/CD Solves:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A well-designed CI/CD pipeline addresses these challenges by automating the entire deployment process:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Consistency:&lt;/strong&gt; Every deployment follows the exact same process, eliminating "it works on my machine" problems&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Speed:&lt;/strong&gt; Automated deployments happen in minutes rather than hours&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reliability:&lt;/strong&gt; Reduces human error through automation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Traceability:&lt;/strong&gt; Every deployment is tied to a specific code commit&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rollback capability:&lt;/strong&gt; Easy to revert to previous versions if issues arise&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Testing integration:&lt;/strong&gt; Automatically runs tests before deployment&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multi-environment support:&lt;/strong&gt; Can deploy to development, staging, and production with the same process&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Our Pipeline Strategy:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The GitHub Actions pipeline implements a deployment workflow that triggers automatically when code changes are pushed to the main branch. Here's what happens behind the scenes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Trigger: Developer pushes code to the main branch&lt;/li&gt;
&lt;li&gt;Build: Pipeline automatically builds a new Docker image&lt;/li&gt;
&lt;li&gt;Test: (Can be extended to run automated tests)&lt;/li&gt;
&lt;li&gt;Tag: Image is tagged with the Git commit SHA for traceability&lt;/li&gt;
&lt;li&gt;Push: Image is pushed to Amazon ECR&lt;/li&gt;
&lt;li&gt;Deploy: EC2 instance is updated with the new image&lt;/li&gt;
&lt;li&gt;Verify: Health checks ensure the deployment was successful&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This approach ensures that every code change goes through the same rigorous, automated process, making deployments predictable and reliable.&lt;/p&gt;

&lt;h4&gt;
  
  
  Create a CI/CD pipeline:
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;First iteration&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In this first iteration of the deploy.yml file, we can see the steps it does to Build, tag, and push image to Amazon ECR and deploy the ec2 instance.&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;# .github/workflows/deploy.yml&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deploy Nginx App&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;main&lt;/span&gt; &lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;main&lt;/span&gt; &lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;AWS_REGION&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;us-east-1&lt;/span&gt;
  &lt;span class="na"&gt;ECR_REPOSITORY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx-app&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deploy&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;

    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Checkout&lt;/span&gt;
      &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v3&lt;/span&gt;

    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Configure AWS credentials&lt;/span&gt;
      &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;aws-actions/configure-aws-credentials@v2&lt;/span&gt;
      &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;aws-access-key-id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.AWS_ACCESS_KEY_ID }}&lt;/span&gt;
        &lt;span class="na"&gt;aws-secret-access-key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.AWS_SECRET_ACCESS_KEY }}&lt;/span&gt;
        &lt;span class="na"&gt;aws-region&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ env.AWS_REGION }}&lt;/span&gt;

    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Login to Amazon ECR&lt;/span&gt;
      &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;login-ecr&lt;/span&gt;
      &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;aws-actions/amazon-ecr-login@v1&lt;/span&gt;

    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build, tag, and push image to Amazon ECR&lt;/span&gt;
      &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;build-image&lt;/span&gt;
      &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;ECR_REGISTRY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ steps.login-ecr.outputs.registry }}&lt;/span&gt;
        &lt;span class="na"&gt;IMAGE_TAG&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ github.sha }}&lt;/span&gt;
      &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
        &lt;span class="s"&gt;docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG .&lt;/span&gt;
        &lt;span class="s"&gt;docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG&lt;/span&gt;
        &lt;span class="s"&gt;echo "image=$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG" &amp;gt;&amp;gt; $GITHUB_OUTPUT&lt;/span&gt;

    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deploy to EC2&lt;/span&gt;
      &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;ECR_REGISTRY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ steps.login-ecr.outputs.registry }}&lt;/span&gt;
        &lt;span class="na"&gt;IMAGE_TAG&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ github.sha }}&lt;/span&gt;
      &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
        &lt;span class="s"&gt;# Add deployment script here&lt;/span&gt;
        &lt;span class="s"&gt;echo "Deployment completed with image: $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Final Iteration:&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;Meanwhile in the final iteration of the workflow, this is how the entire process will look like for the cicd pipeline.&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;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deploy Nginx App&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;main&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;build-docker-setup-infra&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;outputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;instance_ip&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ steps.wait-for-instance.outputs.instance_ip }}&lt;/span&gt;

    &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;REPOSITORY_URI&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.ECR_REPOSITORY_URI }}&lt;/span&gt;
      &lt;span class="na"&gt;CDK_DEFAULT_ACCOUNT&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.AWS_ACCOUNT_ID }}&lt;/span&gt;
      &lt;span class="na"&gt;CDK_DEFAULT_REGION&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ap-southeast-2&lt;/span&gt;

    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Checkout Code&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v3&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Configure AWS Credentials&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;aws-actions/configure-aws-credentials@v1&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;aws-access-key-id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.AWS_ACCESS_KEY_ID }}&lt;/span&gt;
          &lt;span class="na"&gt;aws-secret-access-key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.AWS_SECRET_ACCESS_KEY }}&lt;/span&gt;
          &lt;span class="na"&gt;aws-region&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ap-southeast-2&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Login to Amazon ECR&lt;/span&gt;
        &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;login-ecr&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;aws-actions/amazon-ecr-login@v1&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build, Tag, and Push Docker Image&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;set -x&lt;/span&gt;
          &lt;span class="s"&gt;IMAGE_TAG=latest&lt;/span&gt;
          &lt;span class="s"&gt;docker build -t $REPOSITORY_URI:$IMAGE_TAG .&lt;/span&gt;
          &lt;span class="s"&gt;docker push $REPOSITORY_URI:$IMAGE_TAG&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Set up Python&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-python@v2&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;python-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;3.8'&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install dependencies&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;python -m pip install --upgrade pip&lt;/span&gt;
          &lt;span class="s"&gt;pip install -r infra/requirements.txt&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install AWS CDK&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm install -g aws-cdk&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deploy CDK Stack&lt;/span&gt;
        &lt;span class="na"&gt;working-directory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./infra&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cdk deploy --require-approval never&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Wait for EC2 Instance to be Ready&lt;/span&gt;
        &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;wait-for-instance&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;# Get the instance ID from CDK outputs if possible&lt;/span&gt;
          &lt;span class="s"&gt;STACK_OUTPUTS=$(aws cloudformation describe-stacks --stack-name nginx-cicd-stack --query "Stacks[0].Outputs" --output json)&lt;/span&gt;
          &lt;span class="s"&gt;INSTANCE_ID=$(echo $STACK_OUTPUTS | jq -r '.[] | select(.OutputKey=="InstanceId") | .OutputValue')&lt;/span&gt;
          &lt;span class="s"&gt;INSTANCE_IP=$(echo $STACK_OUTPUTS | jq -r '.[] | select(.OutputKey=="InstancePublicIp") | .OutputValue')&lt;/span&gt;

          &lt;span class="s"&gt;# Fallback to searching by tag if outputs aren't available&lt;/span&gt;
          &lt;span class="s"&gt;if [ -z "$INSTANCE_ID" ] || [ "$INSTANCE_ID" == "null" ]; then&lt;/span&gt;
            &lt;span class="s"&gt;echo "Looking up instance by tag..."&lt;/span&gt;
            &lt;span class="s"&gt;INSTANCE_ID=$(aws ec2 describe-instances --filters "Name=tag:Name,Values=NginxInstance" "Name=instance-state-name,Values=pending,running" --query "Reservations[0].Instances[0].InstanceId" --output text)&lt;/span&gt;

            &lt;span class="s"&gt;if [ -z "$INSTANCE_ID" ] || [ "$INSTANCE_ID" == "None" ]; then&lt;/span&gt;
              &lt;span class="s"&gt;echo "Error: No running EC2 instance with tag 'NginxInstance' found."&lt;/span&gt;
              &lt;span class="s"&gt;exit 1&lt;/span&gt;
            &lt;span class="s"&gt;fi&lt;/span&gt;

            &lt;span class="s"&gt;INSTANCE_IP=$(aws ec2 describe-instances --instance-ids $INSTANCE_ID --query "Reservations[0].Instances[0].PublicIpAddress" --output text)&lt;/span&gt;
          &lt;span class="s"&gt;fi&lt;/span&gt;

          &lt;span class="s"&gt;echo "Found instance $INSTANCE_ID with IP $INSTANCE_IP"&lt;/span&gt;

          &lt;span class="s"&gt;# Wait for the instance to be running&lt;/span&gt;
          &lt;span class="s"&gt;echo "Waiting for instance to be in running state..."&lt;/span&gt;
          &lt;span class="s"&gt;aws ec2 wait instance-running --instance-ids $INSTANCE_ID&lt;/span&gt;

          &lt;span class="s"&gt;# Wait for status checks to pass&lt;/span&gt;
          &lt;span class="s"&gt;echo "Waiting for instance status checks to pass..."&lt;/span&gt;
          &lt;span class="s"&gt;aws ec2 wait instance-status-ok --instance-ids $INSTANCE_ID&lt;/span&gt;

          &lt;span class="s"&gt;# Extra verification of SSH availability&lt;/span&gt;
          &lt;span class="s"&gt;echo "Verifying SSH connectivity..."&lt;/span&gt;
          &lt;span class="s"&gt;counter=0&lt;/span&gt;
          &lt;span class="s"&gt;max_attempts=10&lt;/span&gt;
          &lt;span class="s"&gt;while [ $counter -lt $max_attempts ]; do&lt;/span&gt;
            &lt;span class="s"&gt;if nc -z -w5 $INSTANCE_IP 22; then&lt;/span&gt;
              &lt;span class="s"&gt;echo "SSH port is open!"&lt;/span&gt;
              &lt;span class="s"&gt;break&lt;/span&gt;
            &lt;span class="s"&gt;fi&lt;/span&gt;
            &lt;span class="s"&gt;echo "Waiting for SSH port to open... (attempt $((counter+1))/$max_attempts)"&lt;/span&gt;
            &lt;span class="s"&gt;sleep 10&lt;/span&gt;
            &lt;span class="s"&gt;counter=$((counter+1))&lt;/span&gt;
          &lt;span class="s"&gt;done&lt;/span&gt;

          &lt;span class="s"&gt;if [ $counter -eq $max_attempts ]; then&lt;/span&gt;
            &lt;span class="s"&gt;echo "Warning: Could not verify SSH connectivity after $max_attempts attempts"&lt;/span&gt;
          &lt;span class="s"&gt;fi&lt;/span&gt;

          &lt;span class="s"&gt;echo "Instance ID: $INSTANCE_ID"&lt;/span&gt;
          &lt;span class="s"&gt;echo "Instance IP: $INSTANCE_IP"&lt;/span&gt;
          &lt;span class="s"&gt;echo "instance_ip=$INSTANCE_IP" &amp;gt;&amp;gt; $GITHUB_OUTPUT&lt;/span&gt;

  &lt;span class="na"&gt;deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;needs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;build-docker-setup-infra&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;

    &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;REPOSITORY_URI&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.ECR_REPOSITORY_URI }}&lt;/span&gt;

    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Debug Output IP&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;echo&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;"Using&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;IP&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;address:&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;${{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;needs.build-docker-setup-infra.outputs.instance_ip&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"'&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install SSH Client&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sudo apt-get install -y openssh-client&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Create .ssh Directory&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mkdir -p ~/.ssh&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Add SSH Key&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;echo "${{ secrets.SSH_PRIVATE_KEY }}" &amp;gt; ~/.ssh/id_rsa&lt;/span&gt;
          &lt;span class="s"&gt;chmod 600 ~/.ssh/id_rsa&lt;/span&gt;
          &lt;span class="s"&gt;ssh-keygen -lf ~/.ssh/id_rsa || echo "Key verification failed"&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;SSH into EC2 and Deploy Docker Image&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;INSTANCE_IP&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ needs.build-docker-setup-infra.outputs.instance_ip }}&lt;/span&gt;
          &lt;span class="na"&gt;REPO_URI&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.ECR_REPOSITORY_URI }}&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;ssh -i ~/.ssh/id_rsa -o StrictHostKeyChecking=no -o ConnectTimeout=30 ec2-user@${INSTANCE_IP} "&lt;/span&gt;
            &lt;span class="s"&gt;# Check what's using port 80&lt;/span&gt;
            &lt;span class="s"&gt;echo 'Checking what process is using port 80...'&lt;/span&gt;
            &lt;span class="s"&gt;sudo lsof -i :80 || echo 'No process found by lsof'&lt;/span&gt;

            &lt;span class="s"&gt;# Stop Nginx if it's running&lt;/span&gt;
            &lt;span class="s"&gt;sudo systemctl stop nginx || echo 'Nginx not running or not installed'&lt;/span&gt;

            &lt;span class="s"&gt;# Kill any process using port 80&lt;/span&gt;
            &lt;span class="s"&gt;sudo fuser -k 80/tcp || echo 'No process killed'&lt;/span&gt;

            &lt;span class="s"&gt;# Stop any running Docker containers using port 80&lt;/span&gt;
            &lt;span class="s"&gt;sudo docker ps -q --filter publish=80 | xargs -r sudo docker stop&lt;/span&gt;
            &lt;span class="s"&gt;sudo docker ps -q --filter publish=80 | xargs -r sudo docker rm&lt;/span&gt;

            &lt;span class="s"&gt;# Start Docker and proceed with deployment&lt;/span&gt;
            &lt;span class="s"&gt;sudo systemctl start docker &amp;amp;&amp;amp;&lt;/span&gt;
            &lt;span class="s"&gt;sudo systemctl enable docker &amp;amp;&amp;amp;&lt;/span&gt;
            &lt;span class="s"&gt;aws ecr get-login-password --region ap-southeast-2 | sudo docker login --username AWS --password-stdin ${REPO_URI} &amp;amp;&amp;amp;&lt;/span&gt;
            &lt;span class="s"&gt;sudo docker pull ${REPO_URI}:latest &amp;amp;&amp;amp;&lt;/span&gt;
            &lt;span class="s"&gt;sudo docker run -d -p 80:80 ${REPO_URI}:latest"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 10: SSH Deployment and Container Management
&lt;/h3&gt;

&lt;p&gt;Once the EC2 instance is running, connect and deploy the container:&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;# SSH into the EC2 instance&lt;/span&gt;
ssh &lt;span class="nt"&gt;-i&lt;/span&gt; the-key.pem ec2-user@the-ec2-public-ip

&lt;span class="c"&gt;# Login to ECR&lt;/span&gt;
aws ecr get-login-password &lt;span class="nt"&gt;--region&lt;/span&gt; the-region | docker login &lt;span class="nt"&gt;--username&lt;/span&gt; AWS &lt;span class="nt"&gt;--password-stdin&lt;/span&gt; the-account-id.dkr.ecr.t-heregion.amazonaws.com

&lt;span class="c"&gt;# Pull and run the image&lt;/span&gt;
docker pull the-account-id.dkr.ecr.the-region.amazonaws.com/nginx-app:latest
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 80:80 &lt;span class="nt"&gt;--name&lt;/span&gt; nginx-app the-account-id.dkr.ecr.the-region.amazonaws.com/nginx-app:latest

&lt;span class="c"&gt;# Verify deployment&lt;/span&gt;
curl http://localhost/health
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 11: Testing and Validation
&lt;/h3&gt;

&lt;p&gt;Create unit tests for the infrastructure:&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;# test/unit/test_infra_stack.py
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;aws_cdk&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;core&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;aws_cdk.assertions&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;assertions&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;infra.infrastructure_stack&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;InfrastructureStack&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_ec2_instance_created&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;core&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;stack&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;InfrastructureStack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;test-stack&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;template&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;assertions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Template&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_stack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;template&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has_resource_properties&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;AWS::EC2::Instance&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;InstanceType&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;t3.micro&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_security_group_rules&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;core&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;stack&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;InfrastructureStack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;test-stack&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;template&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;assertions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Template&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_stack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;template&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has_resource_properties&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;AWS::EC2::SecurityGroup&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SecurityGroupIngress&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="p"&gt;{&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;IpProtocol&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;tcp&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;FromPort&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ToPort&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;80&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;Run tests with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; pytest &lt;span class="nb"&gt;test&lt;/span&gt;/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Monitoring and Maintenance
&lt;/h3&gt;

&lt;p&gt;Monitor the deployment with these commands:&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;# Check container status&lt;/span&gt;
docker ps

&lt;span class="c"&gt;# View logs&lt;/span&gt;
docker logs nginx-app

&lt;span class="c"&gt;# Update deployment&lt;/span&gt;
docker pull the-account-id.dkr.ecr.the-region.amazonaws.com/nginx-app:latest
docker stop nginx-app
docker &lt;span class="nb"&gt;rm &lt;/span&gt;nginx-app
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 80:80 &lt;span class="nt"&gt;--name&lt;/span&gt; nginx-app the-account-id.dkr.ecr.the-region.amazonaws.com/nginx-app:latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Next Steps and Enhancements
&lt;/h3&gt;

&lt;p&gt;This foundation can be extended with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Load balancing with Application Load Balancer&lt;/li&gt;
&lt;li&gt;Auto Scaling Groups for high availability&lt;/li&gt;
&lt;li&gt;CloudWatch monitoring and alerting&lt;/li&gt;
&lt;li&gt;SSL/TLS certificate management&lt;/li&gt;
&lt;li&gt;Database integration&lt;/li&gt;
&lt;li&gt;Container orchestration with ECS or EKS&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;You've successfully created a complete deployment pipeline for a containerized Nginx application using modern DevOps practices. This setup provides a solid foundation for more complex applications while demonstrating key concepts in containerization, cloud infrastructure, and automated deployment.&lt;/p&gt;

&lt;p&gt;The combination of Docker, AWS ECR, and EC2 creates a scalable, maintainable deployment solution that can grow with the application needs. The automated CI/CD pipeline ensures consistent deployments while the infrastructure-as-code approach makes the setup reproducible and version-controlled.&lt;/p&gt;

&lt;h2&gt;
  
  
  For full reference, here is the github I have created:
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/clarizalooktech/simple-nginx-app-cicd/tree/main" rel="noopener noreferrer"&gt;Github repository for Building and Deploying a Nginx Web Application with Docker and AWS EC2&lt;/a&gt;&lt;/p&gt;

</description>
      <category>devops</category>
      <category>programming</category>
      <category>python</category>
      <category>nginx</category>
    </item>
    <item>
      <title>Setting up APIcast in Red Hat OpenShift Service on AWS (ROSA) via Operator</title>
      <dc:creator>Clariza Look</dc:creator>
      <pubDate>Tue, 27 May 2025 08:46:58 +0000</pubDate>
      <link>https://forem.com/clarizalooktech/setting-up-3scale-apicast-in-rosa-4jen</link>
      <guid>https://forem.com/clarizalooktech/setting-up-3scale-apicast-in-rosa-4jen</guid>
      <description>&lt;p&gt;Last week, we took the first steps into the world of API management by setting up a self-hosted 3scale environment. Now, we're taking it to the next level by deploying 3scale APIcast on Red Hat OpenShift Service on AWS (ROSA) using the ROSA operator.  &lt;/p&gt;

&lt;p&gt;Here's what we learned along the way and why this migration makes sense for the infrastructure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Starting Point: Setting up Self-Hosted 3scale&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The journey began with a traditional self-hosted 3scale deployment. This gave us hands-on experience with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;API Management Fundamentals&lt;/strong&gt;: Understanding how 3scale handles API keys, rate limiting, and developer portals&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Configuration Management&lt;/strong&gt;: Learning the ins and outs of API policies, plans, and applications&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Integration Patterns&lt;/strong&gt;: Connecting the backend services to the 3scale gateway&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;While the self-hosted approach worked well for getting started, we quickly realized we wanted the scalability and managed services that come with a cloud-native approach.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is ROSA?
&lt;/h2&gt;

&lt;p&gt;Red Hat OpenShift Service on AWS (ROSA) is a fully-managed OpenShift service that runs natively on Amazon Web Services (AWS). It combines Red Hat OpenShift with the scalability and reliability of AWS infrastructure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key ROSA Benefits:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Fully Managed&lt;/strong&gt;: Red Hat handles cluster operations, updates, and maintenance&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AWS Native&lt;/strong&gt;: Deep integration with AWS services like EBS, ELB, and VPC&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enterprise Ready&lt;/strong&gt;: Built-in security, compliance, and support from Red Hat&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pay-as-you-go&lt;/strong&gt;: Flexible pricing model based on actual usage&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  APIcast Deployment Options in ROSA
&lt;/h3&gt;

&lt;p&gt;ROSA provides multiple ways to deploy 3scale APIcast, each suited for different use cases:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Operator-Based Deployment (Recommended)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Declarative Configuration: Uses Custom Resource Definitions (CRDs)&lt;/li&gt;
&lt;li&gt;Lifecycle Management: Automatic updates and scaling&lt;/li&gt;
&lt;li&gt;GitOps Ready: Perfect for CI/CD pipelines&lt;/li&gt;
&lt;li&gt;Multi-Environment Support: Easy promotion between dev/staging/prod&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. Helm Charts&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Template-Based: Traditional Kubernetes deployment method&lt;/li&gt;
&lt;li&gt;Customizable: Fine-grained control over deployment parameters&lt;/li&gt;
&lt;li&gt;Version Control: Easy rollback and version management&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3. Manual YAML Deployment&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Direct Control: Complete control over all Kubernetes resources&lt;/li&gt;
&lt;li&gt;Learning Purpose: Great for understanding underlying components&lt;/li&gt;
&lt;li&gt;Custom Scenarios: When you need specific configurations not covered by operators&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For this setup, we chose the operator-based approach because it provides the best balance of simplicity and control, especially when integrating with the existing self-hosted 3scale management layer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Replicate APIcast on ROSA?
&lt;/h2&gt;

&lt;p&gt;The decision to deploy APIcast on ROSA wasn't about replacing the self-hosted 3scale management layer — it was about creating a safe, isolated testing environment that replicates our current setup.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Goal:&lt;/strong&gt; Keep the existing self-hosted 3scale Admin Portal and System while deploying APIcast gateways on ROSA for isolating the current working system in ROSA with a safe way of testing APIs reaching the backend services.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;High Availability: Eliminate single points of failure in your API gateway layer&lt;/li&gt;
&lt;li&gt;Blue-Green Deployments: Enable zero-downtime updates by switching traffic between replicas&lt;/li&gt;
&lt;li&gt;Testing Environment: Create a production-like environment for testing API changes&lt;/li&gt;
&lt;li&gt;Disaster Recovery: Maintain backup gateways in different availability zones&lt;/li&gt;
&lt;li&gt;Performance Testing: Isolate load testing from production traffic&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Understanding the Architecture
&lt;/h2&gt;

&lt;p&gt;Before diving into the implementation, it's crucial to understand where APIcast fits in the request flow:&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%2Fcfr20qfphq87eufeuzo7.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%2Fcfr20qfphq87eufeuzo7.png" alt="Rosa Api Cast Architecture" width="797" height="618"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ROSA (Red Hat OpenShift Service on AWS) contains:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;OpenShift Route: Acts as the ingress controller, handling external traffic routing&lt;/li&gt;
&lt;li&gt;Kubernetes Service: Provides load balancing and service discovery for the APIcast pods&lt;/li&gt;
&lt;li&gt;3scale APIcast Pods: Multiple instances of the API gateway running as containers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3scale Components:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;APIcast Gateway Pods (running in ROSA): Handle the actual API traffic, rate limiting, authentication, and proxying to backend services&lt;/li&gt;
&lt;li&gt;3scale Management (self-hosted): Can be deployed separately to manage API policies, analytics, and configuration&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Traffic Flow:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Internet traffic comes through the OpenShift Route&lt;/li&gt;
&lt;li&gt;Gets load balanced by the Kubernetes Service&lt;/li&gt;
&lt;li&gt;Distributed across multiple APIcast gateway pods&lt;/li&gt;
&lt;li&gt;APIcast pods proxy requests to backend APIs&lt;/li&gt;
&lt;li&gt;3scale Management provides configuration and policies to APIcast pods&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The key distinction is that APIcast runs as containerized pods within ROSA, while the 3scale Management component can be self-hosted either within the same ROSA cluster or on separate infrastructure, depending on the deployment preferences.&lt;/p&gt;

&lt;h3&gt;
  
  
  The APIcast Gateway Role:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Authentication: Validates API keys and handles OAuth flows&lt;/li&gt;
&lt;li&gt;Rate Limiting: Enforces quotas and throttling policies&lt;/li&gt;
&lt;li&gt;Policy Enforcement: Applies transformation, caching, and security policies&lt;/li&gt;
&lt;li&gt;Analytics Collection: Gathers metrics for monitoring and billing&lt;/li&gt;
&lt;li&gt;Request Routing: Directs traffic to appropriate backend services&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This positioning makes APIcast the critical control point for all API traffic—it's not just a proxy, but an intelligent gateway that enforces the API strategy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deploying APIcast on ROSA with the Operator
&lt;/h2&gt;

&lt;p&gt;With the ROSA cluster already in place and the 3scale operator installed, we were ready to deploy APIcast instances that would integrate with the existing self-hosted 3scale management layer. &lt;/p&gt;

&lt;p&gt;This guide walks through the complete process of replicating an existing APIcast deployment connected to a self-hosted 3scale management platform.&lt;/p&gt;

&lt;h4&gt;
  
  
  Configuring APIcast Custom Resources
&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzh8riahbefyfs28l2cbn.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%2Fzh8riahbefyfs28l2cbn.png" alt="APIcast on ROSA with the Operator" width="800" height="367"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The operator uses Custom Resource Definitions (CRDs) to manage APIcast deployments:&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;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apps.3scale.net/v1alpha1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;APIcast&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apicast-development&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apicast&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;adminPortalURL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://self-hosted-3scale-admin.example.com&lt;/span&gt;
  &lt;span class="na"&gt;exposedHost&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apps.[domain of our openshift].openshiftapps.com&lt;/span&gt;
  &lt;span class="na"&gt;replicas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3&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;limits&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;cpu&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;1000m&lt;/span&gt;
      &lt;span class="na"&gt;memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;128Mi&lt;/span&gt;
    &lt;span class="na"&gt;requests&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;cpu&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;500m&lt;/span&gt;
      &lt;span class="na"&gt;memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;64Mi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Prerequisites&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;OpenShift cluster access with appropriate permissions&lt;/li&gt;
&lt;li&gt;Working self-hosted 3scale deployment&lt;/li&gt;
&lt;li&gt;Access to 3scale admin portal&lt;/li&gt;
&lt;li&gt;oc CLI tool configured&lt;/li&gt;
&lt;li&gt;curl for testing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Analyze Existing APIcast Configuration&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;First, we need to understand how the current APIcast is configured. This ensures the replica maintains the same behavior.&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;# Get the deployment configuration&lt;/span&gt;
&lt;span class="s"&gt;oc get deployment [source-apicast-name] -n [source-namespace] -o yaml &amp;gt; apicast-config.yaml&lt;/span&gt;

&lt;span class="c1"&gt;# Check environment variables&lt;/span&gt;
&lt;span class="s"&gt;oc get deployment [source-apicast-name] -n [source-namespace] \&lt;/span&gt;
  &lt;span class="s"&gt;-o jsonpath='{.spec.template.spec.containers[0].env}' | jq .&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Key Environment Variables to Note&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"THREESCALE_PORTAL_ENDPOINT"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://TOKEN@3scale-admin.apps.cluster.domain.com"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"THREESCALE_DEPLOYMENT_ENV"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"development"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 2: Create the Replica Namespace&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Organize the apicast replica in a dedicated namespace for better isolation and management.&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;# Create replica namespace&lt;/span&gt;
oc create namespace apicast-replica

&lt;span class="c"&gt;# Label for organization&lt;/span&gt;
oc label namespace apicast-replica &lt;span class="nv"&gt;purpose&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;apicast-replica
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 3: Deploy APIcast Replica&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Create Deployment Configuration (based from the apicast-config.yaml with updated metadata names)&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;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apps/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deployment&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apicast-replica&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apicast-replica&lt;/span&gt;
  &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apicast-replica&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;replicas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;matchLabels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apicast-replica&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apicast-replica&lt;/span&gt;
    &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apicast&lt;/span&gt;
        &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;quay.io/3scale/apicast:latest&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;THREESCALE_PORTAL_ENDPOINT&lt;/span&gt;
          &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://TOKEN@3scale-admin.apps.[domain&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;of&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;our&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;openshift].openshiftapps.com"&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;THREESCALE_DEPLOYMENT_ENV&lt;/span&gt;
          &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;development"&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;APICAST_CONFIGURATION_LOADER&lt;/span&gt;
          &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;boot"&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;APICAST_LOG_LEVEL&lt;/span&gt;
          &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;notice"&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;APICAST_PATH_ROUTING&lt;/span&gt;
          &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;true"&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;APICAST_RESPONSE_CODES&lt;/span&gt;
          &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;true"&lt;/span&gt;
        &lt;span class="na"&gt;livenessProbe&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;httpGet&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/status/live&lt;/span&gt;
            &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;management&lt;/span&gt;
          &lt;span class="na"&gt;initialDelaySeconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;
          &lt;span class="na"&gt;timeoutSeconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
        &lt;span class="na"&gt;readinessProbe&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;httpGet&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/status/ready&lt;/span&gt;
            &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;management&lt;/span&gt;
          &lt;span class="na"&gt;initialDelaySeconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;15&lt;/span&gt;
          &lt;span class="na"&gt;timeoutSeconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Deploy the Replica&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;# Apply the deployment&lt;/span&gt;
oc apply &lt;span class="nt"&gt;-f&lt;/span&gt; apicast-replica-deployment.yaml

&lt;span class="c"&gt;# Watch the deployment&lt;/span&gt;
oc get pods &lt;span class="nt"&gt;-n&lt;/span&gt; apicast-replica &lt;span class="nt"&gt;-w&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 4: Create Service and Route for the Apicast&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Service Configuration&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;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Service&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apicast-replica-service&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apicast-replica&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apicast-replica&lt;/span&gt;
  &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;proxy&lt;/span&gt;
    &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8080&lt;/span&gt;
    &lt;span class="na"&gt;targetPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8080&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;management&lt;/span&gt;
    &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8090&lt;/span&gt;
    &lt;span class="na"&gt;targetPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8090&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Route Configuration&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;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;route.openshift.io/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Route&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;api-replica&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apicast-replica&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;api-replica.apps.[domain of our openshift].openshiftapps.com&lt;/span&gt;
  &lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Service&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apicast-replica-service&lt;/span&gt;
  &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;targetPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;proxy&lt;/span&gt;
  &lt;span class="na"&gt;tls&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;termination&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;edge&lt;/span&gt;
    &lt;span class="na"&gt;insecureEdgeTerminationPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Redirect&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then apply configs&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;# Apply service and route&lt;/span&gt;
oc apply &lt;span class="nt"&gt;-f&lt;/span&gt; apicast-replica-service.yaml
oc apply &lt;span class="nt"&gt;-f&lt;/span&gt; apicast-replica-route.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we go to Rosa and check, it will look like this&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%2F2z9avhqe3dh3llux0uwk.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%2F2z9avhqe3dh3llux0uwk.png" alt="Rosa apicast replica deployment" width="800" height="324"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 5: Configure 3scale for the Replica&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;APIcast loads services based on the gateway URL configured in 3scale, not just the connection to 3scale.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Configuration Challenge&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When APIcast starts, it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Connects to 3scale using THREESCALE_PORTAL_ENDPOINT&lt;/li&gt;
&lt;li&gt;Downloads service configurations for THREESCALE_DEPLOYMENT_ENV (staging/production)&lt;/li&gt;
&lt;li&gt;Only loads services where the proxy endpoint matches the incoming request host&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Update Product Configuration&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In the self-hosted 3scale Admin Portal:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to Products &amp;gt; Select the product (e.g., "Test API")&lt;/li&gt;
&lt;li&gt;Go to Integration &amp;gt; Settings&lt;/li&gt;
&lt;li&gt;Update the Staging Public Base URL:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://apicast-replica.apps.[domain].openshiftapps.com
&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%2F8n6xkzoo9foxbiwbhxpi.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%2F8n6xkzoo9foxbiwbhxpi.png" alt="apicast-replica" width="800" height="617"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Critical Step: Go to Integration → Configuration&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Click "Promote v[X] to Staging"&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%2Fioiv2yf5fpugf6dfilhc.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%2Fioiv2yf5fpugf6dfilhc.png" alt="Promot to staging" width="800" height="414"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Important: The "Promote to Staging" step is what actually deploys your configuration changes to APIcast. Without this, APIcast continues using the old configuration.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Step 6: Restart and Verify&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Restart APIcast to Load New Configuration&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;#Force APIcast to reload configuration&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; oc rollout restart deployment/apicast-replica &lt;span class="nt"&gt;-n&lt;/span&gt; apicast-replica

&lt;span class="c"&gt;#Result&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; deployment.apps/apicast-replica restarted

&lt;span class="c"&gt;# Wait for pods to be ready&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; oc get pods &lt;span class="nt"&gt;-n&lt;/span&gt; apicast-replica &lt;span class="nt"&gt;-w&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Test Authentication&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="s2"&gt;"https://api-replica.../status/ready"&lt;/span&gt; &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"user-key: API_APPLICATION_KEY"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Test Actual API Endpoints&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s1"&gt;'https://api-replica.../your/api/endpoint'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
     &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'user-key: APPLICATION_API_KEY'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
     &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'Content-Type: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
     &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"test": "data"}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Replicating APIcast with self-hosted 3scale requires careful attention to service configuration alignment. The key insight is that APIcast service discovery depends on matching the configured gateway URLs in 3scale with the actual domains your replica serves.&lt;/p&gt;

&lt;p&gt;By following this guide, we've created a fully functional APIcast replica that can serve as a backup, testing environment, or part of a blue-green deployment strategy. Remember that the "Promote to Staging" step in 3scale is critical - configuration changes don't take effect until promoted and APIcast is restarted.&lt;/p&gt;

&lt;p&gt;The replica approach provides excellent flexibility for API gateway management while maintaining high availability and enabling safe deployment practices. With proper monitoring and procedures in place, you can confidently manage multiple APIcast instances serving the critical API infrastructure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Additional Resources&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://access.redhat.com/documentation/en-us/red_hat_3scale_api_management" rel="noopener noreferrer"&gt;3scale APIcast Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.openshift.com/container-platform/latest/networking/routes/route-configuration.html" rel="noopener noreferrer"&gt;OpenShift Route Configuration&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/3scale/APIcast#environment-variables" rel="noopener noreferrer"&gt;APIcast Environment Variables Reference&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;This guide was created based on real-world APIcast replica deployment experience. The configuration examples are anonymized but reflect actual working deployments.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>programming</category>
      <category>devops</category>
      <category>redhat</category>
    </item>
    <item>
      <title>Migrating to Self-Hosted 3scale API Management on ROSA Kubernetes</title>
      <dc:creator>Clariza Look</dc:creator>
      <pubDate>Fri, 16 May 2025 12:50:53 +0000</pubDate>
      <link>https://forem.com/clarizalooktech/migrating-to-self-hosted-3scale-api-management-on-rosa-a-kubernetes-journey-3ele</link>
      <guid>https://forem.com/clarizalooktech/migrating-to-self-hosted-3scale-api-management-on-rosa-a-kubernetes-journey-3ele</guid>
      <description>&lt;p&gt;I was tasked to migrate from a Red Hat-hosted 3scale portal to a self-hosted version in ROSA (Red Hat OpenShift Service on AWS). This presented quite a challenge as my knowledge in Kubernetes was mostly theoretical, based on studying for the Kubernetes and Cloud Native Associate (KCNA) certification exam. &lt;/p&gt;

&lt;p&gt;The goal was to recreate a self-hosted version of 3scale using an operator in ROSA, but what I thought would be a straightforward deployment turned into a valuable learning experience.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is Red Hat-Managed/hosted 3scale?
&lt;/h3&gt;

&lt;p&gt;When using Red Hat-hosted 3scale (also known as "SaaS" or managed 3scale), all infrastructure complexities are abstracted away. Red Hat handles the deployment, maintenance, updates, and scaling of the platform. &lt;/p&gt;

&lt;p&gt;As a user, you simply access a provided portal URL and focus on managing your APIs rather than worrying about the underlying infrastructure. Your daily tasks revolve around the actual API management activities like adding backends, configuring products, creating applications, setting up authentication, and managing rate limits. &lt;/p&gt;

&lt;p&gt;It's a convenient option that requires minimal operational overhead, allowing your team to focus on API strategy rather than platform management.&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%2Fv4yr1y2of5ovwklmvut6.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%2Fv4yr1y2of5ovwklmvut6.png" alt="How 3Scale Api Management Portal looks like" width="800" height="439"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  What is self-hosted 3scale?
&lt;/h3&gt;

&lt;p&gt;In contrast, self-hosted 3scale brings both flexibility and responsibility. You gain complete control over your deployment configuration, integration with internal systems, customization options, and data locality. &lt;/p&gt;

&lt;p&gt;Since the infrastructure runs on Kubernetes (in my case, ROSA - Red Hat OpenShift Service on AWS), you have access to all the native Kubernetes capabilities for scaling, monitoring, and management. &lt;/p&gt;

&lt;p&gt;However, this freedom comes with the need to manage the entire application lifecycle within the Kubernetes ecosystem: installation via operators or templates, configuration through custom resources, scaling via horizontal pod autoscalers, implementing backup strategies, and handling upgrades. &lt;/p&gt;

&lt;p&gt;You're responsible for ensuring high availability with proper pod distribution, performance tuning through resource allocation, and troubleshooting any issues that arise in both the 3scale application components and the underlying Kubernetes resources.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Migration
&lt;/h3&gt;

&lt;p&gt;Migrating from managed to self-hosted represented a significant shift in responsibilities, and I was about to discover just how much Red Hat had been handling behind the scenes.&lt;/p&gt;

&lt;p&gt;This blog post documents a real-world troubleshooting journey that encountered and overcame significant challenges:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Missing Routes for Admin Access&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;DNS resolution issues preventing access to Red Hat's container registry&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Architecture mismatch between my ARM-based MacBook for and the x86_64 docker container images required for deployment&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;PVC Access Mode Issues&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Resource Constraints&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Missing Service for App Components&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;By sharing this experience, I hope to help others who might encounter similar issues during their deployment process, especially those who are transitioning from theoretical Kubernetes knowledge to practical application.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Initial Deployment Attempt
&lt;/h3&gt;

&lt;p&gt;We started by creating a dedicated namespace for our 3scale deployment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;oc create namespace 3scale-backup
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After switching to this namespace (oc project 3scale-backup), we downloaded the 3scale API Management Platform template:&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;-o&lt;/span&gt; amp.yml https://raw.githubusercontent.com/3scale/3scale-amp-openshift-templates/master/amp/amp.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we tried to deploy 3scale using this template:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;oc new-app &lt;span class="nt"&gt;--file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;amp.yml &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--param&lt;/span&gt; &lt;span class="nv"&gt;WILDCARD_DOMAIN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;apps.[domain of your openshift].openshiftapps.com &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--param&lt;/span&gt; &lt;span class="nv"&gt;ADMIN_PASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;password123
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The template processing appeared successful, creating numerous resources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Imagestreams&lt;/li&gt;
&lt;li&gt;Deployment configs&lt;/li&gt;
&lt;li&gt;Services&lt;/li&gt;
&lt;li&gt;Routes&lt;/li&gt;
&lt;li&gt;Persistent volume claims&lt;/li&gt;
&lt;li&gt;Secrets
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;oc get all &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;your namespace]
&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%2Fxo9gsgpvwr0l7736vmw5.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%2Fxo9gsgpvwr0l7736vmw5.png" alt="3Scale API Management Rosa deployment" width="692" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;However, when checking the status of the pods, we noticed that many deployments were either not starting, with errors, crashLoopBackOff or stuck in initialization phases:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;oc get pods
&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%2Fubytesllxp184jbrzwin.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%2Fubytesllxp184jbrzwin.png" alt="Get Pods" width="729" height="637"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;While some components like Redis and database pods were running fine, critical components like backend-listener, backend-worker, and backend-cron were not deploying at all. &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%2Fgs024crkwsi4frq80b8u.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%2Fgs024crkwsi4frq80b8u.png" alt="backend-worker issues" width="800" height="298"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The system components were also failing during initialization.&lt;/p&gt;

&lt;h2&gt;
  
  
  Challenge 1: Missing Routes for Admin Access
&lt;/h2&gt;

&lt;p&gt;Our first challenge was that the URLs for accessing the admin portal &lt;code&gt;https://3scale-admin.apps.[YOUR-ROSA-DOMAIN].openshiftapps.com&lt;/code&gt; were showing "Application is not available".&lt;/p&gt;

&lt;p&gt;The reason was simple - the template had not created the necessary routes for our self-hosted 3scale services. We manually created them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;oc create route edge system-admin &lt;span class="nt"&gt;--service&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;system-provider &lt;span class="nt"&gt;--hostname&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;3scale-admin.apps.[YOUR-ROSA-DOMAIN].openshiftapps.com
oc create route edge system-developer &lt;span class="nt"&gt;--service&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;system-developer &lt;span class="nt"&gt;--hostname&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;3scale.apps.[YOUR-ROSA-DOMAIN].openshiftapps.com
oc create route edge system-master &lt;span class="nt"&gt;--service&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;system-master &lt;span class="nt"&gt;--hostname&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;master.apps.[YOUR-ROSA-DOMAIN].openshiftapps.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, after creating the routes, the admin portal still wasn't accessible. Digging into the logs with &lt;code&gt;oc logs system-app-1-hook-pre&lt;/code&gt;, we discovered a more fundamental issue.&lt;/p&gt;

&lt;h2&gt;
  
  
  Challenge 2: DNS Resolution Issues
&lt;/h2&gt;

&lt;p&gt;The pre-deployment hook was failing with a specific error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ThreeScale::Core::APIClient::ConnectionError: connection refused: backend-listener:80
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Further investigation revealed that the backend components weren't deployed at all. When checking the deployment configs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;oc get dc/backend-listener
NAME               REVISION   DESIRED   CURRENT   TRIGGERED BY
backend-listener   0          1         0         config,image&lt;span class="o"&gt;(&lt;/span&gt;amp-backend:2.12&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We saw that backend-listener, backend-worker, and backend-cron had REVISION 0 and CURRENT 0, indicating they hadn't been deployed.&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%2F8zauyzwdrjcrggdgidki.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%2F8zauyzwdrjcrggdgidki.png" alt="oc get dc/backend-listener" width="800" height="298"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The root cause was found in the imagestream:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;oc describe imagestream amp-backend
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This showed an error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;error: Import failed (InternalError): Internal error occurred: registry.redhat.com/3scale-amp2/backend-rhel8:3scale2.12: Get "https://registry.redhat.com/v2/": dial tcp: lookup registry.redhat.com on 100.10.0.11:23: no such host
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our OpenShift cluster couldn't resolve the hostname registry.redhat.com due to DNS issues. This was confirmed by attempting to run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nslookup registry.redhat.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which returned "No answer" from the DNS server.&lt;/p&gt;

&lt;p&gt;Or technically it does not pull the image required from the RedHat registry to the pods.&lt;/p&gt;

&lt;p&gt;So our workaround was to manually pull the image to the docker (locally), then push that image to the namespace's private RedHat registry.&lt;/p&gt;

&lt;h2&gt;
  
  
  Challenge 3: Architecture Mismatch
&lt;/h2&gt;

&lt;p&gt;While working to address the DNS issues , we discovered another challenge - we were trying to pull Red Hat's container images on an ARM64-based machine (likely an Apple Silicon Mac), but the images were only available for x86_64 architecture.&lt;/p&gt;

&lt;p&gt;When attempting to pull the images directly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker login &lt;span class="o"&gt;[&lt;/span&gt;RedHat Credentials]
docker pull registry.redhat.io/3scale-amp2/backend-rhel8:3scale2.12
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We received:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;no matching manifest for linux/arm64/v8 in the manifest list entries
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Solution Process
&lt;/h2&gt;

&lt;p&gt;We implemented a multi-step solution to overcome these challenges:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Authentication with Red Hat Registry&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;First, we logged in to the Red Hat Container Registry:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker login registry.redhat.io
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 2: Architecture-aware Image Pulling&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Because I was using macOS and having the docker desktop installed in it, the pulled image does not match with the operating system's arch. &lt;/p&gt;

&lt;p&gt;To overcome the architecture mismatch, we explicitly specified the platform when pulling:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker pull &lt;span class="nt"&gt;--platform&lt;/span&gt; linux/amd64 registry.redhat.io/3scale-amp2/backend-rhel8:3scale2.12
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This successfully pulled the image by using Rosetta 2 emulation on macOS.&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%2Fk5ra4jmtlmp6vo1ef09t.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%2Fk5ra4jmtlmp6vo1ef09t.png" alt="Redhat Docker Image Pull" width="800" height="386"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3: Exposing the OpenShift Registry&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To make our OpenShift registry accessible:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;oc patch configs.imageregistry.operator.openshift.io/cluster &lt;span class="nt"&gt;--patch&lt;/span&gt; &lt;span class="s1"&gt;'{"spec":{"defaultRoute":true}}'&lt;/span&gt; &lt;span class="nt"&gt;--type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;merge
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 4: Pushing Images to Internal Registry&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We pushed the pulled images to our OpenShift internal registry:&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;# Get credentials&lt;/span&gt;
&lt;span class="nv"&gt;TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;oc &lt;span class="nb"&gt;whoami&lt;/span&gt; &lt;span class="nt"&gt;-t&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;REGISTRY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;oc get route default-route &lt;span class="nt"&gt;-n&lt;/span&gt; openshift-image-registry &lt;span class="nt"&gt;--template&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'{{ .spec.host }}'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Login to registry&lt;/span&gt;
docker login &lt;span class="nt"&gt;-u&lt;/span&gt; kubeadmin &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="nv"&gt;$TOKEN&lt;/span&gt; &lt;span class="nv"&gt;$REGISTRY&lt;/span&gt;

&lt;span class="c"&gt;# Tag and push&lt;/span&gt;
docker tag registry.redhat.io/3scale-amp2/backend-rhel8:3scale2.12 &lt;span class="nv"&gt;$REGISTRY&lt;/span&gt;/[namespace]/amp-backend:2.12
docker push &lt;span class="nv"&gt;$REGISTRY&lt;/span&gt;/[namespace]/amp-backend:2.12
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 5: Updating ImageStreams&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We updated the imagestream to point to our locally pushed image:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;oc tag &lt;span class="nv"&gt;$REGISTRY&lt;/span&gt;/[namespace]/amp-backend:2.12 amp-backend:2.12 &lt;span class="nt"&gt;--source&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;docker
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This automatically triggered the deployment due to the &lt;code&gt;ImageChange&lt;/code&gt; trigger on the deployment config.&lt;/p&gt;

&lt;h3&gt;
  
  
  Results
&lt;/h3&gt;

&lt;p&gt;After implementing these steps for the backend-listener component, the deployment began successfully (at least for this resource!). &lt;/p&gt;

&lt;h2&gt;
  
  
  Challenge 4: PVC Access Mode Issues
&lt;/h2&gt;

&lt;p&gt;So we went to the 3Scale self-hosted Admin Portal and checked that still it's not working. &lt;/p&gt;

&lt;p&gt;We checked the pods and found out that some of them are having issues. &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%2F2l1p1sq9gpoqpwyldfse.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%2F2l1p1sq9gpoqpwyldfse.png" alt="apicast-production-1-deploy Issue" width="800" height="149"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The error logs show that several deployments are failing because their pods are taking too long to become available (timeout errors):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;apicast-production-1-deploy&lt;/code&gt;: "pods took longer than 1800 seconds to become available"&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;system-sidekiq-1-deploy&lt;/code&gt;: "pods took longer than 1200 seconds to become available"&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;system-sphinx-1-deploy&lt;/code&gt;: "pods took longer than 1200 seconds to become available"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This typically happens when pods are stuck in a pending or initializing state for too long.&lt;/p&gt;

&lt;p&gt;So we checked the logs of the problematic pods and checked the PVC as well.&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;# Check logs for apicast-production deployment&lt;/span&gt;
oc logs apicast-production-1-deploy

&lt;span class="c"&gt;# Check logs for system-sidekiq deployment&lt;/span&gt;
oc logs system-sidekiq-1-deploy

&lt;span class="c"&gt;# Check logs for system-sphinx deployment&lt;/span&gt;
oc logs system-sphinx-1-deploy

&lt;span class="c"&gt;# Check events for the pending pod&lt;/span&gt;
oc describe pod system-app-1-hook-pre
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We discovered a storage issue where the system-storage PVC was failing to provision:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;oc get pvc
NAME                    STATUS    VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
backend-redis-storage   Bound     pvc-ss987s5b-026a-4srg-au97-549d8958933a   1Gi        RWO            gp3            53m
mysql-storage           Bound     pvc-72s43210-s033-4c8w-ar53-043bf3kk1496   1Gi        RWO            gp3            53m
system-redis-storage    Bound     pvc-s3r1111d2-4d57-41dd-9066-c488eda666d4   1Gi        RWO            gp3            53m
system-storage          Pending                            
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The error was related to access modes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;failed to provision volume with StorageClass "gp3": rpc error: code = InvalidArgument desc = Volume capabilities MULTI_NODE_MULTI_WRITER not supported. Only AccessModes[ReadWriteOnce] supported.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We fixed it by creating a new PVC with the correct access mode:&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;# First, delete pods using this PVC&lt;/span&gt;
oc delete pod system-app-1-hook-pre

&lt;span class="c"&gt;# Back up the current PVC definition&lt;/span&gt;
oc get pvc system-storage &lt;span class="nt"&gt;-o&lt;/span&gt; yaml &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; system-storage-pvc.yaml

&lt;span class="c"&gt;# Delete the stuck PVC&lt;/span&gt;
oc delete pvc system-storage

&lt;span class="c"&gt;# Create a new PVC with the correct settings&lt;/span&gt;
oc create &lt;span class="nt"&gt;-f&lt;/span&gt; - &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;
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: system-storage
  namespace: [Namespace]
  labels:
    app: 3scale-api-management
    threescale_component: system
    threescale_component_element: app
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
  storageClassName: gp3
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After fixing the PVC issue, restart the deployments:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;oc rollout retry dc/system-app
oc rollout retry dc/apicast-production
oc rollout retry dc/backend-listener
oc rollout retry dc/system-sidekiq
oc rollout retry dc/system-sphinx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The PVC issue got fixed, and the system-storage PVC is now correctly bound to a volume.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;oc get pvc

NAME                    STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
backend-redis-storage   Bound     pvc-ss987s5b-026a-4srg-au97-549d8958933a   1Gi        RWO            gp3            53m
mysql-storage           Bound     pvc-72s43210-s033-4c8w-ar53-043bf3kk1496   1Gi        RWO            gp3            53m
system-redis-storage    Bound     pvc-s3r1111d2-4d57-41dd-9066-c488eda666d4   1Gi        RWO            gp3            53m
system-storage          Bound    pvc-2286196a-8885-490s-11c1-654320bd8a5a6   1Gi        RWO            gp3            116s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Challenge 5: Resource Constraints
&lt;/h2&gt;

&lt;p&gt;Even after resolving the PVC issue, pods were still stuck in Pending state due to insufficient resources:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;oc describe pod system-app-2-mr25z
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...
Warning  FailedScheduling  2m34s  default-scheduler  0/9 nodes are available: 2 Insufficient cpu, 3 node(s) had untolerated taint {node-role.kubernetes.io/infra: }, 3 node(s) had untolerated taint {node-role.kubernetes.io/master: }, 6 node(s) had volume node affinity conflict.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We reduced the resource requirements to make the pods fit on the available nodes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;oc patch dc/system-app &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s1"&gt;'{"spec":{"template":{"spec":{"containers":[{"name":"system-master","resources":{"requests":{"cpu":"25m","memory":"400Mi"}}},{"name":"system-provider","resources":{"requests":{"cpu":"25m","memory":"400Mi"}}},{"name":"system-developer","resources":{"requests":{"cpu":"25m","memory":"400Mi"}}}]}}}}'&lt;/span&gt;
oc patch dc/apicast-production &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s1"&gt;'{"spec":{"template":{"spec":{"containers":[{"name":"apicast-production","resources":{"requests":{"cpu":"25m","memory":"128Mi"}}}]}}}}'&lt;/span&gt;
oc patch dc/system-sidekiq &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s1"&gt;'{"spec":{"template":{"spec":{"containers":[{"name":"system-sidekiq","resources":{"requests":{"cpu":"25m","memory":"250Mi"}}}]}}}}'&lt;/span&gt;
oc patch dc/system-sphinx &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s1"&gt;'{"spec":{"template":{"spec":{"containers":[{"name":"system-sphinx","resources":{"requests":{"cpu":"25m","memory":"250Mi"}}}]}}}}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After applying these patches, we restarted the failed components:&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;# Retry system-sidekiq and system-sphinx deployments&lt;/span&gt;
oc rollout retry dc/system-sidekiq
oc rollout retry dc/system-sphinx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That got fixed too!!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;
➜  oc get services

NAME                 TYPE        CLUSTER-IP       EXTERNAL-IP   PORT&lt;span class="o"&gt;(&lt;/span&gt;S&lt;span class="o"&gt;)&lt;/span&gt;             AGE
apicast-production   ClusterIP   xxx.xx.xxx.xxx   &amp;lt;none&amp;gt;        8080/TCP,8090/TCP   83m
apicast-staging      ClusterIP   xxx.xx.xxx.xxx    &amp;lt;none&amp;gt;        8080/TCP,8090/TCP   83m
backend-listener     ClusterIP   xxx.xx.xxx.xxx   &amp;lt;none&amp;gt;        3000/TCP            83m
backend-redis        ClusterIP   xxx.xx.xxx.xxx     &amp;lt;none&amp;gt;        6379/TCP            83m
system-developer     ClusterIP   xxx.xx.xxx.xxx    &amp;lt;none&amp;gt;        3000/TCP            83m
system-master        ClusterIP   xxx.xx.xxx.xxx     &amp;lt;none&amp;gt;        3000/TCP            83m
system-memcache      ClusterIP   xxx.xx.xxx.xxx    &amp;lt;none&amp;gt;        11211/TCP           83m
system-mysql         ClusterIP   xxx.xx.xx.xxx     &amp;lt;none&amp;gt;        3306/TCP            83m
system-provider      ClusterIP   xxx.xx.x.xxx      &amp;lt;none&amp;gt;        3000/TCP            83m
system-redis         ClusterIP   xxx.xx.xxx.xxx   &amp;lt;none&amp;gt;        6379/TCP            83m
system-sphinx        ClusterIP   xxx.xx.xxx.xxx   &amp;lt;none&amp;gt;        9306/TCP            83m
zync                 ClusterIP   xxx.xx.xxx.xx     &amp;lt;none&amp;gt;        8080/TCP            83m
zync-database        ClusterIP   xxx.xx.xx.xxx    &amp;lt;none&amp;gt;        5432/TCP            83m
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;➜  oc get routes

NAME                         HOST/PORT                                                             PATH         SERVICES             PORT      TERMINATION     WILDCARD
backend                      backend-3scale.apps.[YOUR-DOMAIN].openshiftapps.com                               backend-listener     http      edge/Allow      None
system-admin                 3scale-admin.apps.[YOUR-DOMAIN].openshiftapps.com                                 system-app           3000      edge/Allow      None
system-developer             3scale.apps.[YOUR-DOMAIN].openshiftapps.com                          /developer   system-app           3001      edge/Allow      None
system-master                master.apps.[YOUR-DOMAIN].openshiftapps.com                                       system-app           3002      edge/Allow      None
system-provider              3scale.apps.[YOUR-DOMAIN].openshiftapps.com                                       system-app           3000      edge/Allow      None
zync-3scale-api-hhhjs        api-3scale-apicast-production.apps.[YOUR-DOMAIN].openshiftapps.com                apicast-production   gateway   edge/Redirect   None
zync-3scale-api-phh9n        api-3scale-apicast-staging.apps.[YOUR-DOMAIN].p1.openshiftapps.com                   apicast-staging      gateway   edge/Redirect   None
zync-3scale-master-nhhht     HostAlreadyClaimed                                                                 system-master        http      edge/Redirect   None
zync-3scale-provider-q9hh9   HostAlreadyClaimed                                                                 system-developer     http      edge/Redirect   None
zync-3scale-provider-shh6z   HostAlreadyClaimed                                                                 system-provider      http      edge/Redirect   None
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since the containers are starting up, it should be a matter of minutes before we  can access the admin portal. &lt;/p&gt;

&lt;p&gt;We were just pod status, and when system-app shows 3/3 ready, tried accessing the admin portal at:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://3scale-admin.apps.%5BYOUR-DOMAIN%5D.openshiftapps.com" rel="noopener noreferrer"&gt;https://3scale-admin.apps.[YOUR-DOMAIN].openshiftapps.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But then, it is still UNAVAILABLE.&lt;/p&gt;

&lt;h2&gt;
  
  
  Challenge 6: Missing Service for App Components
&lt;/h2&gt;

&lt;p&gt;Even after all pods were running, the admin portal was not accessible. The issue was that we created routes pointing to a service named "system-app" which didn't exist:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;oc get routes

NAME                         HOST/PORT                                                  PATH        SERVICES      PORT    TERMINATION  WILDCARD
system-admin                 3scale-admin.apps.[YOUR-DOMAIN].openshiftapps.com                     system-app    3000    edge/Allow   None
system-developer             3scale.apps.[YOUR-DOMAIN].openshiftapps.com              /developer   system-app    3001    edge/Allow   None
system-master                master.apps.[YOUR-DOMAIN].openshiftapps.com                           system-app    3002    edge/Allow   None
system-provider              3scale.apps.[YOUR-DOMAIN].openshiftapps.com                           system-app    3000    edge/Allow   None
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;oc describe service system-app

Error from server (NotFound): services "system-app" not found
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We fixed this by creating the missing service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bash
oc create -f - &amp;lt;&amp;lt;EOF
apiVersion: v1
kind: Service
metadata:
  name: system-app
  namespace: [NAMESPACE]
  labels:
    app: 3scale-api-management
spec:
  ports:
  - name: provider
    port: 3000
    protocol: TCP
    targetPort: 3000
  - name: developer
    port: 3001
    protocol: TCP
    targetPort: 3001
  - name: master
    port: 3002
    protocol: TCP
    targetPort: 3002
  selector:
    deploymentConfig: system-app
  type: ClusterIP
EOF
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Final Result&lt;/strong&gt;&lt;br&gt;
After working through all these challenges, we finally had a fully operational 3scale deployment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bash
oc get pods

NAME                        READY   STATUS      RESTARTS      AGE
apicast-production-4-7hh00  1/1     Running     0             2m12s
apicast-staging-1-6hh00     1/1     Running     0             83m
backend-cron-2-6955a        1/1     Running     0             23m
backend-listener-1-5hh00    1/1     Running     0             26m
backend-redis-1-mhh005      1/1     Running     0             57m
backend-worker-2-lr8gb      1/1     Running     0             23m
system-app-3-7ln8g          3/3     Running     0             85s
system-memcache-1-xddig     1/1     Running     0             80m
system-mysql-1-ee4wt        1/1     Running     0             80m
system-redis-1-45hh0        1/1     Running     0             80m
zync-1-l7ghy                1/1     Running     0             80m
zync-database-1-dt3l9       1/1     Running     0             80m
zync-que-1-wwri9            1/1     Running     2 (80m ago)   80m
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With all components running, finally, we were able to access the 3scale admin portal and begin configuring our APIs.&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%2Ftkk8ks7hpil93pao4od8.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%2Ftkk8ks7hpil93pao4od8.png" alt="3Scale Api Management On Rosa Intro" width="800" height="393"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Verify Deployment
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Check all pods are running
oc get pods

# Expected output should show all pods in Running or Completed state:
# - system-app-X-XXXXX (3/3 Running)
# - apicast-production-X-XXXXX (1/1 Running)
# - apicast-staging-X-XXXXX (1/1 Running)
# - system-sidekiq-X-XXXXX (1/1 Running)
# - system-sphinx-X-XXXXX (1/1 Running)
# - backend-* pods (1/1 Running)
# - system-mysql-X-XXXXX (1/1 Running)
# - system-redis-X-XXXXX (1/1 Running)
# - zync-* pods (1/1 Running)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Key Lessons Learned
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;DNS Resolution is Critical: Ensure your OpenShift cluster can resolve external registry hostnames before attempting deployments that rely on them.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Architecture Awareness: When working with enterprise container images on ARM-based development machines, be explicit about architecture requirements using the --platform flag.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Manual Image Mirroring: In restricted environments, manually pulling and pushing images to an internal registry is a viable workaround.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;ImageStream Mechanics: Understanding how OpenShift's ImageStreams work is essential for troubleshooting deployment issues.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Network Policies: In enterprise environments, network policies may restrict access to external registries, requiring coordination with network administrators.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Common Issues and Solutions
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Pod stuck in Pending: Usually resource constraints - reduce resource requests&lt;/li&gt;
&lt;li&gt;PVC mounting issues: Check storage class and access modes&lt;/li&gt;
&lt;li&gt;Routes not working: Ensure services exist and selectors match pods&lt;/li&gt;
&lt;li&gt;Application not available: Create missing system-app service&lt;/li&gt;
&lt;li&gt;Database connection issues: Check that system-mysql pod is running and accessible&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Final Verification Checklist
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt; All pods are in Running state (except completed deployment/hook pods)&lt;/li&gt;
&lt;li&gt; Routes are accessible and don't show "Application not available"&lt;/li&gt;
&lt;li&gt; Can log into Admin Portal successfully&lt;/li&gt;
&lt;li&gt; Can access Developer Portal&lt;/li&gt;
&lt;li&gt; Master Portal is accessible (if needed)&lt;/li&gt;
&lt;li&gt; Default passwords have been changed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once we successfully access the 3scale portals, we can begin migrating the existing 3scale components from another environment or start adding the new components in 3scale.&lt;/p&gt;

&lt;p&gt;A. Backend APIs&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;API definitions and configurations&lt;/li&gt;
&lt;li&gt;Authentication settings&lt;/li&gt;
&lt;li&gt;Rate limiting rules&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;B. Products (API Products)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Product configurations&lt;/li&gt;
&lt;li&gt;Application plans&lt;/li&gt;
&lt;li&gt;Pricing rules&lt;/li&gt;
&lt;li&gt;Methods and metrics&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;C. Applications&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Application keys and secrets&lt;/li&gt;
&lt;li&gt;Application plans assignments&lt;/li&gt;
&lt;li&gt;Usage statistics (if needed)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;D. Accounts and Users&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Developer accounts&lt;/li&gt;
&lt;li&gt;Admin users&lt;/li&gt;
&lt;li&gt;Access permissions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;E. Policies&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Custom policies&lt;/li&gt;
&lt;li&gt;Policy chains&lt;/li&gt;
&lt;li&gt;Configuration settings&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;F. Developer Portal&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Custom pages and templates&lt;/li&gt;
&lt;li&gt;Documentation&lt;/li&gt;
&lt;li&gt;CMS content&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Deploying complex solutions like 3scale API Management in restricted network environments or across architecture boundaries presents unique challenges. By understanding the underlying issues and implementing a systematic approach to manually mirror images, we were able to overcome these obstacles.&lt;/p&gt;

&lt;p&gt;While this process requires more manual effort than a standard deployment, it demonstrates the flexibility of OpenShift's container management capabilities and provides a path forward for deployments in environments with similar restrictions.&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>apimanagement</category>
      <category>devops</category>
      <category>programming</category>
    </item>
    <item>
      <title>From Infra to Platform: A Day in the Life of a DevOps Team -Building Developer Tools</title>
      <dc:creator>Clariza Look</dc:creator>
      <pubDate>Wed, 07 May 2025 05:47:13 +0000</pubDate>
      <link>https://forem.com/clarizalooktech/from-infra-to-platform-a-day-in-the-life-of-a-devops-team-building-developer-tools-n3m</link>
      <guid>https://forem.com/clarizalooktech/from-infra-to-platform-a-day-in-the-life-of-a-devops-team-building-developer-tools-n3m</guid>
      <description>&lt;p&gt;Today marked a shift in our DevOps journey — from managing infrastructure to building a platform that empowers developers to move faster and smarter.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Meeting That Sparked a Platform Vision&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Our team, traditionally focused on infrastructure management for one of our core applications, had a productive meeting with our Solutions Architect — who also leads the development team behind another application. &lt;/p&gt;

&lt;p&gt;The goal? To brainstorm how we can optimized dev operational processes that will make life easier for our internal developers through automation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Problem: Repetitive Tasks Slowing Down Developers&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Developers often perform repetitive infrastructure-related tasks like database refreshes. These actions, while simple, are time-consuming and error-prone when done manually. The idea was simple but powerful: a one-click web interface (an api) that allows developers to trigger these tasks securely and reliably.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Vision: A Self-Service Platform Tool&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We envisioned a Single Page Application (SPA) where users authenticate via Azure AD, then access buttons linked to backend APIs that trigger specific tasks. Think of it as a developer control panel — a self-service tool that abstracts the complexity of infrastructure operations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Architecture Talk: From Frontend to Infra&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The conversation quickly shifted toward how we’d architect and build this platform. Some of the key topics we discussed included:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Authentication: Azure Active Directory (OAuth2) for SSO and secure access.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Frontend: Possibly React, due to its community support and simplicity with SPAs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Backend: Python or TypeScript for the API layer — both have pros and cons, and we’re weighing based on team familiarity and ecosystem.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Infrastructure as Code (IaC): CDK vs. SST — should we go full AWS-native with CDK, or use SST for a more streamlined developer experience?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;CI/CD Pipeline: How to build and deploy the platform efficiently — integrating testing, linting, and deployment workflows.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Repository Strategy: Monorepo vs. multi-repo — placing frontend, backend, and infra code all in one place for cohesion or splitting them for separation of concerns.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Testing: Not just the API, but also integration with cloud components and edge cases that could arise in automation.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;DevOps Wearing a Dev Hat&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;As infrastructure folks, it felt a bit like we were crossing into developer territory — but in reality, we’re just evolving. This isn’t traditional app development; it's platform engineering — building tools and experiences for developers to accelerate their workflow, all while maintaining security and reliability.&lt;/p&gt;

&lt;p&gt;What’s Next&lt;/p&gt;

&lt;p&gt;We’re still in the planning phase, but the momentum is real. This platform could become a cornerstone of how our organization builds and delivers software. It's DevOps at its finest: merging development practices with operational expertise to create something truly impactful.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>infrastructureascode</category>
      <category>devops</category>
      <category>developerjournal</category>
    </item>
    <item>
      <title>Systems Programming and Overstimulation: Finding Balance in a Complex Technical Industry</title>
      <dc:creator>Clariza Look</dc:creator>
      <pubDate>Fri, 11 Apr 2025 03:18:08 +0000</pubDate>
      <link>https://forem.com/clarizalooktech/systems-programming-and-overstimulation-finding-balance-in-a-complex-technical-industry-377h</link>
      <guid>https://forem.com/clarizalooktech/systems-programming-and-overstimulation-finding-balance-in-a-complex-technical-industry-377h</guid>
      <description>&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%2Fh1zompzco1igyei420wu.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%2Fh1zompzco1igyei420wu.png" alt="Systems Programming and Overstimulation" width="800" height="1200"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the demanding world of systems programming and DevOps, where we work with infrastructure, deployment pipelines, and complex system configurations, cognitive overload isn't just an occasional annoyance—it's an occupational hazard. &lt;/p&gt;

&lt;p&gt;As a mid-level DevOps engineer who transitioned from a marketing career, I've experienced a significant shift in how my brain handles daily work and mental stimulation.&lt;/p&gt;

&lt;h2&gt;
  
  
  From Marketing Multitasking to Technical Deep Focus
&lt;/h2&gt;

&lt;p&gt;My career transition from marketing specialist to DevOps engineer brought an unexpected challenge: a complete rewiring of how I manage cognitive load and attention.&lt;/p&gt;

&lt;p&gt;In marketing, my days were filled with varied, shorter tasks—crafting content for ad messaging, targeting, and bidding strategies for Google ads, analyzing campaign metrics, coordinating with design and dev teams, and jumping between multiple client projects. This diversity of work actually suited a certain type of attention management where context-switching was frequent but the depth of focus required for each task was relatively manageable.&lt;/p&gt;

&lt;p&gt;DevOps engineering creates an entirely different cognitive environment with a perfect storm of factors that lead to overstimulation:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Extreme Technical Depth
&lt;/h3&gt;

&lt;p&gt;When debugging why a newly deployed application returns a 404 error or troubleshooting a Lambda function that can't connect to a database, we're not just dealing with a single layer of abstraction. We're simultaneously juggling:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Network configuration and security groups&lt;/li&gt;
&lt;li&gt;Container settings or serverless execution contexts&lt;/li&gt;
&lt;li&gt;IAM permissions and access policies&lt;/li&gt;
&lt;li&gt;Application configurations across environments&lt;/li&gt;
&lt;li&gt;CI/CD pipeline variables and deployment logs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each of these domains demands deep technical understanding, and we're constantly switching between them as we solve problems. This mental context-switching exacts a cognitive toll that accumulates throughout the workday.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. High-Stakes Decision Making
&lt;/h3&gt;

&lt;p&gt;In systems programming, the consequences of errors can be severe. A subtle if else condition in the infrastructure setup might cause rare data path not being found. An inefficient security patterns could create security vulnerabilities and risk affecting credentials gets exposed to the web. This creates an environment where:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Every decision feels consequential&lt;/li&gt;
&lt;li&gt;The margin for error is slim&lt;/li&gt;
&lt;li&gt;The pressure to "get it right" is constant&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This pressure amplifies the mental load and can lead to decision fatigue—a state where our ability to make good technical choices diminishes as the day progresses.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Attention Fragmentation
&lt;/h3&gt;

&lt;p&gt;Modern development environments don't help matters. A typical systems programmer might simultaneously manage:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Multiple terminal sessions&lt;/li&gt;
&lt;li&gt;Several IDE windows&lt;/li&gt;
&lt;li&gt;Documentation browsers&lt;/li&gt;
&lt;li&gt;Communication tools like Slack or Discord&lt;/li&gt;
&lt;li&gt;Monitoring dashboards&lt;/li&gt;
&lt;li&gt;CI/CD pipelines&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each of these information sources competes for our attention, creating a constant state of partial focus that leaves us mentally drained.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Debugging Intensity: A Different Kind of Focus
&lt;/h2&gt;

&lt;p&gt;The most jarring transition has been debugging complex systems issues. In marketing, I could often multitask effectively—perhaps answering emails while sitting in on a conference call, or editing copy while monitoring social media responses.&lt;/p&gt;

&lt;p&gt;With DevOps and systems work, debugging demands a completely different cognitive mode. When troubleshooting a failed deployment pipeline or tracking down an intermittent network issue, there's no multitasking. There's only the problem and a deep, sustained focus that can last for hours. This intense, single-track concentration is mentally taxing in ways my previous career never prepared me for.&lt;/p&gt;

&lt;p&gt;Where marketing work often provided frequent small dopamine hits from completing numerous smaller tasks throughout the day, debugging provides no satisfaction until the entire problem is solved—which might take hours or even days of concentrated effort.&lt;/p&gt;

&lt;h2&gt;
  
  
  Recognizing the Signs of Overstimulation
&lt;/h2&gt;

&lt;p&gt;Since transitioning to technical field, I've learned to recognize when my brain is approaching its processing limits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Debugging Plateau:&lt;/strong&gt; When I've been staring at a complex certificate not found issue for hours and my analytical ability starts to degrade&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Implementation Paralysis:&lt;/strong&gt; When faced with multiple valid approaches to a systems design problem, I suddenly can't evaluate the tradeoffs clearly&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Documentation Blindness:&lt;/strong&gt; Reading the same paragraph of technical documentation repeatedly and not understanding it&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Environmental Sensitivity:&lt;/strong&gt; Becoming unusually irritated by background conversations, office noise, or even the sound of keyboard clicks&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Technical Tunnel Vision:&lt;/strong&gt; Fixating on one approach to a problem while missing obvious alternatives&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are all signs that my cognitive systems are overloaded and need a reset.&lt;/p&gt;

&lt;h2&gt;
  
  
  Strategies That Actually Work
&lt;/h2&gt;

&lt;p&gt;Through trial and error in my journey from marketing specialist to mid-level DevOps engineer, I've developed several techniques to manage this new type of overstimulation without sacrificing productivity:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Intentional Attention Management
&lt;/h3&gt;

&lt;p&gt;Rather than attempting to multitask constantly (which research consistently shows is ineffective), I block my day into distinct modes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Deep Work Blocks:&lt;/strong&gt; 90-120 minute sessions of uninterrupted focus on a single complex problem, with all notifications disabled&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Shallow Work Periods:&lt;/strong&gt; Time allocated for code reviews, emails, and other less cognitively demanding tasks&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Collaboration Windows:&lt;/strong&gt; Scheduled times for meetings and pair programming, concentrated into specific parts of the day&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This approach allows my brain to settle into appropriate levels of focus for each type of work rather than constantly shifting gears.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Environmental Design
&lt;/h3&gt;

&lt;p&gt;I've carefully crafted my work environment to minimize unnecessary stimulation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Noise-canceling headphones playing ambient sound or instrumental music&lt;/li&gt;
&lt;li&gt;A (slightly) clean desk policy with minimal visual distractions&lt;/li&gt;
&lt;li&gt;A dedicated secondary monitor solely for documentation&lt;/li&gt;
&lt;li&gt;Strategic use of natural lighting&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Cognitive Offloading
&lt;/h3&gt;

&lt;p&gt;Instead of keeping complex system details in working memory, I aggressively externalize information:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Detailed diagrams of system components and interactions&lt;/li&gt;
&lt;li&gt;Personal documentation of debugging processes&lt;/li&gt;
&lt;li&gt;Checklists for recurring complex procedures&lt;/li&gt;
&lt;li&gt;Digital note-taking systems that capture contextual details about problems&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This practice preserves mental bandwidth for the creative problem-solving aspects of systems programming.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Physiological Reset Techniques
&lt;/h3&gt;

&lt;p&gt;When I notice signs of overstimulation, I use specific physical interventions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Brief outdoor walks without devices&lt;/strong&gt;&lt;br&gt;
Even 5–10 minutes outside, unplugged from screens, helps ground me and clear mental clutter.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Modified 4-7-8 breathing exercises&lt;/strong&gt;&lt;br&gt;
Deep, paced breathing reduces cortisol levels and helps shift me into a calmer state.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Quick bursts of physical activity&lt;/strong&gt;&lt;br&gt;
A few pushups, dumbbell exercises, jumping jacks, or even spontaneous mid-day karaoke can rewire my focus by engaging different neural pathways.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Hydration + protein-based snacks&lt;/strong&gt;&lt;br&gt;
Staying hydrated and having a quick protein snack keeps my energy stable and wards off the fatigue that contributes to overwhelm.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Low-carb, high-fiber lunches&lt;/strong&gt;&lt;br&gt;
This dietary choice helps minimize sugar spikes and the post-lunch energy crash that can derail my productivity.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Talking to people&lt;/strong&gt;&lt;br&gt;
Sometimes just a short conversation—especially with someone positive or empathetic—helps reset my nervous system and reminds me I'm not alone.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These simple biological interventions have surprising effects on cognitive resilience.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Technical Context Management
&lt;/h3&gt;

&lt;p&gt;To reduce the mental overhead of switching between complex systems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I maintain detailed "context documents" for each major project&lt;/li&gt;
&lt;li&gt;I use consistent development environments across projects&lt;/li&gt;
&lt;li&gt;I create automation scripts for repetitive development tasks&lt;/li&gt;
&lt;li&gt;I implement code organization patterns&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These practices reduce the "reorientation tax" paid when moving between different technical domains.&lt;/p&gt;

&lt;h2&gt;
  
  
  Embracing the Career Transition
&lt;/h2&gt;

&lt;p&gt;The transition from marketing's varied, multitasking-friendly environment to the deep technical focus required in DevOps represents more than just learning new skills—it's required rewiring how I approach work at a fundamental level.&lt;/p&gt;

&lt;p&gt;Some days I still find myself instinctively reaching to check email or Slack during a debugging session, a habit that served me well in marketing but now only fragments my focus and extends the time needed to solve complex technical problems.&lt;/p&gt;

&lt;p&gt;I've had to accept that my previous ability to juggle multiple streams of attention—once a point of pride in my marketing career—is actually counterproductive in my new role. The mental muscles I'm developing now are different: sustained concentration, methodical problem decomposition, and the patience to trace complex system interactions without taking shortcuts.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Bigger Picture: Sustainable Technical Expertise
&lt;/h2&gt;

&lt;p&gt;As technical professionals, we're knowledge workers whose primary tool is our cognitive capacity. Protecting this resource isn't just about short-term productivity—it's about career longevity.&lt;/p&gt;

&lt;p&gt;The reality is that burnout and chronic overstimulation can permanently degrade our ability to solve complex problems. I've watched colleagues leave the field entirely after years of ignoring their cognitive limits.&lt;/p&gt;

&lt;p&gt;Learning to work with—rather than against—our brain's attentional systems is perhaps the most important meta-skill we can develop in technical roles. The technologies we work with will change, but the fundamental challenge of managing cognitive load will remain constant.&lt;/p&gt;

&lt;p&gt;By developing awareness of our mental states and implementing deliberate practices to manage overstimulation, we can sustainably tackle complex systems challenges for decades rather than burning bright and fading fast—even when coming from non-traditional backgrounds like marketing.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;What strategies do you use to manage cognitive load in your technical work? Share your experiences in the comments below.&lt;/em&gt;&lt;/p&gt;

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