<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Prithvi Jethwa</title>
    <description>The latest articles on DEV Community by Prithvi Jethwa (@prithvijj).</description>
    <link>https://dev.to/prithvijj</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%2F1338847%2Fb6343b1c-d43c-4249-8ff9-c0a013585821.png</url>
      <title>DEV Community: Prithvi Jethwa</title>
      <link>https://dev.to/prithvijj</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/prithvijj"/>
    <language>en</language>
    <item>
      <title>CloudFormation Template does count whitespaces in Template Size...</title>
      <dc:creator>Prithvi Jethwa</dc:creator>
      <pubDate>Sat, 21 Feb 2026 14:05:08 +0000</pubDate>
      <link>https://dev.to/prithvijj/cloudformation-template-does-count-whitespaces-in-template-size-5hlg</link>
      <guid>https://dev.to/prithvijj/cloudformation-template-does-count-whitespaces-in-template-size-5hlg</guid>
      <description>&lt;h2&gt;
  
  
  Context
&lt;/h2&gt;

&lt;p&gt;We hit the limit for AWS CloudFormation &lt;a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cloudformation-limits.html" rel="noopener noreferrer"&gt;Template Size&lt;/a&gt; of &lt;code&gt;1 MB&lt;/code&gt; which caused our deployments into &lt;code&gt;alpha&lt;/code&gt;, &lt;code&gt;staging&lt;/code&gt;, and &lt;code&gt;production&lt;/code&gt; to fail. Ideally the suggestion is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use Nested Stacks&lt;/li&gt;
&lt;li&gt;Create more CloudFormation Templates&lt;/li&gt;
&lt;li&gt;Remove unnecessary resources, or consider consolidating IAM Roles and Policies&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We were going to follow our next steps to move the monitoring resources (example: AWS CloudWatch Alarms) into the Nested Stack, but doing a quick analysis using &lt;code&gt;Claude&lt;/code&gt; led me to an interesting yet questionable find.&lt;/p&gt;

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

&lt;p&gt;I found that &lt;code&gt;50%&lt;/code&gt; of the CloudFormation Template Size was just whitespaces, which seems innocent but considering the &lt;code&gt;1 MB&lt;/code&gt; limit, that's a lot.&lt;/p&gt;

&lt;p&gt;To experiment, I ended up running &lt;code&gt;jq&lt;/code&gt; to remove all the whitespaces using&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;jq -c '.' cloudformation-stack-update.json &amp;gt; minified.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Within the &lt;a href="https://aws.amazon.com/infrastructure-composer/" rel="noopener noreferrer"&gt;Infrastructure Composer&lt;/a&gt;, I copied both the CloudFormation Templates, and clicked on the &lt;code&gt;Validate&lt;/code&gt; Template button. The Regular Template showed that the file has exceeded the &lt;code&gt;1 MB&lt;/code&gt; limit, meanwhile the minified CloudFormation Template successfully validated.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next Steps
&lt;/h2&gt;

&lt;p&gt;The Platform Team has a Deployment Pipeline that handles all the packaging of CloudFormation Templates using the &lt;code&gt;Serverless Framework&lt;/code&gt;, so I ended up making a Feature Request about the very issue about minifying things.&lt;/p&gt;

&lt;p&gt;While they would get to it eventually based on priorities, I had an inner rush to resolve it. I used to be a part of that Platform Team, so it was conveniently easy to get back and contribute to those Deployment Pipelines.&lt;/p&gt;

&lt;p&gt;Opened a PR into the Repository, added in my testing notes and got it reviewed and merged. After retrying our service related builds, we saw a &lt;code&gt;50%&lt;/code&gt; cut on the CloudFormation Template Size, helping us unblock our deployments! 🎉&lt;/p&gt;

&lt;h2&gt;
  
  
  Why is this important
&lt;/h2&gt;

&lt;p&gt;It is an interesting gotcha, cause you would not expect whitespaces to eat into the CloudFormation Template Size, but I ran into this issue, and I am hoping others do not run into it. So consider minifying those templates. &lt;/p&gt;

&lt;p&gt;While I haven't seen an option in the &lt;code&gt;Serverless Framework&lt;/code&gt;, adding a &lt;code&gt;provider.minify&lt;/code&gt; would be cool to have.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reference
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cloudformation-limits.html" rel="noopener noreferrer"&gt;https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cloudformation-limits.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>aws</category>
      <category>cloudformation</category>
      <category>jq</category>
      <category>serverlessframework</category>
    </item>
    <item>
      <title>First contribution to Open Source - charmbracelet/huh</title>
      <dc:creator>Prithvi Jethwa</dc:creator>
      <pubDate>Fri, 18 Oct 2024 01:14:35 +0000</pubDate>
      <link>https://dev.to/prithvijj/first-contribution-to-open-source-charmbracelethuh-3ifc</link>
      <guid>https://dev.to/prithvijj/first-contribution-to-open-source-charmbracelethuh-3ifc</guid>
      <description>&lt;h2&gt;
  
  
  Context
&lt;/h2&gt;

