<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Development Journal]]></title><description><![CDATA[I generally write about stuff I feel interesting or projects I have worked on]]></description><link>https://dev-journal-sagnik.hashnode.dev</link><generator>RSS for Node</generator><lastBuildDate>Mon, 14 Sep 2026 13:49:09 GMT</lastBuildDate><atom:link href="https://dev-journal-sagnik.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[How I Completed the Cloud Resume Challenge: A Personal Journey]]></title><description><![CDATA[Last month, while browsing the web, I came across Forrest Brazeal's "Cloud Resume Challenge," which I found both interesting and timely. I was beginning to explore my interest in cloud services and various DevOps tools like Docker, Kubernetes, and CI...]]></description><link>https://dev-journal-sagnik.hashnode.dev/how-i-completed-the-cloud-resume-challenge-a-personal-journey</link><guid isPermaLink="true">https://dev-journal-sagnik.hashnode.dev/how-i-completed-the-cloud-resume-challenge-a-personal-journey</guid><category><![CDATA[Cloud]]></category><category><![CDATA[aws lambda]]></category><category><![CDATA[cloud-resume-challenge]]></category><category><![CDATA[AWS Cloud Resume Challenge]]></category><category><![CDATA[Terraform]]></category><category><![CDATA[projects]]></category><category><![CDATA[CI/CD]]></category><dc:creator><![CDATA[Sagnik Pal]]></dc:creator><pubDate>Thu, 10 Oct 2024 17:41:18 GMT</pubDate><content:encoded><![CDATA[<p>Last month, while browsing the web, I came across Forrest Brazeal's "Cloud Resume Challenge," which I found both interesting and timely. I was beginning to explore my interest in cloud services and various DevOps tools like Docker, Kubernetes, and CI/CD Pipelines. So, I decided that in the coming week, I would try to complete this challenge since I had a basic understanding of these services.</p>
<h2 id="heading-infrastructure-and-automation">Infrastructure And Automation:</h2>
<p>The first part which I wanted to tackle was the entire infrastructure of the challenge and then fill in the parts required for display. To add my own twist to this challenge, I planned on incorporating the principle of <strong>Infrastructure As Code (IaC)</strong> using <strong>Terraform</strong>.</p>
<p>I chose Terraform over AWS SAM or other options because it's an industry standard and I personally prefer it. At first, I set up the entire infrastructure using a "ClickOps" approach, manually selecting and configuring all the necessary services.</p>
<p>The stuff I struggled the most were the</p>
<ul>
<li><p>Resolving my subdomain to the CloudFront distribution using Route 53</p>
</li>
<li><p>Origin Access Control Policies of CloudFront</p>
</li>
</ul>
<p>these problems were pretty trivial after I actually solved them. But they emphasised the fact that issues can be solved faster after reducing the possible cause set one by one. After this I created the <strong>API-Lambda-DynamoDB</strong> <strong>Pipeline</strong> which was pretty easy in my opinion. Now that my entire architecture was created, I moved forward with automation.</p>
<p><strong>Automating</strong> with <strong>Terraform</strong>, was the difficult and creative part, this led to me spending hours with the <strong>Terraform Registry</strong>, going through examples and what not. But, the reward was pretty satisfying after my entire infrastructure could be spun up using just two commands. Navigating through this part taught me the importance of reading documentations and examples.</p>
<p>Some tips I want to share is,</p>
<ul>
<li><p>Do not try to automate the Route53 and Certificate Creation.</p>
</li>
<li><p>For now do that manually and implement the other parts as code.</p>
</li>
<li><p>Write a <code>config.tf</code> using the following format below and you should be good to go with your code.</p>
</li>
</ul>
<pre><code class="lang-plaintext">terraform {
required_providers {
  aws = {
    source  = "hashicorp/aws"
    version = "~&gt; 5.0"
  }
}
}

provider "aws" {
  region = "us-east-1"
}