&lt;p&gt;I always wanted to contribute to an Open Source Project, but I could never find out a project where I could drop in and understand the issues posted.&lt;br&gt;
I did have a hard requirement for the project to be in &lt;code&gt;Go&lt;/code&gt;, and then came along this very cool project called &lt;a href="https://charm.sh/" rel="noopener noreferrer"&gt;Charm&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The project provides tools and libraries to build out Terminal User Interfaces (TUIs) using &lt;code&gt;Go&lt;/code&gt;. It helps to create terminal applications that improve Developer productivity which I highly appreciate.&lt;/p&gt;

&lt;h2&gt;
  
  
  How did I start
&lt;/h2&gt;

&lt;p&gt;I joined their Discord channel a while back, and mostly lurked around seeing all the TUIs built by other users, and how contributions were being discussed. It led me to try out the examples provided within the repositories and reading the source code for it. After feeling slightly comfortable for it, I took a look into the GitHub Issues created and found this particular one &lt;a href="https://github.com/charmbracelet/huh/issues/367" rel="noopener noreferrer"&gt;https://github.com/charmbracelet/huh/issues/367&lt;/a&gt; .&lt;/p&gt;

&lt;p&gt;First steps involved reading through the issue, and attempting to write minimal code to reproduce the issue. I started looking at the related functions to figure out how and why the output is being rendered.&lt;br&gt;
After trial and error, created the necessary changes to help fix the issue, and requested a PR Review! &lt;/p&gt;

&lt;p&gt;It got merged in &lt;a href="https://github.com/charmbracelet/huh/pull/427" rel="noopener noreferrer"&gt;PR-427&lt;/a&gt; (technically &lt;a href="https://github.com/charmbracelet/huh/pull/425" rel="noopener noreferrer"&gt;PR-425&lt;/a&gt;), and I'm very glad that it did! 🎉&lt;/p&gt;

&lt;h2&gt;
  
  
  Why it's important to me
&lt;/h2&gt;

&lt;p&gt;I'm finally in a position that I'm able to contribute back to Open Source, that hopefully benefits 1 developer atleast. Having the Source Code available helps me learn how and why the changes were made. It provides an opportunity for making a mental model of the given system, which is a good challenge. Finally it's in &lt;code&gt;Go&lt;/code&gt; and I enjoy writing in &lt;code&gt;Go&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I'm grateful that the my first Open Source Contribution is towards &lt;a href="https://charm.sh/" rel="noopener noreferrer"&gt;Charm&lt;/a&gt; and looking forward to helping out more!&lt;/p&gt;

&lt;h2&gt;
  
  
  Next Steps
&lt;/h2&gt;

&lt;p&gt;I'll continue watching for GitHub Issues across the multiple tools that they provide, and consider trying to solve those challenges mentioned.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://charm.sh/" rel="noopener noreferrer"&gt;https://charm.sh/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/charmbracelet/huh/issues/367" rel="noopener noreferrer"&gt;https://github.com/charmbracelet/huh/issues/367&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/charmbracelet/huh/pull/425" rel="noopener noreferrer"&gt;https://github.com/charmbracelet/huh/pull/425&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/charmbracelet/huh/pull/427" rel="noopener noreferrer"&gt;https://github.com/charmbracelet/huh/pull/427&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>go</category>
      <category>charmbracelet</category>
      <category>huh</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Parse UserParameters sent from AWS CodePipeline to AWS Lambda in Go</title>
      <dc:creator>Prithvi Jethwa</dc:creator>
      <pubDate>Wed, 02 Oct 2024 17:59:54 +0000</pubDate>
      <link>https://dev.to/prithvijj/parse-userparameters-sent-from-aws-codepipeline-to-aws-lambda-in-go-ffe</link>
      <guid>https://dev.to/prithvijj/parse-userparameters-sent-from-aws-codepipeline-to-aws-lambda-in-go-ffe</guid>
      <description>&lt;h2&gt;
  
  
  Context
&lt;/h2&gt;

&lt;p&gt;I was trying to setup the &lt;code&gt;UserParameters&lt;/code&gt; configuration within the AWS CodePipeline Template being generated,&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;...&lt;/span&gt;
&lt;span class="na"&gt;Actions&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;Invoke-Lambda&lt;/span&gt;
    &lt;span class="na"&gt;ActionTypeId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;Category&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Invoke&lt;/span&gt;
      &lt;span class="na"&gt;Owner&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS&lt;/span&gt;
      &lt;span class="na"&gt;Provider&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Lambda&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;1'&lt;/span&gt;
    &lt;span class="na"&gt;Configuration&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;exampleLambdaFunction&lt;/span&gt;
      &lt;span class="na"&gt;UserParameters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;{"example":"user-parameters"}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While testing it out on an AWS Lambda, written in Go, it took a bit longer than usual to find out the Function Definition for the Handler, to parse the AWS CodePipeline JSON Event that would be sent, For Example:&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;"CodePipeline.job"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"11111111-abcd-1111-abcd-111111abcdef"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"accountId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"111111111111"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"actionConfiguration"&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;"configuration"&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;"FunctionName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"exampleLambdaFunction"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"UserParameters"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;example&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;user-parameters&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;}"&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"inputArtifacts"&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="err"&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="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Solution
&lt;/h2&gt;

&lt;p&gt;Use the &lt;code&gt;github.com/aws/aws-lambda-go/events&lt;/code&gt; package &lt;a href="https://github.com/aws/aws-lambda-go/blob/main/events/codepipeline_job.go" rel="noopener noreferrer"&gt;link&lt;/a&gt; which contains the &lt;code&gt;events.CodePipelineJobEvent&lt;/code&gt; that helps unmarshal the AWS CodePipeline JSON event being sent&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"context"&lt;/span&gt;
    &lt;span class="s"&gt;"fmt"&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/aws/aws-lambda-go/events"&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/aws/aws-lambda-go/lambda"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;Handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt; &lt;span class="n"&gt;events&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CodePipelineJobEvent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"received codepipeline event function name: %+v&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CodePipelineJob&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Data&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ActionConfiguration&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Configuration&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FunctionName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"received codepipeline event user parameters: %+v&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CodePipelineJob&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Data&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ActionConfiguration&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Configuration&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UserParameters&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"cool"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/codepipeline/latest/userguide/action-reference-Lambda.html" rel="noopener noreferrer"&gt;https://docs.aws.amazon.com/codepipeline/latest/userguide/action-reference-Lambda.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/codepipeline/latest/userguide/action-reference-Lambda.html#action-reference-Lambda-event" rel="noopener noreferrer"&gt;https://docs.aws.amazon.com/codepipeline/latest/userguide/action-reference-Lambda.html#action-reference-Lambda-event&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/aws/aws-lambda-go/blob/main/events/codepipeline_job.go" rel="noopener noreferrer"&gt;https://github.com/aws/aws-lambda-go/blob/main/events/codepipeline_job.go&lt;/a&gt; &lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>aws</category>
      <category>go</category>
      <category>awscodepipeline</category>
      <category>awslambda</category>
    </item>
    <item>
      <title>Importing CloudFormation Resources to help fix deployments to Production</title>
      <dc:creator>Prithvi Jethwa</dc:creator>
      <pubDate>Sun, 31 Mar 2024 16:33:15 +0000</pubDate>
      <link>https://dev.to/prithvijj/importing-cloudformation-resources-to-help-fix-deployments-to-production-3ejn</link>
      <guid>https://dev.to/prithvijj/importing-cloudformation-resources-to-help-fix-deployments-to-production-3ejn</guid>
      <description>&lt;h2&gt;
  
  
  Context
&lt;/h2&gt;

&lt;p&gt;A Frontend Developer in another Team, had merged their PR with the following changes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;# serverless.yml
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gi"&gt;+ tags:
+    coolTag: coolValue
+    coolTag2: coolValue2
&lt;/span&gt;&lt;span class="gd"&gt;- resources:
&lt;/span&gt;   Description: Cool Description
   Resources:
     CoolS3Bucket:
       Type: AWS::S3::Bucket
       ...
&lt;span class="err"&gt;
&lt;/span&gt;     CoolS3BucketPolicy:
       Type: AWS::S3::BucketPolicy
       ...
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It was a simple PR to add in AWS Tags for helping out with visibility of the resources created through AWS CloudFormation and also for cost exploration purposes.&lt;/p&gt;

&lt;p&gt;It also removed 1 key called &lt;code&gt;resources&lt;/code&gt;. &lt;code&gt;Serverless Framework&lt;/code&gt; enthusiasts will know that &lt;code&gt;resources&lt;/code&gt; [1] contains the AWS CloudFormation Resources that we'd like to deploy. Since the key got removed, it also removed any resources that were being managed, namely the &lt;code&gt;CoolS3Bucket&lt;/code&gt; and &lt;code&gt;CoolS3BucketPolicy&lt;/code&gt;. It contained objects that can only be accessed through AWS CloudFront, using custom headers [2].&lt;/p&gt;

&lt;p&gt;After the PR was merged, the new CloudFormation Template was deployed to &lt;code&gt;Alpha&lt;/code&gt;, &lt;code&gt;Staging&lt;/code&gt; and then &lt;code&gt;Production&lt;/code&gt;, and caused the website to go down for a little while 😅&lt;/p&gt;

&lt;p&gt;While it eventually got fixed by adding in the Bucket Policy manually to the S3 Bucket, we now have a scenario where if we reintroduce the &lt;code&gt;resources&lt;/code&gt; key (reverting the PR is a quick way), it would cause deployments to Production to fail, because the CloudFormation Template wants to "create" a new resource called &lt;code&gt;cool-s3-bucket&lt;/code&gt; but fails to "create" it, since it already exists. For fun, I tried to help fix their issue.&lt;/p&gt;

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