locals {
  s3_bucket_name      = "some unique name"
  domain              = "domain you bought"
  hosted_zone_id      = "ID of the Route53 hosted zone"
  cert_arn            = "arn:aws:acm:XXXXXXXXXXXXXXXX"
  cloudfront_oac_name = "some name"
}
</code></pre>
<h2 id="heading-frontend-and-cicd-pipeline">Frontend and CI/CD Pipeline:</h2>
<p>The reason I wanted to address the frontend and CI/CD pipeline together is that I didn't want to manually update my files in the S3 bucket after each iteration. Implementing a CI/CD pipeline seemed like the perfect solution for this.</p>
<p>For frontend I chose a template from the GitHub of <strong>Bartosz Jarocki,</strong> I truly am grateful for that. As it is a nightmare for me to figure out my own designs. I can work better and faster with templates. The template was in <strong>NEXT.js</strong> and after studying it for a while I implemented a pretty great navbar and also added some extra content.</p>
<p>For the visitor count, you can keep a dummy url for now and update it with the pubic url of the API Gateway after <code>terraform apply</code>.</p>
<pre><code class="lang-yaml"><span class="hljs-attr">name:</span> <span class="hljs-string">Build</span> <span class="hljs-string">website</span> <span class="hljs-string">and</span> <span class="hljs-string">upload</span> <span class="hljs-string">to</span> <span class="hljs-string">s3</span>

<span class="hljs-attr">on:</span>
  <span class="hljs-attr">push:</span>
    <span class="hljs-attr">branches:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-string">main</span>

<span class="hljs-attr">jobs:</span>
  <span class="hljs-attr">build-website-and-upload-to-s3:</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>
    <span class="hljs-attr">steps:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">checkout</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v3</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/setup-node@v3</span>
        <span class="hljs-attr">with:</span>
          <span class="hljs-attr">node-version:</span> <span class="hljs-string">"22.x"</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Install</span> <span class="hljs-string">yarn</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">npm</span> <span class="hljs-string">install</span> <span class="hljs-string">-g</span> <span class="hljs-string">yarn</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Install</span> <span class="hljs-string">dependencies</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">cd</span> <span class="hljs-string">./frontend/</span> <span class="hljs-string">&amp;&amp;</span> <span class="hljs-string">yarn</span> <span class="hljs-string">install</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Build</span> <span class="hljs-string">the</span> <span class="hljs-string">out</span> <span class="hljs-string">file</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">cd</span> <span class="hljs-string">./frontend/</span> <span class="hljs-string">&amp;&amp;</span> <span class="hljs-string">npm</span> <span class="hljs-string">run</span> <span class="hljs-string">build</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Syncing</span> <span class="hljs-string">to</span> <span class="hljs-string">s3</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">jakejarvis/s3-sync-action@master</span>
        <span class="hljs-attr">with:</span>
          <span class="hljs-attr">args:</span> <span class="hljs-string">--follow-symlinks</span> <span class="hljs-string">--delete</span> <span class="hljs-string">--exclude</span> <span class="hljs-string">'.git/*'</span> <span class="hljs-string">--size-only</span>
        <span class="hljs-attr">env:</span>
          <span class="hljs-attr">AWS_S3_BUCKET:</span> <span class="hljs-string">${{</span> <span class="hljs-string">secrets.AWS_S3_BUCKET</span> <span class="hljs-string">}}</span>
          <span class="hljs-attr">AWS_ACCESS_KEY_ID:</span> <span class="hljs-string">${{</span> <span class="hljs-string">secrets.AWS_ACCESS_KEY_ID</span> <span class="hljs-string">}}</span>
          <span class="hljs-attr">AWS_SECRET_ACCESS_KEY:</span> <span class="hljs-string">${{</span> <span class="hljs-string">secrets.AWS_SECRET_ACCESS_KEY</span> <span class="hljs-string">}}</span>
          <span class="hljs-attr">AWS_REGION:</span> <span class="hljs-string">'us-east-1'</span>  
          <span class="hljs-attr">SOURCE_DIR:</span> <span class="hljs-string">'frontend/out'</span>

  <span class="hljs-attr">invalidate-cloudfront-cache:</span>
    <span class="hljs-attr">needs:</span> <span class="hljs-string">build-website-and-upload-to-s3</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>
    <span class="hljs-attr">steps:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Invalidate</span> <span class="hljs-string">CloudFront</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">chetan/invalidate-cloudfront-action@v2</span>
        <span class="hljs-attr">env:</span>
          <span class="hljs-attr">DISTRIBUTION:</span> <span class="hljs-string">${{</span> <span class="hljs-string">secrets.CLOUDFRONT_DISTRIBUTION</span> <span class="hljs-string">}}</span>
          <span class="hljs-attr">PATHS:</span> <span class="hljs-string">"/*"</span>
          <span class="hljs-attr">AWS_REGION:</span> <span class="hljs-string">"us-east-1"</span>
          <span class="hljs-attr">AWS_ACCESS_KEY_ID:</span> <span class="hljs-string">${{</span> <span class="hljs-string">secrets.AWS_ACCESS_KEY_ID</span> <span class="hljs-string">}}</span>
          <span class="hljs-attr">AWS_SECRET_ACCESS_KEY:</span> <span class="hljs-string">${{</span> <span class="hljs-string">secrets.AWS_SECRET_ACCESS_KEY</span> <span class="hljs-string">}}</span>