&lt;p&gt;The first thing I checked was when the new CloudFormation Template (without any resources) was deployed to update the CloudFormation Stack, did it throw an error when attempting to remove a S3 Bucket that contained objects? Turns out based on the &lt;code&gt;Events&lt;/code&gt; Tab for the CloudFormation Stack, it did try to delete the S3 bucket, but could not since there were objects within it, which is expected. However the CloudFormation Stack just outright removed the &lt;code&gt;S3 Bucket&lt;/code&gt; and &lt;code&gt;S3 Bucket Policy&lt;/code&gt; Resource that it managed. This felt weird because there was an error that occurred with the Deletion of the S3 bucket but the CloudFormation Stack Update silently succeeded stating &lt;code&gt;Update successful. One or more resources could not be deleted&lt;/code&gt;. I expected an &lt;code&gt;UPDATE_ROLLBACK_*&lt;/code&gt; to appear, but it didn't 🤷.&lt;/p&gt;

&lt;p&gt;Two options came to mind to help fix the current issue:&lt;/p&gt;

&lt;p&gt;1) &lt;strong&gt;Create, Update, Delete, Recreate&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a new S3 Bucket &lt;code&gt;cool-s3-bucket-2&lt;/code&gt; and S3 Bucket policy entirely&lt;/li&gt;
&lt;li&gt;Update AWS CloudFront to refer to the new S3 Bucket&lt;/li&gt;
&lt;li&gt;Delete the &lt;code&gt;cool-s3-bucket&lt;/code&gt; Resource&lt;/li&gt;
&lt;li&gt;Recreate the S3 Bucket named &lt;code&gt;cool-s3-bucket&lt;/code&gt; with the Policy&lt;/li&gt;
&lt;li&gt;Update AWS CloudFront to refer back to the old S3 Bucket&lt;/li&gt;
&lt;li&gt;Delete the new S3 Bucket &lt;code&gt;cool-s3-bucket-2&lt;/code&gt; so that we're back to the same position we were before the PR was introduced&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With this approach, it just works and while there would be a bunch of resource deletions, it should bring things back to normal. I was confident about it, since it looks pretty standard in implementation, while avoiding any downtime.&lt;/p&gt;

&lt;p&gt;2) &lt;strong&gt;Import Resources&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I would glance by the &lt;code&gt;Import CloudFormation Resources&lt;/code&gt; feature, but was suspicious about it, mainly in terms of does it actually work? I had refrained from using it thinking that it would mess up the CloudFormation Resources that were being imported versus the CloudFormation Resources intended to be deployed through &lt;code&gt;serverless.yml&lt;/code&gt;. But it was an option and so to help alleviate the mystery of Importing CloudFormation Resources, looked at understanding it by creating a simple POC to see if it works.&lt;/p&gt;

&lt;p&gt;I setup the following for my POC:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Manually created the S3 Bucket named &lt;code&gt;test-cool-s3-bucket&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Manually setup a random S3 Bucket Policy&lt;/li&gt;
&lt;li&gt;Crafted and deployed a CloudFormation Template with minimal resources (example: Log Group Resource)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I clicked on the &lt;code&gt;Stack Actions -&amp;gt; Import Resources into stack&lt;/code&gt; option and just read through the simple instructions provided. From there, crafted a template that contained S3 Bucket, and S3 Bucket Policy&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;# cloudformation-template.yml converted to json after&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;TestCoolS3Bucket&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="s"&gt;...&lt;/span&gt;

  &lt;span class="na"&gt;TestCoolS3BucketPolicy&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::BucketPolicy&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Import Wizard asked for the &lt;code&gt;BucketName&lt;/code&gt; to import and after verifying the list of resources being imported, clicked on the &lt;code&gt;Import Resources&lt;/code&gt; Button. It was able to successfully import my manually created resources into the CloudFormation Stack. I tried updating values within the CloudFormation Template, and was able to successfully deploy an updated CloudFormation Template.&lt;/p&gt;

&lt;h2&gt;
  
  
  Results
&lt;/h2&gt;

&lt;p&gt;🎉 Since the &lt;code&gt;Import Resources&lt;/code&gt; method POC worked, I suggested a plan of steps that needed to be taken. I Crafted the CloudFormation Templates for all our stages/environments (Example: &lt;code&gt;Alpha&lt;/code&gt; &lt;code&gt;Staging&lt;/code&gt; &lt;code&gt;Production&lt;/code&gt; etc.) and we followed those steps for &lt;code&gt;Alpha&lt;/code&gt;. Once it had successfully imported the resources, we tried to rebuild the Jenkins pipeline and the job was successful. The &lt;code&gt;Alpha&lt;/code&gt; CloudFormation Stack was back to normal!&lt;/p&gt;

&lt;p&gt;We then asked our OPs team to execute those steps to import the CloudFormation Resources into the CloudFormation Stack for the remaining stages/environments, and verified the Jenkins Deployments were successful all the way till &lt;code&gt;Production&lt;/code&gt;!&lt;/p&gt;

&lt;h2&gt;
  
  
  Things that make you go &lt;code&gt;hmmmm&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Q) Were there any quality gates as such that would have caught this?&lt;/p&gt;

&lt;p&gt;Ans) From what I had observed, seems like the PR being reviewed  was the only quality gate. There weren't any diffs being generated for the current CloudFormation Template with the proposed CloudFormation Template. Action items were created later on, to help create more quality gates for seemingly sensitive infrastructure changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://www.serverless.com/framework/docs-providers-aws-guide-resources" rel="noopener noreferrer"&gt;https://www.serverless.com/framework/docs-providers-aws-guide-resources&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-overview.html#forward-custom-headers-restrict-access" rel="noopener noreferrer"&gt;https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-overview.html#forward-custom-headers-restrict-access&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>aws</category>
      <category>cloudformation</category>
      <category>serverlessframework</category>
    </item>
    <item>
      <title>Saving 90% of our AWS Cost using ECR Lifecycle Rules</title>
      <dc:creator>Prithvi Jethwa</dc:creator>
      <pubDate>Sat, 16 Mar 2024 18:28:41 +0000</pubDate>
      <link>https://dev.to/prithvijj/saving-90-of-our-aws-cost-using-ecr-lifecycle-rules-353f</link>
      <guid>https://dev.to/prithvijj/saving-90-of-our-aws-cost-using-ecr-lifecycle-rules-353f</guid>
      <description>&lt;h2&gt;
  
  
  Context
&lt;/h2&gt;

&lt;p&gt;For fun, I logged into an AWS account that is used for development purposes. Due to it's purpose, there was a high chance that resources may have been dangling around and not being cleaned up. &lt;/p&gt;

&lt;p&gt;Exploring in the AWS Costs Explorer, I took a quick look at the different services, and I came across the ECR Service, which was around &lt;code&gt;$16 per day (around $480 per month)&lt;/code&gt;. Switching to &lt;code&gt;Dimension: Usage Type&lt;/code&gt; seems like most of the cost was for an attribute called &lt;code&gt;TimedStorage-BytesHrs&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;I opened random ECR Private Repos that were manually setup before through a bash script. I found one in particular where the ECR Repo had around &lt;code&gt;9000+ Images&lt;/code&gt;, and most of it was prefixed with tag named as &lt;code&gt;dev-*&lt;/code&gt;. Just scrolling through each image size was around &lt;code&gt;600 MB&lt;/code&gt;, and existed as far back as 2021. Oh boy, I had to find out what it costed us.&lt;/p&gt;

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

&lt;p&gt;I had to get the size of the Repository, and hence used the AWS CLI command to retrieve the information using &lt;code&gt;AWS CloudShell&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws ecr describe-images &lt;span class="nt"&gt;--repository-name&lt;/span&gt; &lt;span class="s1"&gt;'repo-a'&lt;/span&gt; &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s1"&gt;'imageDetails[*].imageSizeInBytes'&lt;/span&gt; | jq &lt;span class="s1"&gt;'.[]'&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{sum+=0} END{print sum}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Copied over the Bytes Value, and converting into GB&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;4,236,629,104,429 Bytes/1024 = 4,137,333,109.79 KB
4,137,333,109.79 KB/1024 = 4,040,364.36 MB
4,040,364.36 MB/1024 = 3,945.66 GB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Yeah that's a lotta GBs. Taking a quick look at &lt;a href="https://aws.amazon.com/ecr/pricing/" rel="noopener noreferrer"&gt;ECR Pricing&lt;/a&gt;, it's &lt;code&gt;$0.10 per GB / month&lt;/code&gt;, so doing some quick maths&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;3,945.66 GB * $0.1 GB per month = $ 394 per month potentially
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This was just for a single ECR Private Repository, which seemed super weird that we were paying this for a long while. Oh man, if this is for 1 ECR Repository, what about the others?&lt;/p&gt;

&lt;p&gt;Quick &lt;code&gt;ChatGPT&lt;/code&gt; queries later, had got the AWS CLI commands to call&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;#!/bin/bash&lt;/span&gt;

 &lt;span class="nv"&gt;repositories&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;aws ecr describe-repositories &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s1"&gt;'repositories[*].repositoryName'&lt;/span&gt; &lt;span class="nt"&gt;--output&lt;/span&gt; text&lt;span class="si"&gt;)&lt;/span&gt;

 &lt;span class="k"&gt;for &lt;/span&gt;repo &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nv"&gt;$repositories&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
         &lt;/span&gt;&lt;span class="nv"&gt;size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;aws ecr describe-images &lt;span class="nt"&gt;--repository-name&lt;/span&gt; &lt;span class="nv"&gt;$repo&lt;/span&gt; &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s1"&gt;'imageDetails[*].imageSizeInBytes'&lt;/span&gt; | jq &lt;span class="s1"&gt;'if length == 0 then 0 else .[] end'&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{sum+=$0} END{print sum}'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
         &lt;span class="nv"&gt;leng&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;aws ecr list-images &lt;span class="nt"&gt;--repository-name&lt;/span&gt; &lt;span class="nv"&gt;$repo&lt;/span&gt; | jq &lt;span class="s1"&gt;'.imageIds | unique_by(.imageDigest) | length'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
         &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Repository: &lt;/span&gt;&lt;span class="nv"&gt;$repo&lt;/span&gt;&lt;span class="s2"&gt;, num-of-images: &lt;/span&gt;&lt;span class="nv"&gt;$leng&lt;/span&gt;&lt;span class="s2"&gt;, Total Size: &lt;/span&gt;&lt;span class="nv"&gt;$size&lt;/span&gt;&lt;span class="s2"&gt; Bytes"&lt;/span&gt;
 &lt;span class="k"&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Called this script within &lt;code&gt;AWS CloudShell&lt;/code&gt; and piped the output&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bash cool.sh | &lt;span class="nb"&gt;tee &lt;/span&gt;cool.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From here I wanted to get a list of ECR Private Repos sorted by &lt;code&gt;Size&lt;/code&gt; so I used the following&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt; &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{sum=$7/1024/1024/1024} {cost=sum*0.1} {printf("%s\tnum-of-images: %s\t%s GB\t\$%s\n",$2,$4,sum,cost)}'&lt;/span&gt; cool.txt | column &lt;span class="nt"&gt;-t&lt;/span&gt; | &lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="nt"&gt;-k4&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; sorted-cool.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I was able to get a list of all the ECR Private Repositories, along with the Total size of the given repository, and the potential cost for it&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Rough Numbers