</code></pre>
<p>This is the CI/CD pipeline I made for github actions and the true beauty of this pipeline is that it works with any kind of <strong>NEXT.js</strong> project which would want some cloud infrastructure implementation.</p>
<h2 id="heading-backend">Backend:</h2>
<p>For the backend, it was a pretty straightforward <strong>API Gateway-Lambda-DynamoDB</strong> implementation. The code for the Lambda function is</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> json
<span class="hljs-keyword">import</span> boto3
<span class="hljs-keyword">from</span> decimal <span class="hljs-keyword">import</span> Decimal

client = boto3.client(<span class="hljs-string">'dynamodb'</span>)
dynamodb = boto3.resource(<span class="hljs-string">"dynamodb"</span>)
table = dynamodb.Table(<span class="hljs-string">'resumeVisitorTable'</span>)
tableName = <span class="hljs-string">'resumeVisitorTable'</span>

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">lambda_handler</span>(<span class="hljs-params">event, context</span>):</span>
    <span class="hljs-keyword">try</span>:
        statusCode = <span class="hljs-number">200</span>
        response = table.update_item(
            Key={
                <span class="hljs-string">"id"</span>: <span class="hljs-string">"visitors-count"</span>},
            UpdateExpression=<span class="hljs-string">"SET visitors = visitors + :val"</span>,
            ExpressionAttributeValues={<span class="hljs-string">':val'</span>: <span class="hljs-number">1</span>},
            ReturnValues=<span class="hljs-string">"UPDATED_NEW"</span>
        )
        body = json.dumps({<span class="hljs-string">"count"</span>: int(response[<span class="hljs-string">'Attributes'</span>][<span class="hljs-string">'visitors'</span>])})

    <span class="hljs-keyword">except</span> Exception <span class="hljs-keyword">as</span> e:
        statusCode = <span class="hljs-number">400</span>
        body = json.dumps({<span class="hljs-string">"error"</span>: str(e)})

    apiRes = {
        <span class="hljs-string">"statusCode"</span>: statusCode,
        <span class="hljs-string">'headers'</span>: {
            <span class="hljs-string">'Content-Type'</span>: <span class="hljs-string">'application/json'</span>,
            <span class="hljs-string">'Access-Control-Allow-Methods'</span>: <span class="hljs-string">'OPTIONS,POST,GET'</span>
        },
        <span class="hljs-string">"body"</span>: body
    }

    <span class="hljs-keyword">return</span> apiRes
</code></pre>
<p>There are certain resources which can help with this part</p>
<p><strong>Resources:</strong></p>
<ul>
<li><p><a target="_blank" href="https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-dynamo-db.html">API Gateway Manual</a></p>
</li>
<li><p><a target="_blank" href="https://medium.com/onfido-tech/aws-api-gateway-with-terraform-7a2bebe8b68f">API Gateway Terraform</a></p>
</li>
</ul>
<h2 id="heading-conclusion">Conclusion:</h2>
<p>This was truly a fun challenge to work with, I am also working on the securing supply chain mod of this challenge. So there would also be future blogs regarding that.</p>
<p>Finally you can checkout my links below</p>
<ul>
<li><p><a target="_blank" href="https://github.com/palSagnik/crc-aws">Github (CRC-AWS)</a></p>
</li>
<li><p><a target="_blank" href="https://cv.sagnik-pal.tech/">Resume</a></p>
</li>
<li><p><a target="_blank" href="https://www.linkedin.com/in/sagnik-pal-iitism/">LinkedIN</a></p>
</li>
</ul>
]]></content:encoded></item></channel></rss>