...
repo-d, num-of-images:  3800,  890     GB  $89.0
repo-c, num-of-images:  3100,  1000    GB  $100.0
repo-b, num-of-images:  5000,  1600    GB  $160.0
repo-a, num-of-images:  9800,  3900    GB  $390.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Where to go from here?
&lt;/h2&gt;

&lt;p&gt;I started with providing visibility, by making a post on the Slack Channel to the Team that owned those ECR Private Repositories and providing them information about what I had found. I also asked for their permission to act on cleaning out those unused resources. This eventually led to a 1 hour meeting where we identified stale images and ECR Private Repos that should definitely be deleted.&lt;/p&gt;

&lt;p&gt;Next I gathered screenshots of all the ECR Privates Repos we would intend to purge, to get "evidence" of a before and after comparison.&lt;/p&gt;

&lt;p&gt;After all the "evidences" were validated, sent the following message in the Slack Thread.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;I intend to start this procedure by 12:45PM
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;[ℹ️ &lt;code&gt;Turn the Ship Around!&lt;/code&gt; is a nice book, learned about &lt;code&gt;I intend to&lt;/code&gt; before doing something.]&lt;/p&gt;

&lt;p&gt;I went into the 9 ECR Private Repos and manually added the following Lifecycle Rules&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1 Untagged    expire | sinceImagePushed(7 days) | untagged
2 dev-*       expire | sinceImagePushed(14 days) | tagged prefix [dev-]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This essential means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Delete any untagged images that are 7 days old&lt;/li&gt;
&lt;li&gt;Delete any tagged images with prefix &lt;code&gt;dev-&lt;/code&gt; that are 14 days old&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After adding it in, we waited for a couple of days for the Lifecycle Rules to execute.&lt;/p&gt;

&lt;h2&gt;
  
  
  Results
&lt;/h2&gt;

&lt;p&gt;We saw some great results&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Before:
# Rough Numbers

...
repo-d, num-of-images:  3800,  890     GB  $89.0
repo-c, num-of-images:  3100,  1000    GB  $100.0
repo-b, num-of-images:  5000,  1600    GB  $160.0
repo-a, num-of-images:  9800,  3900    GB  $390.0
&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;# After
# Rough Numbers

...
repo-d, num-of-images:  420,  90     GB  $9.0
repo-c, num-of-images:  360,  120    GB  $12
repo-b, num-of-images:  480,  150    GB  $15
repo-a, num-of-images:  670,  330    GB  $33
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;🤑 🎉 Looking at the ECR Costs in Cost Explorer, we saw that the Storage costs of the image in the ECR Private Repos (&lt;code&gt;Attribute: TimedStorage-ByteHrs&lt;/code&gt;) dropped to around &lt;code&gt;$1.48 per day ($44.4 per month)&lt;/code&gt; compared to the &lt;code&gt;$16 per day (around $480 per month)&lt;/code&gt; which was &lt;code&gt;~90% savings&lt;/code&gt; on our AWS bills! &lt;/p&gt;

&lt;p&gt;It was good to share these results with the Team.&lt;br&gt;
One thing that I'd look forward to how to is how to implement this at scale. &lt;a href="https://knowyourmeme.com/memes/thats-a-lotta-damage" rel="noopener noreferrer"&gt;Since that's a lotta savings&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Things that make you go &lt;code&gt;hmmmm&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Q) How did this flow under the radar?&lt;/p&gt;

&lt;p&gt;Ans) I think that it was a mix of :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Assumptions that there were checks and balances (like the ECR Lifecycle Rules) were already setup&lt;/li&gt;
&lt;li&gt;Changing the Docker Tags that were being used across the 4 years&lt;/li&gt;
&lt;li&gt;No observability in terms of size of the ECR Repository&lt;/li&gt;
&lt;li&gt;Change in Priorities and Teams might have caused this to flow under the Radar&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Q) You mentioned manually adding, Why not automate it using a script?&lt;/p&gt;

&lt;p&gt;Ans) I think that it was best to start off with manually adding in the rules to help remove most of the costs, and then acting on how this can be scaled out for all repositories.&lt;/p&gt;

&lt;p&gt;Q) You mentioned the ECR Repos manually setup using a bash script? Why not IaC?&lt;/p&gt;

&lt;p&gt;Unluckly, when the project initially began, there was a bash script that was provided and run manually to help setup the ECR Private Repos. There is clearly room for improvement in terms of converting it all into IaC that can be managed&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://aws.amazon.com/ecr/pricing/" rel="noopener noreferrer"&gt;https://aws.amazon.com/ecr/pricing/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://knowyourmeme.com/memes/thats-a-lotta-damage" rel="noopener noreferrer"&gt;https://knowyourmeme.com/memes/thats-a-lotta-damage&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>aws</category>
      <category>savings</category>
      <category>ecr</category>
      <category>costoptimization</category>
    </item>
    <item>
      <title>Resolving 'provided principal was invalid' for AWS::Lambda::Permission</title>
      <dc:creator>Prithvi Jethwa</dc:creator>
      <pubDate>Fri, 08 Mar 2024 19:51:59 +0000</pubDate>
      <link>https://dev.to/prithvijj/resolving-provided-principal-was-invalid-for-awslambdapermission-49jp</link>
      <guid>https://dev.to/prithvijj/resolving-provided-principal-was-invalid-for-awslambdapermission-49jp</guid>
      <description>&lt;h2&gt;
  
  
  Context
&lt;/h2&gt;

&lt;p&gt;My friend puts the following Error Message on a Slack Channel that our Team monitors and provides assistance in&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;coolLambdaPermission | UPDATE_FAILED | Resource handler returned message: "The provided principal was invalid. Please check the principal and trying again" (Service: Lambda, Status Code: 400 ...)&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;He also provides some details on the Jenkins Job that showed this error message, and wanted to get some help. I was free so I jumped into resolving it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Issue
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;AWS::Lambda::Permission&lt;/code&gt; CloudFormation Resource would fail to update within the CloudFormation Stack, due to the Principal provided being invalid (based on the error message), even though the Principal provided within the CloudFormation Template was verified as valid. &lt;/p&gt;

&lt;h2&gt;
  
  
  Debugging
&lt;/h2&gt;

&lt;p&gt;The first step I took was checking the AWS CloudFormation Events Tab to see the error message, and as seen the &lt;code&gt;AWS::Lambda::Permission&lt;/code&gt; CloudFormation Resource failed to update.&lt;/p&gt;

&lt;p&gt;Looking into the CloudFormation Template that initiated the Stack Update, I verified the the &lt;code&gt;AWS::IAM::Role&lt;/code&gt; used within the &lt;code&gt;AWS::Lambda::Permission&lt;/code&gt; Principal attribute existed and was valid.&lt;/p&gt;

&lt;p&gt;I checked the Policy Permissions and Trust Relationship for the given IAM Role, and compared it with similar CloudFormation Stacks that use the same IAM Role for the &lt;code&gt;AWS::Lambda::Permission&lt;/code&gt; resource, but things looked fine.&lt;/p&gt;

&lt;p&gt;Quick Google-Fu searches later, I didn't really get anywhere. My Manager took a quick look and mentioned that the Lambda Permission has an IAM Role associated, which does not exist since it was deleted, which might be causing issues for CloudFormation Stack Updates 🤷&lt;/p&gt;

&lt;p&gt;🤔 Since the CloudFormation Template changes involved the following&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;# serverless.yml
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="p"&gt;coolLambdaPermission:
&lt;/span&gt;  Type: "AWS::Lambda::Permission"
  Properties:
    FunctionName: ...
    Action: ...
&lt;span class="gd"&gt;-    Principal: ${self:custom.deletedRole}
&lt;/span&gt;&lt;span class="gi"&gt;+    Principal: ${self:custom.validRole}  
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I would have expected the new &lt;code&gt;Valid Role&lt;/code&gt; to &lt;strong&gt;replace&lt;/strong&gt; the old &lt;code&gt;Deleted Role&lt;/code&gt; when the CloudFormation Stack is being updated, which it's exactly trying to do.&lt;/p&gt;

&lt;p&gt;For a Sanity check, I went into the Lambda Resource, associated with the Lambda Permission, available under &lt;code&gt;Lambda -&amp;gt; Configuration Tab -&amp;gt; Permissions Tab -&amp;gt; Resource-based policy statements&lt;/code&gt; and verified the &lt;code&gt;Deleted Role&lt;/code&gt; is present in the Principal, but the actual IAM Role is deleted.&lt;/p&gt;

&lt;p&gt;I concluded that it must be the &lt;code&gt;AWS::Lambda::Permission&lt;/code&gt; &lt;code&gt;Physical ID&lt;/code&gt; that might be causing the error and hence suggested my friend to create a PR that updates that resource&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;# serverless.yml
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gd"&gt;- coolLambdaPermission:
&lt;/span&gt;&lt;span class="gi"&gt;+ coolLambdaPermissions:
&lt;/span&gt;  Type: "AWS::Lambda::Permission"
  Properties:
    FunctionName: ...
    Action: ...
&lt;span class="gd"&gt;-    Principal: ${self:custom.deletedRole}
&lt;/span&gt;&lt;span class="gi"&gt;+    Principal: ${self:custom.validRole}  
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This would mean that CloudFormation would create a new &lt;code&gt;AWS::Lambda::Permission&lt;/code&gt; Resource with a new &lt;code&gt;Physical ID&lt;/code&gt;, and the update would go through. Nope ❌, that did not work, It failed with the same error message.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;The provided principal was invalid. Please check the principal and trying again
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;❓ My next question was, &lt;code&gt;Can I manually add in the Valid Role to the Lambda Permission?&lt;/code&gt; So I went into the Lambda Console, and attempted to manually add the &lt;code&gt;Valid Role&lt;/code&gt;, and it failed with the same error. This is actually great.&lt;/p&gt;

&lt;p&gt;Next step was to manually remove the &lt;code&gt;Deleted Role&lt;/code&gt; Principal within the Lambda Console, and try to add the &lt;code&gt;Valid Role&lt;/code&gt; Principal again, and that worked! 🎉&lt;/p&gt;

&lt;p&gt;This seems like the Lambda wants all the Principals provided to be Valid, and only then would it allow for more Principals to be added in.&lt;/p&gt;

&lt;p&gt;I suggested my friend to do the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a PR that removes the &lt;code&gt;AWS::Lambda::Permission&lt;/code&gt; altogether
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;# serverless.yml
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gd"&gt;- coolLambdaPermission:
-  Type: "AWS::Lambda::Permission"
-  Properties:
-    FunctionName: ...
-    Action: ...
-    Principal: ${self:custom.deletedRole}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Create a follow up PR that adds the &lt;code&gt;AWS::Lambda::Permission&lt;/code&gt; Resource back
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;# serverless.yml
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gi"&gt;+ coolLambdaPermission:
+  Type: "AWS::Lambda::Permission"
+  Properties:
+    FunctionName: ...
+    Action: ...
+    Principal: ${self:custom.validRole}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;✅ That worked and the CloudFormation Stack was updated successfully&lt;/p&gt;

&lt;h2&gt;
  
  
  Solution
&lt;/h2&gt;

&lt;p&gt;If you have the &lt;code&gt;AWS::Lambda::Permission&lt;/code&gt; CloudFormation Resource, which contains a Principal that does not exist anymore, and if you want to update the Principal attribute with a &lt;code&gt;Valid Role&lt;/code&gt;, you'll need to remove the &lt;code&gt;AWS::Lambda::Permission&lt;/code&gt; CloudFormation Resource first, and then add it back in with the &lt;code&gt;Valid Role&lt;/code&gt;&lt;br&gt;
i.e.&lt;/p&gt;

&lt;p&gt;❌ Replacing the Principal within the CloudFormation Template and deploying it would not work(atleast for me)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;# serverless.yml
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gd"&gt;- coolLambdaPermission:
&lt;/span&gt;&lt;span class="gi"&gt;+ coolLambdaPermissions:
&lt;/span&gt;  Type: "AWS::Lambda::Permission"
  Properties:
    FunctionName: ...
    Action: ...
&lt;span class="gd"&gt;-    Principal: ${self:custom.deletedRole}
&lt;/span&gt;&lt;span class="gi"&gt;+    Principal: ${self:custom.validRole}  
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Rather,&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;✅ Removing the CloudFormation Resource and deploying the CloudFormation Template
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;# serverless.yml
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gd"&gt;- coolLambdaPermission:
-  Type: "AWS::Lambda::Permission"
-  Properties:
-    FunctionName: ...
-    Action: ...
-    Principal: ${self:custom.deletedRole}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;✅ Adding back the CloudFormation Resource with the new Principal and then deploying the CloudFormation Template
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;# serverless.yml
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gi"&gt;+ coolLambdaPermission:
+  Type: "AWS::Lambda::Permission"
+  Properties:
+    FunctionName: ...
+    Action: ...
+    Principal: ${self:custom.validRole}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Notes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;I would have assumed that changing the Principal Value within the &lt;code&gt;AWS::Lambda::Permission&lt;/code&gt; Resource would have replaced it with a &lt;code&gt;Valid Role&lt;/code&gt;, but the oddities of AWS are a mystery.&lt;/li&gt;
&lt;li&gt;A question you might ponder is why was the &lt;code&gt;Deleted Role&lt;/code&gt;, deleted? It seems like there are Resources that use the &lt;code&gt;Deleted Role&lt;/code&gt;? It was because of a migration process that involved removing that &lt;code&gt;Deleted Role&lt;/code&gt; since it should not be used anymore. Who started the migration process, ummm &lt;strong&gt;me&lt;/strong&gt; 😬&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-permission.html" rel="noopener noreferrer"&gt;https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-permission.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>aws</category>
      <category>lambda</category>
      <category>iam</category>
      <category>cloudformation</category>
    </item>
  </channel>
</rss>
