<?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: Mohamed Wasim</title>
    <description>The latest articles on DEV Community by Mohamed Wasim (@wasimtty).</description>
    <link>https://dev.to/wasimtty</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%2F2146601%2F15f1c2df-ee9d-45c5-b681-0eb292992435.jpg</url>
      <title>DEV Community: Mohamed Wasim</title>
      <link>https://dev.to/wasimtty</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/wasimtty"/>
    <language>en</language>
    <item>
      <title>Deploy Fider as a Private App on AWS with CloudFront VPC Origin</title>
      <dc:creator>Mohamed Wasim</dc:creator>
      <pubDate>Wed, 26 Mar 2025 04:49:33 +0000</pubDate>
      <link>https://dev.to/ittrident/deploy-fider-as-a-private-app-on-aws-with-cloudfront-vpc-origin-1oa6</link>
      <guid>https://dev.to/ittrident/deploy-fider-as-a-private-app-on-aws-with-cloudfront-vpc-origin-1oa6</guid>
      <description>&lt;p&gt;When architecting solutions on AWS, minimizing the attack surface and ensuring secure deployments is essential. CloudFront VPC Origins provides a way to deliver content from private VPC subnet, reducing direct exposure to the internet. This makes it an ideal choice for those looking to host a private app while securely exposing it to the public internet, offering a more secure alternative to exposing applications from a public subnet.&lt;/p&gt;

&lt;p&gt;In this guide, I’ll explain:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
What is CloudFront VPC Origin
&lt;/li&gt;
&lt;li&gt;
Why you should deploy Fider as a private application &lt;/li&gt;
&lt;li&gt;
Create an Internal Application Load Balancer &lt;/li&gt;
&lt;li&gt;Create CloudFront VPC origin&lt;/li&gt;
&lt;li&gt;Create a CloudFront Distribution&lt;/li&gt;
&lt;li&gt;Create Secrets manager secret and Lambda function&lt;/li&gt;
&lt;li&gt;Create a Custom Header rule in AWS WAF&lt;/li&gt;
&lt;li&gt;Spin up ECS and RDS&lt;/li&gt;
&lt;li&gt;Monitor CloudFront and WAF logs&lt;/li&gt;
&lt;li&gt;Do You Really Need Zero Trust for Every App&lt;/li&gt;
&lt;li&gt;Is CloudFront Truly Zero Trust Compliant&lt;/li&gt;
&lt;li&gt;Deciding on Enterprise Grade Deployment for Fider&lt;/li&gt;
&lt;li&gt;Caveats to consider&lt;/li&gt;
&lt;li&gt;Final thoughts&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  1. What is CloudFront VPC Origin
&lt;/h2&gt;

&lt;p&gt;Traditionally, when using CloudFront with an origin resource (such as an ALB or EC2 instance), the origin needs to be in a public subnet with a public IP address, implementing Access Control Lists (ACLs) and other controls to restrict access effectively. Users needed to invest ongoing effort to implement and maintain these solutions, resulting in undifferentiated heavy lifting.&lt;/p&gt;

&lt;p&gt;However, With CloudFront VPC Origins, users can host their applications in a private VPC, without requiring any direct route to the internet and make sure CloudFront is the only entry point to their applications. When CloudFront VPC Origin is set up as an origin for the CloudFront distribution, traffic stays on the high-throughput AWS Backbone network all the way to your AWS origin, making sure of optimized performance and low latency.&lt;/p&gt;

&lt;p&gt;This is designed to prevent end users from discovering or bypassing CloudFront to access web applications directly. By implementing &lt;strong&gt;network-level isolation&lt;/strong&gt;, the origin servers remain hidden on the internet (&lt;em&gt;Obfuscating AWS resources&lt;/em&gt;), significantly reducing the attack surface (&lt;em&gt;attack surface reduction&lt;/em&gt;) and enhancing the overall security posture. At the same time, users continue to benefit from the CloudFront global scale and high-performance capabilities.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Why you should deploy Fider as a private application
&lt;/h2&gt;

&lt;p&gt;Alright, here’s the deal: With this setup, even your ALB can be &lt;strong&gt;internal&lt;/strong&gt;, and it'll route traffic straight to your ECS instances in those private subnets. You heard that right, &lt;strong&gt;no public access&lt;/strong&gt;. This keeps your backend services completely hidden from the internet. User requests go from CloudFront to the VPC origins over a private, secure connection, providing additional security for your applications.&lt;/p&gt;

&lt;p&gt;Now, with CloudFront VPC origin, you can stick that ALB in a private subnet where nobody can get to it directly. CloudFront is the only way in, so you can rest easy knowing the attack surface is way smaller. Plus, that ALB? The DNS name only resolves to private IPs, so no internet users can mess with it. By doing so, you significantly reduce the exposure of your internal ALB to DDoS attacks.&lt;/p&gt;

&lt;p&gt;Even if someone were to somehow discover your ALB’s ARN (which is very unlikely), they still couldn’t send traffic to it using their own CloudFront distribution. That’s because internal ALBs are only accessible from within the same AWS account and VPC.&lt;/p&gt;

&lt;p&gt;And if you’re really serious about security, you can slap on Origin Custom Headers to make sure only your CloudFront distribution is allowed to talk to the ALB. This header verification ensures the traffic legitimacy. But wait, it gets better, those custom headers can be &lt;strong&gt;dynamically rotated&lt;/strong&gt; using AWS Secrets Manager and a Lambda function automation, keeping everything secure without manual intervention.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Create an Internal Application Load Balancer
&lt;/h2&gt;

&lt;p&gt;Login to management console and navigate to EC2, under Load Balancing section choose Load Balancers,&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%2Fhwit2823adfy7r6fisph.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%2Fhwit2823adfy7r6fisph.png" alt=" " width="800" height="133"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click on create load balancer, and choose Application Load Balancer&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%2Fzi33anfre4it0h3476u5.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%2Fzi33anfre4it0h3476u5.png" alt=" " width="800" height="640"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Under &lt;strong&gt;Basic Configuration&lt;/strong&gt; choose &lt;strong&gt;Internal&lt;/strong&gt; as scheme,&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%2Fgmk3nnl8qbq529m55ex8.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%2Fgmk3nnl8qbq529m55ex8.png" alt=" " width="800" height="388"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Under &lt;strong&gt;Network Mapping&lt;/strong&gt; choose the VPC you created or refer &lt;a href="https://dev.to/ittrident/deploying-fider-on-aws-ecs-a-step-by-step-guide-to-deploy-a-feedback-platform-jl4#create-a-vpc"&gt;this&lt;/a&gt; guide to create a VPC, creating a single public subnet should suffice this time (for placing NAT gateway).&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%2F4gedzz1a7h6pac5whsb5.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%2F4gedzz1a7h6pac5whsb5.png" alt=" " width="800" height="166"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Choose to launch the Application Load Balancer in at least 2 Azs, in private subnets.&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%2F3hxz3aseu9gsn7oiy4cj.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%2F3hxz3aseu9gsn7oiy4cj.png" alt=" " width="800" height="270"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Choose the &lt;strong&gt;Security group&lt;/strong&gt; for your internal ALB and create an HTTPS listener,&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%2Fh75c5l4hq9z6k1ox4agt.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%2Fh75c5l4hq9z6k1ox4agt.png" alt=" " width="800" height="270"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Navigate to the VPC console and click on the &lt;strong&gt;Managed prefix lists&lt;/strong&gt;, choose &lt;strong&gt;pl-b6a144df&lt;/strong&gt; and map this to your ALB's inbound SG rules(HTTPS), by doing so you are only letting the CloudFront's IP ranges to communicate with your ALB. This is the recommended approach according to &lt;a href="https://docs.aws.amazon.com/whitepapers/latest/aws-best-practices-ddos-resiliency/protecting-your-origin-bp1-bp5.html" rel="noopener noreferrer"&gt;AWS Best Practices for DDoS Resiliency.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn17m2qqt669zmu4wpivy.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%2Fn17m2qqt669zmu4wpivy.png" alt=" " width="800" height="392"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Under &lt;strong&gt;Secure listener settings&lt;/strong&gt; choose the certificate you created from &lt;strong&gt;ACM&lt;/strong&gt;, if not you can refer &lt;a href="https://dev.to/ittrident/deploying-fider-on-aws-ecs-a-step-by-step-guide-to-deploy-a-feedback-platform-jl4#create-an-ecs-service"&gt;this&lt;/a&gt; guide and forward to &lt;strong&gt;Step 3&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%2F249w3ev8gfhkjx7as86s.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%2F249w3ev8gfhkjx7as86s.png" alt=" " width="800" height="271"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once you are done with requesting the public certificate in &lt;strong&gt;ACM&lt;/strong&gt;, your certificate will appear in the drop-down select it, scroll down, and click on &lt;strong&gt;create load balancer&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%2Fs9huh97vrxj7kzfl4drt.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%2Fs9huh97vrxj7kzfl4drt.png" alt=" " width="800" height="347"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The internal ALB is now created with the traffic originating from the CloudFront IP ranges.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Create CloudFront VPC origin
&lt;/h2&gt;

&lt;p&gt;Now that you are done with the internal ALB creation, Navigate to the CloudFront service console and choose &lt;strong&gt;VPC origins&lt;/strong&gt; from the navigation pane.&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%2F755qv4zcwgvv6obdvllf.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%2F755qv4zcwgvv6obdvllf.png" alt=" " width="800" height="116"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click on VPC origins and create a VPC origin,&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%2Fn24oqjt8nl6ntg1kkgq4.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%2Fn24oqjt8nl6ntg1kkgq4.png" alt=" " width="800" height="116"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Provide a Name and choose internal ALB's &lt;strong&gt;ARN&lt;/strong&gt; from the &lt;strong&gt;Origin ARN&lt;/strong&gt; drop-down,&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%2Foxcfi7ns9b93mjrd8qzw.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%2Foxcfi7ns9b93mjrd8qzw.png" alt=" " width="800" height="338"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Choose the protocol to &lt;strong&gt;HTTPS only&lt;/strong&gt; and click on create VPC origin.&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%2Fnn38k0itn5nc03nvwz19.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%2Fnn38k0itn5nc03nvwz19.png" alt=" " width="800" height="205"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The VPC origin creation is now complete.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Create a CloudFront Distribution
&lt;/h2&gt;

&lt;p&gt;Navigate to the CloudFront service console and click on &lt;strong&gt;Create a CloudFront distribution&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%2F6w30yqgw5o68ei23ylpq.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%2F6w30yqgw5o68ei23ylpq.png" alt=" " width="800" height="389"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Under the &lt;strong&gt;Origin&lt;/strong&gt; section, select the VPC origin you created. Enable &lt;strong&gt;Origin Shield&lt;/strong&gt; for an additional layer of caching, and don't forget to add the &lt;strong&gt;custom header&lt;/strong&gt;, name it &lt;em&gt;x-origin-verify&lt;/em&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%2Fxpwwgg4tf3bd0m31zok3.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%2Fxpwwgg4tf3bd0m31zok3.png" alt=" " width="800" height="404"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Under &lt;strong&gt;Default cache behaviour&lt;/strong&gt; choose the following settings, under &lt;strong&gt;Allowed HTTP methods&lt;/strong&gt; choose the third option, if you want to know why then check out &lt;a href="https://dev.to/ittrident/deploying-fider-on-aws-ecs-a-step-by-step-guide-to-deploy-a-feedback-platform-jl4#create-cloudfront-distribution-with-waf"&gt;this&lt;/a&gt; guide for reason.&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%2Fden6r4xd7517s53zm48x.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%2Fden6r4xd7517s53zm48x.png" alt=" " width="800" height="325"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Under &lt;strong&gt;Cache key and origin requests&lt;/strong&gt; choose &lt;strong&gt;Cache policy and origin request policy (recommended)&lt;/strong&gt;. Choose the default cache policy and origin request policy. Under &lt;strong&gt;Response headers policy&lt;/strong&gt; choose &lt;strong&gt;CORS-with-preflight-and-SecurityHeadersPolicy&lt;/strong&gt;, it is an AWS-managed response header policy. Use this policy to specify security-related HTTP response headers that CloudFront adds to HTTP responses that it sends to viewers.&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%2Fv6ry4utjly2fuc7ilj8g.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%2Fv6ry4utjly2fuc7ilj8g.png" alt=" " width="800" height="325"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The AWS-managed policy provides the following security headers, it ultimately narrows down to your use case, you can also create a &lt;strong&gt;Custom policy&lt;/strong&gt; and attach it under &lt;strong&gt;Response headers policy&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%2Fam0kjezs4ixf4pfxvc4u.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%2Fam0kjezs4ixf4pfxvc4u.png" alt=" " width="800" height="409"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Enable &lt;strong&gt;Web Application Firewall&lt;/strong&gt; for your CloudFront distribution, toggle on &lt;strong&gt;use monitor mode&lt;/strong&gt; you can later disable it once you get know how much of your requests are being blocked by this WAF configuration(False positives and red herrings).&lt;/p&gt;

&lt;p&gt;Check &lt;strong&gt;SQL protections&lt;/strong&gt; and &lt;strong&gt;Rate limiting&lt;/strong&gt;(DDoS protection) and take a note of price estimation.&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%2Fk3owxatqrr42u3jho6lg.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%2Fk3owxatqrr42u3jho6lg.png" alt=" " width="800" height="398"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Add the &lt;strong&gt;CNAME&lt;/strong&gt; through which you would like to access your application publicly and choose the custom SSL certificate for your &lt;strong&gt;CNAME&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%2Fheel5vlalxbk5vkztzsb.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%2Fheel5vlalxbk5vkztzsb.png" alt=" " width="800" height="380"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Configure standard logging for your CloudFront distribution to monitor the performance and usage metrics.&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%2F5cp7j3uf57aiivz4pwo9.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%2F5cp7j3uf57aiivz4pwo9.png" alt=" " width="800" height="380"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The CloudFront distribution creation is now complete.&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%2Ffxak5ogjao1ibvljl4lb.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%2Ffxak5ogjao1ibvljl4lb.png" alt=" " width="800" height="328"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Navigate to the &lt;strong&gt;WAF &amp;amp; Shield&lt;/strong&gt; console, click on Web ACLs and choose Global(CloudFront) in the region drop-down. Click on the Web ACL and make a note of the rules and WCUs.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Create Secrets manager secret and Lambda function
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Choose secret type:&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Navigate to the Secrets manager service console, and click on &lt;strong&gt;Store a new secret&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%2Fsjvd83m1yfjvz6j6krac.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%2Fsjvd83m1yfjvz6j6krac.png" alt=" " width="800" height="194"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Under &lt;strong&gt;Secret type&lt;/strong&gt; choose &lt;em&gt;Other type of secret&lt;/em&gt;, and provide &lt;em&gt;HEADERVALUE&lt;/em&gt; as a &lt;strong&gt;Key&lt;/strong&gt; and paste the same header value you used in the CloudFront distribution. Click on next.&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%2F37oze0fl0rfki69gshpi.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%2F37oze0fl0rfki69gshpi.png" alt=" " width="800" height="335"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Configure secret:&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Choose the secret name and click on next, take a note of &lt;strong&gt;Resource permissions&lt;/strong&gt; step, we will come back to this step once we are done creating the lambda function.&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%2F4930xvcfq5fpymxqty4k.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%2F4930xvcfq5fpymxqty4k.png" alt=" " width="800" height="383"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Review:&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Skip to the review section leaving behind the configure rotation step, as we will configure that once we create and configure the lambda function.&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%2F7lheuxgmriccj5bgcj8o.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%2F7lheuxgmriccj5bgcj8o.png" alt=" " width="800" height="383"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The secret creation is now complete.&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%2Fqm1myrc3e643m9m8rp1s.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%2Fqm1myrc3e643m9m8rp1s.png" alt=" " width="800" height="103"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Create a rotator lambda function:&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Navigate to the lambda console and click on &lt;strong&gt;Create function&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%2F149dgasbdub1hxhrmmqx.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%2F149dgasbdub1hxhrmmqx.png" alt=" " width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Choose &lt;strong&gt;Author from scratch&lt;/strong&gt; and choose &lt;em&gt;Python 3.9&lt;/em&gt; as runtime with architecture as &lt;em&gt;x86_64&lt;/em&gt;. Under &lt;strong&gt;Execution role&lt;/strong&gt; choose create a new role, this creates a new service role for the function.&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%2Fffzusoxztpva7j1iywqw.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%2Fffzusoxztpva7j1iywqw.png" alt=" " width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Under &lt;strong&gt;Additional Configurations&lt;/strong&gt; enable tags and VPC, i am placing lambda function inside VPC to enable &lt;strong&gt;VPC flow logs&lt;/strong&gt; to monitor network traffic, which is critical for auditing and incident response.  Before moving onto choosing the security group create a new one in the EC2 console and then revert back here.&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%2Fh0rbcz8vcypf1ddwlp8z.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%2Fh0rbcz8vcypf1ddwlp8z.png" alt=" " width="800" height="291"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since this rotator Lambda function will be triggered by Secrets Manager, it won't be needing any inbound rules, as it's &lt;em&gt;event-driven&lt;/em&gt;. It still needs one outbound rule, to reach out to the Secrets manager endpoint via HTTPS protocol, as the traffic (&lt;em&gt;for HTTPS&lt;/em&gt;) will be routed through public internet via a NAT-gateway.&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%2Frszpj112jkjefh6w7eyh.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%2Frszpj112jkjefh6w7eyh.png" alt=" " width="800" height="230"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, reference the created SG under the lambda's configuration parameters drop-down and click on &lt;strong&gt;Create function&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%2Fujd1s45xvan8dlxzux2j.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%2Fujd1s45xvan8dlxzux2j.png" alt=" " width="800" height="375"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The rotator-lambda function is now created.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Configuration parameters - rotator lambda function:&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Use the python code &lt;a href="https://gist.github.com/WasimTTY/ff092402d09c56f6496b359036e42aa0" rel="noopener noreferrer"&gt;https://gist.github.com/WasimTTY/ff092402d09c56f6496b359036e42aa0&lt;/a&gt; from this gist to deploy the code onto the lambda function. &lt;/p&gt;

&lt;p&gt;This code was &lt;em&gt;modified&lt;/em&gt; to work with the CloudFront WAF configuration.&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%2Fjr7drrxdssrlqj3p0a9c.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%2Fjr7drrxdssrlqj3p0a9c.png" alt=" " width="800" height="375"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once pasted, click on &lt;em&gt;deploy&lt;/em&gt; to deploy the code to the lambda function. Additionally, this code does come with an external dependency, so create a lambda layer and attach it to the rotator-lambda function.&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%2F0uiug5ujninexarsz17r.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%2F0uiug5ujninexarsz17r.png" alt=" " width="800" height="206"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can find the &lt;em&gt;artifact&lt;/em&gt; in this repository: &lt;a href="https://github.com/aws-samples/amazon-cloudfront-waf-secretsmanager/tree/master/artifacts" rel="noopener noreferrer"&gt;https://github.com/aws-samples/amazon-cloudfront-waf-secretsmanager/tree/master/artifacts&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe3j5nwlpnqv3y8secvc6.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%2Fe3j5nwlpnqv3y8secvc6.png" alt=" " width="800" height="206"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, under the &lt;strong&gt;Configuration&lt;/strong&gt; tab choose the &lt;em&gt;Timeout&lt;/em&gt; for the rotator-lambda function, i have set it to &lt;em&gt;5 mins 3 sec&lt;/em&gt; with the memory and the Ephemeral storage left to defaults.&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%2Ftc7xp13drkve0vnhkp9q.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%2Ftc7xp13drkve0vnhkp9q.png" alt=" " width="800" height="403"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Take a note of the &lt;em&gt;Existing role&lt;/em&gt; attached to the lambda function. we will be needing it attach the policies to let the lambda function interact with the CloudFront distribution and WAF for rotating the &lt;strong&gt;secret-custom-header&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Navigate to the IAM console and under &lt;em&gt;Roles&lt;/em&gt; tab search for the service role created by the rotator-lambda function.&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%2Fjfbipixas66ac07gmis6.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%2Fjfbipixas66ac07gmis6.png" alt=" " width="800" height="403"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Create an inline policy for accessing the CloudFront distribution. I’ve provided only the permissions necessary for the service to function. This policy grants the rotator Lambda function the minimal privileges required to modify the Secret-Custom-Header on the CloudFront distribution.&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;{&lt;/span&gt;
    &lt;span class="s2"&gt;"Version"&lt;/span&gt;: &lt;span class="s2"&gt;"2012-10-17"&lt;/span&gt;,
    &lt;span class="s2"&gt;"Statement"&lt;/span&gt;: &lt;span class="o"&gt;[&lt;/span&gt;
        &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="s2"&gt;"Sid"&lt;/span&gt;: &lt;span class="s2"&gt;"VisualEditor0"&lt;/span&gt;,
            &lt;span class="s2"&gt;"Effect"&lt;/span&gt;: &lt;span class="s2"&gt;"Allow"&lt;/span&gt;,
            &lt;span class="s2"&gt;"Action"&lt;/span&gt;: &lt;span class="o"&gt;[&lt;/span&gt;
                &lt;span class="s2"&gt;"cloudfront:GetDistribution"&lt;/span&gt;,
                &lt;span class="s2"&gt;"cloudfront:UpdateDistribution"&lt;/span&gt;,
                &lt;span class="s2"&gt;"cloudfront:GetDistributionConfig"&lt;/span&gt;
            &lt;span class="o"&gt;]&lt;/span&gt;,
            &lt;span class="s2"&gt;"Resource"&lt;/span&gt;: &lt;span class="s2"&gt;"arn:aws:cloudfront::&amp;lt;AWS ACCOUNT ID&amp;gt;:distribution/E3R3ZKP77W4214"&lt;/span&gt;,
            &lt;span class="s2"&gt;"Condition"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="s2"&gt;"StringEquals"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
                    &lt;span class="s2"&gt;"aws:PrincipalArn"&lt;/span&gt;: &lt;span class="s2"&gt;"arn:aws:iam::&amp;lt;AWS ACCOUNT ID&amp;gt;:role/service-role/header-rotate-function-role-tinh6qnu"&lt;/span&gt;
                &lt;span class="o"&gt;}&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I’ve applied the same approach for the Secrets Manager, CloudFront WAF, and Lambda VPC policies as well, granting only the minimal permissions required for each service.&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;{&lt;/span&gt;
    &lt;span class="s2"&gt;"Version"&lt;/span&gt;: &lt;span class="s2"&gt;"2012-10-17"&lt;/span&gt;,
    &lt;span class="s2"&gt;"Statement"&lt;/span&gt;: &lt;span class="o"&gt;[&lt;/span&gt;
        &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="s2"&gt;"Sid"&lt;/span&gt;: &lt;span class="s2"&gt;"VisualEditor0"&lt;/span&gt;,
            &lt;span class="s2"&gt;"Effect"&lt;/span&gt;: &lt;span class="s2"&gt;"Allow"&lt;/span&gt;,
            &lt;span class="s2"&gt;"Action"&lt;/span&gt;: &lt;span class="o"&gt;[&lt;/span&gt;
                &lt;span class="s2"&gt;"secretsmanager:GetSecretValue"&lt;/span&gt;,
                &lt;span class="s2"&gt;"secretsmanager:DescribeSecret"&lt;/span&gt;,
                &lt;span class="s2"&gt;"secretsmanager:PutSecretValue"&lt;/span&gt;,
                &lt;span class="s2"&gt;"secretsmanager:UpdateSecretVersionStage"&lt;/span&gt;
            &lt;span class="o"&gt;]&lt;/span&gt;,
            &lt;span class="s2"&gt;"Resource"&lt;/span&gt;: &lt;span class="s2"&gt;"arn:aws:secretsmanager:us-east-2:&amp;lt;AWS ACCOUNT ID&amp;gt;:secret:Custom-Header-lQWRrQ"&lt;/span&gt;,
            &lt;span class="s2"&gt;"Condition"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="s2"&gt;"StringEquals"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
                    &lt;span class="s2"&gt;"aws:PrincipalArn"&lt;/span&gt;: &lt;span class="s2"&gt;"arn:aws:iam::&amp;lt;AWS ACCOUNT ID&amp;gt;:role/service-role/header-rotate-function-role-tinh6qnu"&lt;/span&gt;
                &lt;span class="o"&gt;}&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;,
        &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="s2"&gt;"Sid"&lt;/span&gt;: &lt;span class="s2"&gt;"VisualEditor1"&lt;/span&gt;,
            &lt;span class="s2"&gt;"Effect"&lt;/span&gt;: &lt;span class="s2"&gt;"Allow"&lt;/span&gt;,
            &lt;span class="s2"&gt;"Action"&lt;/span&gt;: &lt;span class="s2"&gt;"secretsmanager:GetRandomPassword"&lt;/span&gt;,
            &lt;span class="s2"&gt;"Resource"&lt;/span&gt;: &lt;span class="s2"&gt;"*"&lt;/span&gt;,
            &lt;span class="s2"&gt;"Condition"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="s2"&gt;"StringEquals"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
                    &lt;span class="s2"&gt;"aws:PrincipalArn"&lt;/span&gt;: &lt;span class="s2"&gt;"arn:aws:iam::&amp;lt;AWS ACCOUNT ID&amp;gt;:role/service-role/header-rotate-function-role-tinh6qnu"&lt;/span&gt;
                &lt;span class="o"&gt;}&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&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;  &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"Version"&lt;/span&gt;: &lt;span class="s2"&gt;"2012-10-17"&lt;/span&gt;,
    &lt;span class="s2"&gt;"Statement"&lt;/span&gt;: &lt;span class="o"&gt;[&lt;/span&gt;
        &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="s2"&gt;"Sid"&lt;/span&gt;: &lt;span class="s2"&gt;"VisualEditor0"&lt;/span&gt;,
            &lt;span class="s2"&gt;"Effect"&lt;/span&gt;: &lt;span class="s2"&gt;"Allow"&lt;/span&gt;,
            &lt;span class="s2"&gt;"Action"&lt;/span&gt;: &lt;span class="o"&gt;[&lt;/span&gt;
                &lt;span class="s2"&gt;"ec2:CreateNetworkInterface"&lt;/span&gt;,
                &lt;span class="s2"&gt;"ec2:DescribeNetworkInterfaces"&lt;/span&gt;,
                &lt;span class="s2"&gt;"ec2:DeleteNetworkInterface"&lt;/span&gt;,
                &lt;span class="s2"&gt;"ec2:UnassignPrivateIpAddresses"&lt;/span&gt;,
                &lt;span class="s2"&gt;"ec2:DescribeSubnets"&lt;/span&gt;,
                &lt;span class="s2"&gt;"ec2:AssignPrivateIpAddresses"&lt;/span&gt;
            &lt;span class="o"&gt;]&lt;/span&gt;,
            &lt;span class="s2"&gt;"Resource"&lt;/span&gt;: &lt;span class="s2"&gt;"*"&lt;/span&gt;,
            &lt;span class="s2"&gt;"Condition"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="s2"&gt;"StringEquals"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
                    &lt;span class="s2"&gt;"aws:PrincipalArn"&lt;/span&gt;: &lt;span class="s2"&gt;"arn:aws:iam::&amp;lt;AWS ACCOUNT ID&amp;gt;:role/service-role/header-rotate-function-role-tinh6qnu"&lt;/span&gt;
                &lt;span class="o"&gt;}&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&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;  &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"Version"&lt;/span&gt;: &lt;span class="s2"&gt;"2012-10-17"&lt;/span&gt;,
    &lt;span class="s2"&gt;"Statement"&lt;/span&gt;: &lt;span class="o"&gt;[&lt;/span&gt;
        &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="s2"&gt;"Sid"&lt;/span&gt;: &lt;span class="s2"&gt;"VisualEditor0"&lt;/span&gt;,
            &lt;span class="s2"&gt;"Effect"&lt;/span&gt;: &lt;span class="s2"&gt;"Allow"&lt;/span&gt;,
            &lt;span class="s2"&gt;"Action"&lt;/span&gt;: &lt;span class="o"&gt;[&lt;/span&gt;
                &lt;span class="s2"&gt;"wafv2:UpdateWebACL"&lt;/span&gt;,
                &lt;span class="s2"&gt;"wafv2:GetWebACL"&lt;/span&gt;
            &lt;span class="o"&gt;]&lt;/span&gt;,
            &lt;span class="s2"&gt;"Resource"&lt;/span&gt;: &lt;span class="o"&gt;[&lt;/span&gt;
                &lt;span class="s2"&gt;"arn:aws:wafv2:*:&amp;lt;AWS ACCOUNT ID&amp;gt;:*/regexpatternset/*/*"&lt;/span&gt;,
                &lt;span class="s2"&gt;"arn:aws:wafv2:*:&amp;lt;AWS ACCOUNT ID&amp;gt;:*/managedruleset/*/*"&lt;/span&gt;,
                &lt;span class="s2"&gt;"arn:aws:wafv2:*:&amp;lt;AWS ACCOUNT ID&amp;gt;:*/rulegroup/*/*"&lt;/span&gt;,
                &lt;span class="s2"&gt;"arn:aws:wafv2:us-east-1:&amp;lt;AWS ACCOUNT ID&amp;gt;:global/webacl/CreatedByCloudFront-4ffb490d-8e92-419b-a38b-017361fd80aa/a2512452-7f08-4591-bf1a-fec1923ac75a"&lt;/span&gt;,
                &lt;span class="s2"&gt;"arn:aws:wafv2:*:&amp;lt;AWS ACCOUNT ID&amp;gt;:*/ipset/*/*"&lt;/span&gt;
            &lt;span class="o"&gt;]&lt;/span&gt;,
            &lt;span class="s2"&gt;"Condition"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="s2"&gt;"StringEquals"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
                    &lt;span class="s2"&gt;"aws:PrincipalArn"&lt;/span&gt;: &lt;span class="s2"&gt;"arn:aws:iam::&amp;lt;AWS ACCOUNT ID&amp;gt;:role/service-role/header-rotate-function-role-tinh6qnu"&lt;/span&gt;
                &lt;span class="o"&gt;}&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The IAM policies crafted above are &lt;em&gt;identity-based policies&lt;/em&gt; scoped down to a specific IAM role using a &lt;em&gt;PrincipalArn&lt;/em&gt; condition, enforcing least privilege.&lt;/p&gt;

&lt;p&gt;The rotator-lambda function also needs a &lt;em&gt;resource-based policy&lt;/em&gt; in order for the Secrets manager to invoke the function. I have used AWS CLI to add this policy to the lambda function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  aws lambda add-permission &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--function-name&lt;/span&gt; secret-header-rotator-function &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--statement-id&lt;/span&gt; AllowSecretsManagerInvoke &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--action&lt;/span&gt; lambda:InvokeFunction &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--principal&lt;/span&gt; secretsmanager.amazonaws.com &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--source-account&lt;/span&gt; &amp;lt;AWS ACCOUNT ID&amp;gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--source-arn&lt;/span&gt; arn:aws:secretsmanager:us-east-2:&amp;lt;AWS ACCOUNT ID&amp;gt;:secret:Custom-Header-lQWRrQ

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

&lt;/div&gt;



&lt;p&gt;The resultant policy will look like this,&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;{&lt;/span&gt;
  &lt;span class="s2"&gt;"Version"&lt;/span&gt;: &lt;span class="s2"&gt;"2012-10-17"&lt;/span&gt;,
  &lt;span class="s2"&gt;"Id"&lt;/span&gt;: &lt;span class="s2"&gt;"default"&lt;/span&gt;,
  &lt;span class="s2"&gt;"Statement"&lt;/span&gt;: &lt;span class="o"&gt;[&lt;/span&gt;
    &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="s2"&gt;"Sid"&lt;/span&gt;: &lt;span class="s2"&gt;"AllowSecretsManagerInvoke"&lt;/span&gt;,
      &lt;span class="s2"&gt;"Effect"&lt;/span&gt;: &lt;span class="s2"&gt;"Allow"&lt;/span&gt;,
      &lt;span class="s2"&gt;"Principal"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="s2"&gt;"Service"&lt;/span&gt;: &lt;span class="s2"&gt;"secretsmanager.amazonaws.com"&lt;/span&gt;
      &lt;span class="o"&gt;}&lt;/span&gt;,
      &lt;span class="s2"&gt;"Action"&lt;/span&gt;: &lt;span class="s2"&gt;"lambda:InvokeFunction"&lt;/span&gt;,
      &lt;span class="s2"&gt;"Resource"&lt;/span&gt;: &lt;span class="s2"&gt;"arn:aws:lambda:us-east-2:&amp;lt;AWS ACCOUNT ID&amp;gt;:function:secret-header-rotator-function"&lt;/span&gt;,
      &lt;span class="s2"&gt;"Condition"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="s2"&gt;"StringEquals"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
          &lt;span class="s2"&gt;"AWS:SourceAccount"&lt;/span&gt;: &lt;span class="s2"&gt;"&amp;lt;AWS ACCOUNT ID&amp;gt;"&lt;/span&gt;
         &lt;span class="o"&gt;}&lt;/span&gt;,
        &lt;span class="s2"&gt;"ArnLike"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
          &lt;span class="s2"&gt;"AWS:SourceArn"&lt;/span&gt;: &lt;span class="s2"&gt;"arn:aws:secretsmanager:us-east-2:&amp;lt;AWS ACCOUNT ID&amp;gt;:secret:Custom-Header-lQWRrQ"&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
      &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
  &lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;The &lt;em&gt;resource-based policy&lt;/em&gt; above follows the &lt;strong&gt;Zero Trust&lt;/strong&gt; principle by  minimizing trust, ensuring access is explicitly verified through conditions like Source Account and Source ARN before granting permissions.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Configure secrets manager - resource policy:&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now, navigate to the Secrets manager service console and under the &lt;strong&gt;Resource permissions&lt;/strong&gt; paste the &lt;em&gt;resource-based policy&lt;/em&gt; for invoking the rotator-lambda function.&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;{&lt;/span&gt;
  &lt;span class="s2"&gt;"Version"&lt;/span&gt; : &lt;span class="s2"&gt;"2012-10-17"&lt;/span&gt;,
  &lt;span class="s2"&gt;"Statement"&lt;/span&gt; : &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"Sid"&lt;/span&gt; : &lt;span class="s2"&gt;"AllowLambdaAccess"&lt;/span&gt;,
    &lt;span class="s2"&gt;"Effect"&lt;/span&gt; : &lt;span class="s2"&gt;"Allow"&lt;/span&gt;,
    &lt;span class="s2"&gt;"Principal"&lt;/span&gt; : &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="s2"&gt;"AWS"&lt;/span&gt; : &lt;span class="s2"&gt;"arn:aws:iam::&amp;lt;AWS ACCOUNT ID&amp;gt;:role/service-role/header-rotate-function-role-tinh6qnu"&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;,
    &lt;span class="s2"&gt;"Action"&lt;/span&gt; : &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"secretsmanager:GetSecretValue"&lt;/span&gt;, &lt;span class="s2"&gt;"secretsmanager:PutSecretValue"&lt;/span&gt;, &lt;span class="s2"&gt;"secretsmanager:UpdateSecretVersionStage"&lt;/span&gt;, &lt;span class="s2"&gt;"secretsmanager:DescribeSecret"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;,
    &lt;span class="s2"&gt;"Resource"&lt;/span&gt; : &lt;span class="s2"&gt;"arn:aws:secretsmanager:us-east-2:&amp;lt;AWS ACCOUNT ID&amp;gt;:secret:Custom-Header-lQWRrQ"&lt;/span&gt;,
    &lt;span class="s2"&gt;"Condition"&lt;/span&gt; : &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="s2"&gt;"StringEquals"&lt;/span&gt; : &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="s2"&gt;"aws:ResourceTag/Environment"&lt;/span&gt; : &lt;span class="s2"&gt;"production"&lt;/span&gt;,
        &lt;span class="s2"&gt;"aws:SourceAccount"&lt;/span&gt; : &lt;span class="s2"&gt;"&amp;lt;AWS ACCOUNT ID&amp;gt;"&lt;/span&gt;,
        &lt;span class="s2"&gt;"aws:SourceArn"&lt;/span&gt; : &lt;span class="s2"&gt;"arn:aws:lambda:us-east-2:&amp;lt;AWS ACCOUNT ID&amp;gt;:function:secret-header-rotator-function"&lt;/span&gt;
      &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;This policy also aligns with the &lt;strong&gt;Zero Trust&lt;/strong&gt; principle, Even though the request is coming from the rotator-lambda function (which could be considered a trusted service), the policy does not allow access by default. It enforces strict conditions including &lt;strong&gt;ABAC&lt;/strong&gt; (&lt;em&gt;Attribute Based Access Control&lt;/em&gt;) using resource tags to validate the request’s origin and ensure the caller is explicitly authorized.&lt;/p&gt;

&lt;p&gt;Taking this approach tightens security by enforcing stringent, context-aware access controls.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Create a Custom Header rule in AWS WAF
&lt;/h2&gt;

&lt;p&gt;Navigate to the WAF &amp;amp; Shield service console and look-up for &lt;em&gt;Web ACLs&lt;/em&gt; created under the &lt;strong&gt;Global(CloudFront)&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%2Fz78rwe3akbm7rkjwbub0.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%2Fz78rwe3akbm7rkjwbub0.png" alt=" " width="800" height="164"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Create a new rule named &lt;strong&gt;XOriginVerify&lt;/strong&gt;, and configure the same &lt;em&gt;secret-custom-header&lt;/em&gt; value that was set in the CloudFront distribution and the Secrets manager.&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%2Fyeg5fnrkxkcmhn02ntoe.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%2Fyeg5fnrkxkcmhn02ntoe.png" alt=" " width="800" height="248"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The rotator-lambda function looks &lt;strong&gt;Explicitly&lt;/strong&gt; for this rule name as it is hard-coded in the python code. You can modify it according to your use case.&lt;/p&gt;

&lt;p&gt;Once the rotator-lambda function updates the &lt;em&gt;secret-custom-header&lt;/em&gt; in both the CloudFront distribution and in WAF, it tests the secret by pinging your domain-name, in this case the request has to propagate through the CloudFront, and WAF will let the request through only if that header matches, it should result in &lt;strong&gt;200 OK&lt;/strong&gt;. You have to check CloudWatch logs for this.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Spin up ECS and RDS
&lt;/h2&gt;

&lt;p&gt;You can refer to &lt;a href="https://dev.to/ittrident/deploying-fider-on-aws-ecs-a-step-by-step-guide-to-deploy-a-feedback-platform-jl4#install-aws-cli"&gt;this&lt;/a&gt; guide for spinning up the ECS and RDS infrastructure.&lt;/p&gt;

&lt;h2&gt;
  
  
  9. Monitor CloudFront and WAF logs
&lt;/h2&gt;

&lt;p&gt;Monitoring CloudFront and AWS WAF logs is a crucial part of &lt;em&gt;incident response&lt;/em&gt; in any secure AWS setup, especially when you're running a public-facing app.&lt;/p&gt;

&lt;p&gt;You need visibility into what’s happening at your edge and app layers. That’s where CloudFront and AWS WAF logs come in.&lt;/p&gt;

&lt;p&gt;In the AWS management console under the region drop-down choose &lt;strong&gt;us-east-1&lt;/strong&gt;, this is where CloudFront delivers logs to in the CloudWatch log-group.&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%2Fxts20wrjkfxbm7hm33ty.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%2Fxts20wrjkfxbm7hm33ty.png" alt=" " width="800" height="133"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Each log contains information such as the time the request was received, the processing time, request paths,client-IP and server responses. You can use these access logs to analyze response times and to troubleshoot 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%2F47yb0j5703rt4keu1paa.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%2F47yb0j5703rt4keu1paa.png" alt=" " width="800" height="383"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When you're absolutely done observing the false positives for WAF rules, navigate to the WAF console and adjust the actions for the rules according to your use case. You can choose whether to set actions such as Allow, Block, Captcha, or Count based on your specific needs. It ultimately narrows down to your requirements and the level of protection you wish to enforce.&lt;/p&gt;

&lt;p&gt;It’s important to note that when you set a rule group action to 'Count,' it will override any Allow, Block, or Captcha actions that were originally specified for the rules within that group. As a result, all rules in the group will only count matching requests, regardless of their original action.&lt;/p&gt;

&lt;p&gt;By default, the action is set to 'Count' during testing and monitoring of the rule group’s behavior, which allows you to observe how the rules perform before deploying them to production.&lt;/p&gt;

&lt;p&gt;AWS WAF now supports sending logs to CloudWatch logs, Hover over to the &lt;strong&gt;Logging and metrics&lt;/strong&gt; tab and  click on &lt;strong&gt;Enable&lt;/strong&gt; drop-down and choose &lt;strong&gt;Logging destination&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%2Ftma12clyqg7150d33hs4.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%2Ftma12clyqg7150d33hs4.png" alt=" " width="800" height="376"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Choose the CloudWatch logs log group as destination and click on save,&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%2Fde6x9r78i2nbj9gjg7ja.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%2Fde6x9r78i2nbj9gjg7ja.png" alt=" " width="800" height="376"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Logging is now enabled for AWS WAF.&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%2Fas2iimfg50rf0uo9b5ag.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%2Fas2iimfg50rf0uo9b5ag.png" alt=" " width="800" height="376"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Hover over to the &lt;strong&gt;Sampled requests&lt;/strong&gt; tab to help you analyze how your AWS WAF rules are &lt;strong&gt;evaluating&lt;/strong&gt; traffic. Each sampled request shows details like the &lt;strong&gt;IP address&lt;/strong&gt;, &lt;strong&gt;headers&lt;/strong&gt;, &lt;strong&gt;URI&lt;/strong&gt;, &lt;strong&gt;country&lt;/strong&gt;, and &lt;strong&gt;rule evaluation outcome&lt;/strong&gt;. Sampled requests are &lt;strong&gt;free&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%2Fo9zk38t9odiyq71ln5e7.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%2Fo9zk38t9odiyq71ln5e7.png" alt=" " width="800" height="376"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxn4k81k3rw6gl1s4l6py.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%2Fxn4k81k3rw6gl1s4l6py.png" alt=" " width="800" height="376"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In addition to this, you can also use the default queries provided by WAF to query the number of requests that come from an IP address. Under the &lt;strong&gt;Cloudwatch logs insight&lt;/strong&gt; tab in the WAF 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%2Fkxx8rmp2iredw57c0sox.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%2Fkxx8rmp2iredw57c0sox.png" alt=" " width="800" height="387"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  10. Do You Really Need Zero Trust for Every App
&lt;/h2&gt;

&lt;p&gt;Before slapping on Zero Trust policies like they’re the new fad, take a step back and do the &lt;strong&gt;threat modelling&lt;/strong&gt; first.&lt;/p&gt;

&lt;p&gt;That’s the only way to know whether Zero Trust is even the right fit for your app.&lt;/p&gt;

&lt;p&gt;Threat modelling is like a security audit before you go shopping for locks and alarms. It forces you to ask:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;What exactly are you protecting ?&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Who would want to break in ?&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;How might they try to do it ?&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That last part? That’s basically &lt;strong&gt;recon&lt;/strong&gt; and attackers do it all the time. They scan your public IPs, poke at your APIs, look for leaky S3 buckets or overly permissive roles. So when you threat model, you're doing a defensive version of recon: identifying what an attacker might see and how they'd try to exploit it.&lt;/p&gt;

&lt;p&gt;Threat modeling helps you spot risks like this, Once you know that, you can set up your Zero Trust stuff, like least privilege access, network segmentation, and strong authentication all tailored to the actual threats, not just random paranoia.&lt;/p&gt;

&lt;p&gt;Even something as focused as the rotator Lambda function, which runs every 4 hours to manage the secret header, carries real risk. Its footprint may be small, but its role is critical. In cloud environments, even well-scoped serverless functions can become attack vectors if misconfigured or exposed.&lt;/p&gt;

&lt;p&gt;This strict posture isn't just theoretical. Tools like &lt;a href="https://github.com/OtterHacker/AWSRedirector" rel="noopener noreferrer"&gt;AWSRedirector&lt;/a&gt; demonstrates how serverless components can be exploited in real-world attacks . That's why we lock down even the smallest moving parts.&lt;/p&gt;

&lt;p&gt;Without threat modeling, you’d be like, ‘Oh, I’ll just block everything!’ but that’s like building a fortress with no doors. You gotta know who’s allowed in, and under what conditions. That’s how Zero Trust works it’s not about ‘trust no one’ for the sake of it, it’s about verifying every request based on real risks. You should understand the balance between security and usability.&lt;/p&gt;

&lt;p&gt;If you’re curious about how AWS approaches Zero Trust in detail, you can check out their &lt;a href="https://aws.amazon.com/security/zero-trust/" rel="noopener noreferrer"&gt;Zero Trust Security Model&lt;/a&gt; and &lt;a href="https://docs.aws.amazon.com/prescriptive-guidance/latest/strategy-zero-trust-architecture/zero-trust-principles.html" rel="noopener noreferrer"&gt;AWS Zero Trust Architecture principles&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  11. Is CloudFront Truly Zero Trust Compliant
&lt;/h2&gt;

&lt;p&gt;Zero Trust architectures are &lt;strong&gt;identity-centric&lt;/strong&gt;, requiring per-request authentication and authorization before traffic reaches the application. Using CloudFront means your application is publicly addressable on the internet, even if access is restricted with WAF, signed URLs, or custom headers.  This introduces a public entry point, contrary to Zero Trust principles of default deny and private access.&lt;/p&gt;

&lt;p&gt;CloudFront does not support integration with &lt;em&gt;IAM Identity Center, Azure AD, Okta&lt;/em&gt;, or other identity providers (IdPs) to enforce access control. While it offers IP-level and path-level access logs, it lacks identity-based audit trails, making per-user auditing and continuous trust evaluation impossible. Additionally, it does not inspect or enforce device posture or context, meaning it cannot verify whether requests originate from secure, approved environments. &lt;/p&gt;

&lt;p&gt;If you're looking to implement true Zero Trust at the edge, &lt;strong&gt;Cloudflare&lt;/strong&gt; and &lt;strong&gt;Akamai (EAA)&lt;/strong&gt; offer the most complete solutions today. Others like Fastly are catching up, but platforms like AWS CloudFront, Azure Front Door, and Google Cloud CDN still fall short of Zero Trust standards.&lt;/p&gt;

&lt;h2&gt;
  
  
  12. Deciding on Enterprise Grade Deployment for Fider
&lt;/h2&gt;

&lt;p&gt;Fider is just a feedback platform, and most of what it contains are suggestions and feedback. We don’t store any sensitive Personally Identifiable Information &lt;strong&gt;(PII)&lt;/strong&gt; like Social Security Numbers or financial data. Even in the event of a compromise, the risk is minimal, typically limited to names and email addresses. However, if you're using Fider internally within your organization, the risk could be higher, as internal feedback may include more sensitive or proprietary information&lt;/p&gt;

&lt;p&gt;Given Fider’s risk profile, a Lambda-based secret header rotator backed by Secrets Manager offers a strong security baseline. This level of protection is more than sufficient if you’re using Fider to collect public feedback and suggestions without overengineering.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://aws.amazon.com/verified-access/" rel="noopener noreferrer"&gt;AWS Verified Access&lt;/a&gt; is indeed the right choice if you’re planning to host everything within AWS and require enterprise-level security controls. But, it’s generally overkill unless you have tighter access control policies in your enterprise setup, and costs more. You are also charged per GB for data processed by Verified Access. You can calculate the estimated cost here &lt;a href="https://aws.amazon.com/verified-access/pricing/" rel="noopener noreferrer"&gt;AWS Verified Access Pricing&lt;/a&gt;. It functions more as an &lt;strong&gt;access control solution&lt;/strong&gt; than as traditional &lt;strong&gt;VPN tunnels&lt;/strong&gt;. It only works with AWS hosted systems.&lt;/p&gt;

&lt;p&gt;If you are curious about this setup then you can check this out &lt;a href="https://pages.nist.gov/zero-trust-architecture/VolumeC/HowTo-E4B5.html" rel="noopener noreferrer"&gt;https://pages.nist.gov/zero-trust-architecture/VolumeC/HowTo-E4B5.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.cloudflare.com/en-gb/zero-trust/products/access/" rel="noopener noreferrer"&gt;Cloudflare Zero Trust&lt;/a&gt; is the sweet spot and comes out as the better choice when considering both &lt;strong&gt;cost&lt;/strong&gt; and &lt;strong&gt;performance&lt;/strong&gt;.  It supports &lt;strong&gt;edge services&lt;/strong&gt;, &lt;strong&gt;DNS&lt;/strong&gt;, and &lt;strong&gt;mTLS&lt;/strong&gt; out of the box, providing enhanced security, performance, and seamless encryption with Cloudflare’s global network.You can check out its pricing &lt;a href="https://www.cloudflare.com/en-gb/plans/zero-trust-services/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  13. Caveats to consider
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;VPC Endpoints&lt;/strong&gt;:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can still choose to go with VPC interface endpoints (Powered by AWS PrivateLink) if you haven't integrated external OAuth providers (like Google, GitHub, etc.) because these services are not available via AWS PrivateLink.&lt;/p&gt;

&lt;p&gt;When you configure OAuth providers with Fider, the authentication requests go to the OAuth provider's endpoints over the internet. This involves external APIs that cannot be routed through VPC Interface Endpoints directly, since these OAuth services are external to your AWS infrastructure and not hosted within your VPC or private AWS environment.&lt;/p&gt;

&lt;p&gt;It will also cost you more than the NAT gateway, you can checkout this &lt;a href="https://repost.aws/questions/QUmfyiKedjTd225PQS7MlHQQ/vpc-nat-gateway-vs-vpc-endpoint-pricing" rel="noopener noreferrer"&gt;post&lt;/a&gt; from the official AWS forum for even more clarification. Only choose this method if you consider security as paramount.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;⚠️ Network Isolation ≠ Identity Validation&lt;/strong&gt;:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Using a VPC origin does make the app private, but it does not guarantee identity-based access control. CloudFront doesn't authenticate requesters the way solutions like Cloudflare Zero Trust do, leaving the application vulnerable without proper identity verification.&lt;/p&gt;

&lt;h2&gt;
  
  
  14. Final thoughts
&lt;/h2&gt;

&lt;p&gt;Let’s be real no system is made of &lt;em&gt;adamantium&lt;/em&gt;. Given enough time, resources, or misconfigurations, even the most locked-down architecture can be poked at. But this setup doesn’t aim for invincibility, it aims to minimize exposure, limit blast radius, and make exploitation significantly harder and riskier.&lt;/p&gt;

&lt;p&gt;This architecture trades convenience for control, reducing your attack surface, slowing down adversaries, and giving you time and telemetry to respond. &lt;/p&gt;

</description>
      <category>devops</category>
      <category>security</category>
      <category>cloud</category>
      <category>aws</category>
    </item>
    <item>
      <title>Deploying Fider on AWS ECS: A Step-by-Step Guide to Deploy a Feedback Platform</title>
      <dc:creator>Mohamed Wasim</dc:creator>
      <pubDate>Tue, 05 Nov 2024 12:12:17 +0000</pubDate>
      <link>https://dev.to/ittrident/deploying-fider-on-aws-ecs-a-step-by-step-guide-to-deploy-a-feedback-platform-jl4</link>
      <guid>https://dev.to/ittrident/deploying-fider-on-aws-ecs-a-step-by-step-guide-to-deploy-a-feedback-platform-jl4</guid>
      <description>&lt;p&gt;This guide provides detailed instructions for deploying Fider on AWS ECS, with tasks running inside a private subnet and an internet-facing Application Load Balancer (ALB) set up as a custom origin for CloudFront(Cloudfront + WAF).&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This is a detailed article, so buckle up! Since this setup closely mimics a production deployment and let’s be real, deploying Fider on AWS isn’t an easy feat, you’ll want to follow along carefully.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  AWS services used
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Elastic container registry(ECR)&lt;/li&gt;
&lt;li&gt;Elastic container service(ECS Fargate launch type)&lt;/li&gt;
&lt;li&gt;Application Load Balancer(ALB)&lt;/li&gt;
&lt;li&gt;Relational Database Service(RDS)&lt;/li&gt;
&lt;li&gt;Systems Manager Parameter Store&lt;/li&gt;
&lt;li&gt;Simple Storage Service(S3)&lt;/li&gt;
&lt;li&gt;Simple Email Service(SES)&lt;/li&gt;
&lt;li&gt;Cloudfront(CDN)&lt;/li&gt;
&lt;li&gt;Web Application Firewall(WAF)&lt;/li&gt;
&lt;li&gt;Key Management Service(KMS)&lt;/li&gt;
&lt;li&gt;Amazon Certificate Manager (ACM)&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Table of Contents
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;What is Fider ?&lt;/li&gt;
&lt;li&gt;Fider's Tech Stack&lt;/li&gt;
&lt;li&gt;Pre-flight Checks&lt;/li&gt;
&lt;li&gt;Install AWS CLI&lt;/li&gt;
&lt;li&gt;Push the Docker Images to ECR&lt;/li&gt;
&lt;li&gt;Create a VPC&lt;/li&gt;
&lt;li&gt;Create Subnet Group for RDS&lt;/li&gt;
&lt;li&gt;Create an RDS PostgreSQL DB&lt;/li&gt;
&lt;li&gt;Create Parameters in Systems Manager Parameter Store&lt;/li&gt;
&lt;li&gt;Create an IAM User&lt;/li&gt;
&lt;li&gt;Create an S3 Bucket&lt;/li&gt;
&lt;li&gt;Create an Identity in SES&lt;/li&gt;
&lt;li&gt;Why Use ECS Instead of EC2&lt;/li&gt;
&lt;li&gt;Create an ECS Cluster&lt;/li&gt;
&lt;li&gt;Create Task Definition&lt;/li&gt;
&lt;li&gt;Create an ECS Service&lt;/li&gt;
&lt;li&gt;Create CloudFront Distribution with WAF&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What is Fider ?
&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%2Fqncq40lxhg0i4zuwjqmj.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%2Fqncq40lxhg0i4zuwjqmj.png" alt="Image description" width="256" height="256"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Okay, okay, so, listen up! Fider is like this thing where people can go and, like, share their opinions on stuff, right? Imagine you’ve got a big ol' bag of potato chips don’t ask me why I’m talking about chips, it just seems right and you wanna know what everyone thinks about the flavor. Fider’s the place where people can give feedback on stuff like that, but, you know, for real stuff like websites and apps.&lt;/p&gt;

&lt;p&gt;So, you go there, type up your thoughts, and boom other people can vote on whether they agree or disagree, like they're choosing between watching Family Guy or some other dumb show. But here’s the kicker, Fider lets people actually vote and prioritize the feedback they care about! &lt;/p&gt;

&lt;p&gt;So, yeah, that’s Fider. Kinda like a big opinion poll but with a lot more techy stuff behind it. And you can totally use it for, you know, not chips...&lt;/p&gt;

&lt;p&gt;You can checkout the Fider's official page for more details: &lt;a href="https://fider.io/" rel="noopener noreferrer"&gt;https://fider.io/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This guide focuses on deploying Fider on AWS, but you can also self-host it on other platforms like &lt;strong&gt;Heroku&lt;/strong&gt;, &lt;strong&gt;Azure&lt;/strong&gt;, or your own servers. For more details, check out the &lt;a href="https://docs.fider.io/self-hosted/" rel="noopener noreferrer"&gt;official Fider self-hosting documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fider's Tech Stack
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Backend&lt;/strong&gt;: Go (Golang), with custom HTTP handling.&lt;br&gt;
&lt;strong&gt;Frontend&lt;/strong&gt;: React, using SCSS(Sassy CSS) for styling.&lt;br&gt;
&lt;strong&gt;Database&lt;/strong&gt;: PostgreSQL.&lt;br&gt;
&lt;strong&gt;Authentication&lt;/strong&gt;: OAuth 2.0.&lt;br&gt;
&lt;strong&gt;Background Jobs&lt;/strong&gt;: Go routines.&lt;br&gt;
&lt;strong&gt;Cloud Storage and Email Integration&lt;/strong&gt;: S3 for blob storage and SES for email notifications.&lt;/p&gt;
&lt;h3&gt;
  
  
  Pre-flight checks
&lt;/h3&gt;

&lt;p&gt;Before starting, ensure that you have an AWS account with access to the free tier or a valid payment method on file.&lt;/p&gt;
&lt;h3&gt;
  
  
  Install AWS CLI
&lt;/h3&gt;
&lt;h4&gt;
  
  
  Step 1:
&lt;/h4&gt;

&lt;p&gt;You need the awscli package installed for pushing the images built locally to Elastic Container Registry(ECR).&lt;/p&gt;
&lt;h4&gt;
  
  
  Step 2:
&lt;/h4&gt;

&lt;p&gt;If you use ubuntu distro, Check the snap version by using the following command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  snap version
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run the following snap install command for the AWS CLI.&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;sudo &lt;/span&gt;snap &lt;span class="nb"&gt;install &lt;/span&gt;aws-cli &lt;span class="nt"&gt;--classic&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After the installation is complete, verify the installation by using the command,&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  aws &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;/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%2F7jbmqiyzkt4ubzkhhnnk.jpeg" 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%2F7jbmqiyzkt4ubzkhhnnk.jpeg" alt="Image description" width="800" height="82"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With Snap package you always get the latest version of AWS CLI as snap packages automatically refresh.&lt;/p&gt;

&lt;p&gt;For command line installer check out the documentation: &lt;a href="https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html" rel="noopener noreferrer"&gt;https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 4: Configure AWS CLI
&lt;/h4&gt;

&lt;p&gt;Use this command to configure the AWS CLI.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  aws configure
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once configured, verify your AWS CLI credentials using the following command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  aws configure list 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Step 5: Pull official docker images from DockerHub
&lt;/h4&gt;

&lt;p&gt;Pull the official docker image for Fider from the DockerHub registry at: &lt;a href="https://hub.docker.com/r/getfider/fider/tags" rel="noopener noreferrer"&gt;https://hub.docker.com/r/getfider/fider/tags&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Use the docker pull command to pull the image from the DockerHub.&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 getfider/fider:main
&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%2F00pmk1k3mu2w4b2vrjy5.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%2F00pmk1k3mu2w4b2vrjy5.png" alt="Image description" width="735" height="342"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Check if the docker image is available in your instance.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  docker images 
&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%2Fx0jdzadv7bo7hjg5ogwh.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%2Fx0jdzadv7bo7hjg5ogwh.png" alt="Image description" width="660" height="81"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Push the Docker images to ECR
&lt;/h2&gt;

&lt;h4&gt;
  
  
  Step 1: Visit the ECR console
&lt;/h4&gt;

&lt;p&gt;Create a public or private repository based on your use case by clicking on the create repository prompt in the top right corner.&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%2F9eyde058uerav0n99lgd.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%2F9eyde058uerav0n99lgd.png" alt="Image description" width="800" height="731"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Name the repo getfider/fider&lt;/p&gt;

&lt;p&gt;Check if the repository is created.&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%2F9dfquszu9rzm03ewfko5.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%2F9dfquszu9rzm03ewfko5.png" alt="Image description" width="800" height="96"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 2: View the push commands
&lt;/h4&gt;

&lt;p&gt;View the push commands by clicking on the respective repository.&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%2Frgngc3atvaf2f90nk060.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%2Frgngc3atvaf2f90nk060.png" alt="Image description" width="800" height="509"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Use the AWS CLI to retrieve an authentication token and authenticate your Docker client to your registry. &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%2Fsqtyhl7vpvh8efzwbex8.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%2Fsqtyhl7vpvh8efzwbex8.png" alt="Image description" width="800" height="130"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;tag your pulled image so you can push the image to this repository.&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%2F77f4qya27o96gvc094tm.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%2F77f4qya27o96gvc094tm.png" alt="Image description" width="800" height="29"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Run the following command to push this image to your newly created AWS repository.&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%2Fs9k0fysnztbok4zdr20i.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%2Fs9k0fysnztbok4zdr20i.png" alt="Image description" width="800" height="230"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The docker images should now be available in the repository you created.&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%2F2nup2dse4a0hp1ztz6k1.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%2F2nup2dse4a0hp1ztz6k1.png" alt="Image description" width="800" height="176"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Create a VPC
&lt;/h2&gt;

&lt;h4&gt;
  
  
  Step 1: Create a standalone VPC for deploying Fider application
&lt;/h4&gt;

&lt;p&gt;Login to the management console then navigate to the VPC service console and click on Create VPC.&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%2Froxm3lyorwu7zu80mq5s.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%2Froxm3lyorwu7zu80mq5s.png" alt="Image description" width="800" height="762"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Create VPC only for now, you can create public and private subnets manually and can attach these subnets to particular route tables.&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%2Fqh5fyz3wim4vixqjwine.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%2Fqh5fyz3wim4vixqjwine.png" alt="Image description" width="800" height="371"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A route table will also get created with route to local traffic.&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%2Fem8feb67ocak9wtfdx1h.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%2Fem8feb67ocak9wtfdx1h.png" alt="Image description" width="800" height="169"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 2: Create an Internet gateway
&lt;/h4&gt;

&lt;p&gt;Create an IGW and attach it to the VPC you created.&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%2F90w855bq467tu2jl89pv.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%2F90w855bq467tu2jl89pv.png" alt="Image description" width="800" height="195"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj4nr8oyesbvqf7gxiqw7.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%2Fj4nr8oyesbvqf7gxiqw7.png" alt="Image description" width="800" height="70"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The IGW is now attached to the VPC.&lt;/p&gt;

&lt;p&gt;Associate the IGW with the route table by clicking on edit routes.&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%2Fvr0axktlpivlgst60ns9.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%2Fvr0axktlpivlgst60ns9.png" alt="Image description" width="800" height="233"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By clicking on Add route attach the the IGW to the route table.&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%2Fq2y0tjik0idto209wagl.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%2Fq2y0tjik0idto209wagl.png" alt="Image description" width="800" height="173"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The route table now has a route to the IGW, which means the subnets associated with this route table can have Internet access.&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%2Ff3dw4b0csbq1alhnbp4t.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%2Ff3dw4b0csbq1alhnbp4t.png" alt="Image description" width="800" height="248"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 3: Create Public Subnets
&lt;/h4&gt;

&lt;p&gt;Create at least two public subnets, so that the Application Load Balancer can reside in it.&lt;/p&gt;

&lt;p&gt;Navigate to the VPC Console, under the subnets section create two public subnets.&lt;/p&gt;

&lt;p&gt;Decide the subnet CIDR range and provision it accordingly.&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%2F5e552dqpozf14a3mfvwu.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%2F5e552dqpozf14a3mfvwu.png" alt="Image description" width="800" height="134"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Associate the public subnets to the public route table where the IGW is also attached.&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%2F4eax24jnq32dxuiwcul8.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%2F4eax24jnq32dxuiwcul8.png" alt="Image description" width="800" height="352"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 4: Create Private Subnets
&lt;/h4&gt;

&lt;p&gt;Create at least two private subnets, so that your ECS tasks and RDS can reside in it.&lt;/p&gt;

&lt;p&gt;Navigate to the VPC Console, under the subnets section create at least two private subnets.&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%2Fh4dr0dgs1elkb9kw98tw.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%2Fh4dr0dgs1elkb9kw98tw.png" alt="Image description" width="800" height="129"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Under the actions dropdown choose Edit subnet settings and uncheck the Enable auto-assign public IPv4 address.&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%2Fywlviu2piima439ztuqr.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%2Fywlviu2piima439ztuqr.png" alt="Image description" width="800" height="395"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 5: Create a Private route table
&lt;/h4&gt;

&lt;p&gt;Create a private route table in the VPC console under the route tables section to route traffic locally.&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%2Fpvdrqs25r44z85qpsgyf.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%2Fpvdrqs25r44z85qpsgyf.png" alt="Image description" width="800" height="622"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpiio8rnesf2fi6ylt1u2.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%2Fpiio8rnesf2fi6ylt1u2.png" alt="Image description" width="800" height="108"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Associate the private subnets to the private route table, like how you did with the public route table, and follow the same steps.&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 6: Create an Elastic-ip
&lt;/h4&gt;

&lt;p&gt;Allocate an elastic-ip address and attach it with the NAT gateway.&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%2Fc5wdrvrmy16qljuqve6o.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%2Fc5wdrvrmy16qljuqve6o.png" alt="Image description" width="800" height="110"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click on Allocate Elastic IP address&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%2Fyawnn5qhbiif4m19zqwc.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%2Fyawnn5qhbiif4m19zqwc.png" alt="Image description" width="800" height="323"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;From public ipv4 address pool, choose Amazon's pool of ipv4 address and click Allocate&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%2Fttrndfcsrujtgi1b0o6y.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%2Fttrndfcsrujtgi1b0o6y.png" alt="Image description" width="800" height="162"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You should now be able to check the static Elastic IP allocated to you. It can now be attached to the NAT gateway.&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 7: Create NAT gateway
&lt;/h4&gt;

&lt;p&gt;Create a NAT gateway to allow tasks in the private subnet to connect to the internet.&lt;/p&gt;

&lt;p&gt;Navigate to NAT gateway section under the VPC console and create a NAT gateway.&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%2Ffyljs1pp2o8ldo9m68t4.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%2Ffyljs1pp2o8ldo9m68t4.png" alt="Image description" width="800" height="369"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Associate it with a public subnet and also associate an Elastic IP address to it.&lt;/p&gt;

&lt;p&gt;Set the connectivity type to public.&lt;/p&gt;

&lt;p&gt;Attach the NAT gateway to the private route table that has three private subnets associated with it.&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%2Fx7huv836zbdrcvftub75.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%2Fx7huv836zbdrcvftub75.png" alt="Image description" width="800" height="179"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The private route table now has a route to the NAT gateway.&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%2Fgb534p084qe7wdx438n7.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%2Fgb534p084qe7wdx438n7.png" alt="Image description" width="800" height="250"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Your VPC lineage should now 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%2Fhdeitwgde21em5p7v55n.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%2Fhdeitwgde21em5p7v55n.png" alt="Image description" width="800" height="256"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Create Subnet group for RDS
&lt;/h2&gt;

&lt;p&gt;Navigate to the RDS sevice console and click on subnet groups,&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%2Fth1ldgbcg6n174h1yzn7.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%2Fth1ldgbcg6n174h1yzn7.png" alt="Image description" width="800" height="334"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click on create DB subnet group&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%2F9k1c9rjle20gsdmme2y6.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%2F9k1c9rjle20gsdmme2y6.png" alt="Image description" width="800" height="112"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Fill out the subnet group details and add subnets and choose the availability zones(azs) accordingly(private subnets).&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%2Fmsi5lhq4vpxg3nnt0q8u.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%2Fmsi5lhq4vpxg3nnt0q8u.png" alt="Image description" width="800" height="370"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you choose to launch multi-AZ DB clusters, you must select 3 subnets in three different availability zones.&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%2Fig839buhfkdpdwp94ah3.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%2Fig839buhfkdpdwp94ah3.png" alt="Image description" width="800" height="156"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click on create,your subnet group will now be available in the subnet groups dashboard.&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%2F33ut1qdvtjrgz29u29kb.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%2F33ut1qdvtjrgz29u29kb.png" alt="Image description" width="800" height="149"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Create an RDS PostreSQL DB
&lt;/h2&gt;

&lt;p&gt;Navigate to the RDS service console from the management console and click on DB instance, to create an RDS DB instance.&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%2Fy1apv0dn4i7kmhdb4cpg.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%2Fy1apv0dn4i7kmhdb4cpg.png" alt="Image description" width="800" height="149"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click on create database&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%2F2kkcxc3bz2sxqs1zqw1u.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%2F2kkcxc3bz2sxqs1zqw1u.png" alt="Image description" width="800" height="380"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Choose standard create and choose PostgreSQL as engine type.&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%2Ffsdg2etmmj2mplzlv1tx.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%2Ffsdg2etmmj2mplzlv1tx.png" alt="Image description" width="800" height="380"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Choose the latest PostgreSQL engine version and for this guide i choose to go with single DB instance under the availability and durability section.&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%2Fgz3efahsbbu9jpkyiqq4.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%2Fgz3efahsbbu9jpkyiqq4.png" alt="Image description" width="800" height="312"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Under credential settings provide postgres as master user name and choose self managed to use your custom password for authenticating with RDS.&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%2Feokhchvdjt9oxhoeesm6.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%2Feokhchvdjt9oxhoeesm6.png" alt="Image description" width="800" height="372"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For this guide, I chose to go with Burstable classes, depending on your use case you can fine tune the DB instance class. Storage will be left as default, unless you choose to customise it.&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%2Fzo0e8nrwa020loz0ptom.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%2Fzo0e8nrwa020loz0ptom.png" alt="Image description" width="800" height="372"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Under connectivity section, choose the VPC that you created(fider-vpc) and the subnet group you created previously would appear under the DB subnet group section.&lt;/p&gt;

&lt;p&gt;Choose NO under public access section.&lt;/p&gt;

&lt;p&gt;Now, before proceeding to VPC security group section, navigate to VPC console and create a security group for both RDS and ECS. &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%2Fh4pk8q9mpsg28qlolsta.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%2Fh4pk8q9mpsg28qlolsta.png" alt="Image description" width="800" height="286"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe7i7rmnvrpnuzfd84jjm.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%2Fe7i7rmnvrpnuzfd84jjm.png" alt="Image description" width="800" height="286"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The above sg rules are for ECS service.&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%2Fl2ds6q07uovnkw8wpuow.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%2Fl2ds6q07uovnkw8wpuow.png" alt="Image description" width="800" height="280"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The above sg rule is for RDS and it has no outbound rules set.&lt;/p&gt;

&lt;p&gt;Now back to the RDS console where we left,&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%2Fp2rzxlyr3nkwfkv7vs0z.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%2Fp2rzxlyr3nkwfkv7vs0z.png" alt="Image description" width="800" height="280"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Choose the security group you created and choose the availability zone where you would want to host the DB instance.&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%2F8q1q5jf3ch4l2k3k6dqu.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%2F8q1q5jf3ch4l2k3k6dqu.png" alt="Image description" width="800" height="386"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Set database authentication as password based, I have disabled monitoring you can enable it if you want. &lt;/p&gt;

&lt;p&gt;Create the initial database as fider in the DB instance.&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%2Fjsl9hdpfiddo5no38tzq.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%2Fjsl9hdpfiddo5no38tzq.png" alt="Image description" width="800" height="386"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I chose to go with the above settings, you can enable it if you want.&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%2Fsg6veg398nhrutlibs3x.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%2Fsg6veg398nhrutlibs3x.png" alt="Image description" width="800" height="175"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click on create database to provision an RDS PostgrSQL DB.&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%2Fltuznyrg3upn5kt142dp.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%2Fltuznyrg3upn5kt142dp.png" alt="Image description" width="800" height="165"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The RDS PostgreSQL DB is now available!&lt;/p&gt;

&lt;h2&gt;
  
  
  Create Parameters in Systems manager Parameter Store
&lt;/h2&gt;

&lt;p&gt;Since Fider currently does not appear to have built-in logic specifically to fetch credentials or secrets from AWS Secrets Manager, I have used parameter store to store Fider's environment variables so the tasks in ECS can fetch these credentials when they are being fired up. &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%2Fcbi2x7s86tzj162mi7l4.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%2Fcbi2x7s86tzj162mi7l4.png" alt="Image description" width="800" height="392"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Fider supports the above environment variables. I have used the standard tier and secureString type to encrypt sensitive data using KMS keys.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create an IAM user
&lt;/h2&gt;

&lt;p&gt;Fider supports the use of Amazon S3 for blob storage and Amazon SES for sending emails. However, simply passing the access keys through environment variables will not automatically make the images appear in the S3 bucket, nor will it enable SES for email sending.&lt;/p&gt;

&lt;p&gt;The application will assume the role assigned to the user, which will then allow it to perform actions on the user's behalf.&lt;/p&gt;

&lt;p&gt;Navigate to the IAM console and create a user and assign the roles necessary for accessing the S3 bucket and SES service,&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%2Fmbq43063l7h4wuo8av0h.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%2Fmbq43063l7h4wuo8av0h.png" alt="Image description" width="800" height="325"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I've granted full access to S3 and SES for this guide, but this isn't the recommended approach. Instead, you should create fine-grained access policies and assign them to the role.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create an S3 bucket
&lt;/h2&gt;

&lt;p&gt;Navigate to the S3 bucket console and create a general purpose S3 bucket for blob storage.&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%2F59g5vruko09zjuy9h6hv.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%2F59g5vruko09zjuy9h6hv.png" alt="Image description" width="800" height="244"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once you've created the bucket, navigate to the Permissions tab and check the Bucket Policy section. It’s likely empty, right? Well, it shouldn’t be, if left empty the S3 bucket will remain empty forever.&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;{&lt;/span&gt;
    &lt;span class="s2"&gt;"Version"&lt;/span&gt;: &lt;span class="s2"&gt;"2012-10-17"&lt;/span&gt;,
    &lt;span class="s2"&gt;"Statement"&lt;/span&gt;: &lt;span class="o"&gt;[&lt;/span&gt;
        &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="s2"&gt;"Effect"&lt;/span&gt;: &lt;span class="s2"&gt;"Allow"&lt;/span&gt;,
            &lt;span class="s2"&gt;"Principal"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="s2"&gt;"AWS"&lt;/span&gt;: &lt;span class="s2"&gt;"arn:aws:iam::&amp;lt;ACCOUNT_ID&amp;gt;:user/&amp;lt;USER_NAME&amp;gt;"&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;,
            &lt;span class="s2"&gt;"Action"&lt;/span&gt;: &lt;span class="s2"&gt;"s3:*"&lt;/span&gt;,
            &lt;span class="s2"&gt;"Resource"&lt;/span&gt;: &lt;span class="s2"&gt;"arn:aws:s3:::&amp;lt;BUCKET_NAME&amp;gt;"&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;This policy is too permissive; you can create more fine-grained policies to restrict access.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create an identity in SES
&lt;/h2&gt;

&lt;p&gt;The application requires email verification as part of the signup process to prevent spam and ensure only valid users access the platform. I chose the SES API route instead of SMTP.&lt;/p&gt;

&lt;p&gt;Navigate to the SES service console and start creating an identity.&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%2Fmalx0ivf03isd64kwkr7.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%2Fmalx0ivf03isd64kwkr7.png" alt="Image description" width="800" height="251"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I chose to go with domain verification. Once you've created the identity via the domain, make sure to validate the records with your DNS provider.&lt;/p&gt;

&lt;p&gt;You can send a test mail from the verified identities to the check the workflow.&lt;/p&gt;

&lt;p&gt;If you plan to run this setup in production, you can request production access from Amazon support. Otherwise, you can remain in the sandbox environment.&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%2Ffeab894u4lt18akjm0gw.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%2Ffeab894u4lt18akjm0gw.png" alt="Image description" width="800" height="376"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why use ECS instead of EC2
&lt;/h2&gt;

&lt;p&gt;There is one major reason to use ECS instead of EC2,&lt;/p&gt;

&lt;h4&gt;
  
  
  1. Reduced Operational Overhead:
&lt;/h4&gt;

&lt;p&gt;EC2 is cheaper on paper, but that requires careful capacity management and container resource planning plus reserved instance commitments or spot fleet configurations. In the end it feels like 99% of the time, the admin overhead doesn't outweigh the savings.&lt;/p&gt;

&lt;p&gt;Depends on how much control you want. If you want less control, go with Fargate. And yes, if you go with standard ECS, the EC2 instances are run and managed by you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create an ECS cluster
&lt;/h2&gt;

&lt;h4&gt;
  
  
  Step 1:
&lt;/h4&gt;

&lt;p&gt;Navigate to the ECS console and create an ECS cluster with Fargate launch type.&lt;/p&gt;

&lt;p&gt;Click on Create cluster.&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%2Fm6q208ji14f1smu5nqc4.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%2Fm6q208ji14f1smu5nqc4.png" alt="Image description" width="800" height="164"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fefqncbblldbj4nrcu3s4.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%2Fefqncbblldbj4nrcu3s4.png" alt="Image description" width="800" height="749"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiznr8v5fbxjblhjzgfo0.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%2Fiznr8v5fbxjblhjzgfo0.png" alt="Image description" width="800" height="298"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I have turned on container insights for this guide, to see aggregated metrics at cluster and service level. In this way you can run the deep dive analysis.&lt;/p&gt;

&lt;p&gt;This will create an ECS cluster via Cloudformation in the backend.&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%2Fmnbw9mr10o6j1j5u7ccg.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%2Fmnbw9mr10o6j1j5u7ccg.png" alt="Image description" width="800" height="138"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Create Task Definition
&lt;/h2&gt;

&lt;h4&gt;
  
  
  Step 1:
&lt;/h4&gt;

&lt;p&gt;Navigate to the task definition section under the ECS console and create a task definition named fider-app-task-definition.&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%2F6e8xvg1kmcbzomrv0zqy.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%2F6e8xvg1kmcbzomrv0zqy.png" alt="Image description" width="800" height="137"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The task definition can be created via both the console and JSON.&lt;/p&gt;

&lt;p&gt;Refer to the following JSON task definition for creating the task definition via JSON.&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;{&lt;/span&gt;
    &lt;span class="s2"&gt;"taskDefinitionArn"&lt;/span&gt;: &lt;span class="s2"&gt;"arn:aws:ecs:us-east-2:624184658995:task-definition/fider-app-task-definition:7"&lt;/span&gt;,
    &lt;span class="s2"&gt;"containerDefinitions"&lt;/span&gt;: &lt;span class="o"&gt;[&lt;/span&gt;
        &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="s2"&gt;"name"&lt;/span&gt;: &lt;span class="s2"&gt;"fider-app"&lt;/span&gt;,
            &lt;span class="s2"&gt;"image"&lt;/span&gt;: &lt;span class="s2"&gt;"624184658995.dkr.ecr.us-east-2.amazonaws.com/getfider/fider:main"&lt;/span&gt;,
            &lt;span class="s2"&gt;"cpu"&lt;/span&gt;: 2048,
            &lt;span class="s2"&gt;"memory"&lt;/span&gt;: 8192,
            &lt;span class="s2"&gt;"memoryReservation"&lt;/span&gt;: 4096,
            &lt;span class="s2"&gt;"portMappings"&lt;/span&gt;: &lt;span class="o"&gt;[&lt;/span&gt;
                &lt;span class="o"&gt;{&lt;/span&gt;
                    &lt;span class="s2"&gt;"name"&lt;/span&gt;: &lt;span class="s2"&gt;"fider-app-3000-tcp"&lt;/span&gt;,
                    &lt;span class="s2"&gt;"containerPort"&lt;/span&gt;: 3000,
                    &lt;span class="s2"&gt;"hostPort"&lt;/span&gt;: 3000,
                    &lt;span class="s2"&gt;"protocol"&lt;/span&gt;: &lt;span class="s2"&gt;"tcp"&lt;/span&gt;,
                    &lt;span class="s2"&gt;"appProtocol"&lt;/span&gt;: &lt;span class="s2"&gt;"http"&lt;/span&gt;
                &lt;span class="o"&gt;}&lt;/span&gt;
            &lt;span class="o"&gt;]&lt;/span&gt;,
            &lt;span class="s2"&gt;"essential"&lt;/span&gt;: &lt;span class="nb"&gt;true&lt;/span&gt;,
            &lt;span class="s2"&gt;"environment"&lt;/span&gt;: &lt;span class="o"&gt;[]&lt;/span&gt;,
            &lt;span class="s2"&gt;"mountPoints"&lt;/span&gt;: &lt;span class="o"&gt;[]&lt;/span&gt;,
            &lt;span class="s2"&gt;"volumesFrom"&lt;/span&gt;: &lt;span class="o"&gt;[]&lt;/span&gt;,
            &lt;span class="s2"&gt;"secrets"&lt;/span&gt;: &lt;span class="o"&gt;[&lt;/span&gt;
                &lt;span class="o"&gt;{&lt;/span&gt;
                    &lt;span class="s2"&gt;"name"&lt;/span&gt;: &lt;span class="s2"&gt;"BASE_URL"&lt;/span&gt;,
                    &lt;span class="s2"&gt;"valueFrom"&lt;/span&gt;: &lt;span class="s2"&gt;"arn:aws:ssm:us-east-2:624184658995:parameter/BASE_URL"&lt;/span&gt;
                &lt;span class="o"&gt;}&lt;/span&gt;,
                &lt;span class="o"&gt;{&lt;/span&gt;
                    &lt;span class="s2"&gt;"name"&lt;/span&gt;: &lt;span class="s2"&gt;"DATABASE_URL"&lt;/span&gt;,
                    &lt;span class="s2"&gt;"valueFrom"&lt;/span&gt;: &lt;span class="s2"&gt;"arn:aws:ssm:us-east-2:624184658995:parameter/DATABASE_URL"&lt;/span&gt;
                &lt;span class="o"&gt;}&lt;/span&gt;,
                &lt;span class="o"&gt;{&lt;/span&gt;
                    &lt;span class="s2"&gt;"name"&lt;/span&gt;: &lt;span class="s2"&gt;"JWT_SECRET"&lt;/span&gt;,
                    &lt;span class="s2"&gt;"valueFrom"&lt;/span&gt;: &lt;span class="s2"&gt;"arn:aws:ssm:us-east-2:624184658995:parameter/JWT_SECRET"&lt;/span&gt;
                &lt;span class="o"&gt;}&lt;/span&gt;,
                &lt;span class="o"&gt;{&lt;/span&gt;
                    &lt;span class="s2"&gt;"name"&lt;/span&gt;: &lt;span class="s2"&gt;"GO_ENV"&lt;/span&gt;,
                    &lt;span class="s2"&gt;"valueFrom"&lt;/span&gt;: &lt;span class="s2"&gt;"arn:aws:ssm:us-east-2:624184658995:parameter/GO_ENV"&lt;/span&gt;
                &lt;span class="o"&gt;}&lt;/span&gt;,
                &lt;span class="o"&gt;{&lt;/span&gt;
                    &lt;span class="s2"&gt;"name"&lt;/span&gt;: &lt;span class="s2"&gt;"EMAIL_NOREPLY"&lt;/span&gt;,
                    &lt;span class="s2"&gt;"valueFrom"&lt;/span&gt;: &lt;span class="s2"&gt;"arn:aws:ssm:us-east-2:624184658995:parameter/EMAIL_NOREPLY"&lt;/span&gt;
                &lt;span class="o"&gt;}&lt;/span&gt;,
                &lt;span class="o"&gt;{&lt;/span&gt;
                    &lt;span class="s2"&gt;"name"&lt;/span&gt;: &lt;span class="s2"&gt;"EMAIL_AWSSES_ACCESS_KEY_ID"&lt;/span&gt;,
                    &lt;span class="s2"&gt;"valueFrom"&lt;/span&gt;: &lt;span class="s2"&gt;"arn:aws:ssm:us-east-2:624184658995:parameter/EMAIL_AWSSES_ACCESS_KEY_ID"&lt;/span&gt;
                &lt;span class="o"&gt;}&lt;/span&gt;,
                &lt;span class="o"&gt;{&lt;/span&gt;
                    &lt;span class="s2"&gt;"name"&lt;/span&gt;: &lt;span class="s2"&gt;"EMAIL_AWSSES_SECRET_ACCESS_KEY"&lt;/span&gt;,
                    &lt;span class="s2"&gt;"valueFrom"&lt;/span&gt;: &lt;span class="s2"&gt;"arn:aws:ssm:us-east-2:624184658995:parameter/EMAIL_AWSSES_SECRET_ACCESS_KEY"&lt;/span&gt;
                &lt;span class="o"&gt;}&lt;/span&gt;,
                &lt;span class="o"&gt;{&lt;/span&gt;
                    &lt;span class="s2"&gt;"name"&lt;/span&gt;: &lt;span class="s2"&gt;"EMAIL_AWSSES_REGION"&lt;/span&gt;,
                    &lt;span class="s2"&gt;"valueFrom"&lt;/span&gt;: &lt;span class="s2"&gt;"arn:aws:ssm:us-east-2:624184658995:parameter/EMAIL_AWSSES_REGION"&lt;/span&gt;
                &lt;span class="o"&gt;}&lt;/span&gt;,
                &lt;span class="o"&gt;{&lt;/span&gt;
                    &lt;span class="s2"&gt;"name"&lt;/span&gt;: &lt;span class="s2"&gt;"BLOB_STORAGE"&lt;/span&gt;,
                    &lt;span class="s2"&gt;"valueFrom"&lt;/span&gt;: &lt;span class="s2"&gt;"arn:aws:ssm:us-east-2:624184658995:parameter/BLOB_STORAGE"&lt;/span&gt;
                &lt;span class="o"&gt;}&lt;/span&gt;,
                &lt;span class="o"&gt;{&lt;/span&gt;
                    &lt;span class="s2"&gt;"name"&lt;/span&gt;: &lt;span class="s2"&gt;"BLOB_STORAGE_S3_ACCESS_KEY_ID"&lt;/span&gt;,
                    &lt;span class="s2"&gt;"valueFrom"&lt;/span&gt;: &lt;span class="s2"&gt;"arn:aws:ssm:us-east-2:624184658995:parameter/BLOB_STORAGE_S3_ACCESS_KEY_ID"&lt;/span&gt;
                &lt;span class="o"&gt;}&lt;/span&gt;,
                &lt;span class="o"&gt;{&lt;/span&gt;
                    &lt;span class="s2"&gt;"name"&lt;/span&gt;: &lt;span class="s2"&gt;"BLOB_STORAGE_S3_BUCKET"&lt;/span&gt;,
                    &lt;span class="s2"&gt;"valueFrom"&lt;/span&gt;: &lt;span class="s2"&gt;"arn:aws:ssm:us-east-2:624184658995:parameter/BLOB_STORAGE_S3_BUCKET"&lt;/span&gt;
                &lt;span class="o"&gt;}&lt;/span&gt;,
                &lt;span class="o"&gt;{&lt;/span&gt;
                    &lt;span class="s2"&gt;"name"&lt;/span&gt;: &lt;span class="s2"&gt;"BLOB_STORAGE_S3_ENDPOINT_URL"&lt;/span&gt;,
                    &lt;span class="s2"&gt;"valueFrom"&lt;/span&gt;: &lt;span class="s2"&gt;"arn:aws:ssm:us-east-2:624184658995:parameter/BLOB_STORAGE_S3_ENDPOINT_URL"&lt;/span&gt;
                &lt;span class="o"&gt;}&lt;/span&gt;,
                &lt;span class="o"&gt;{&lt;/span&gt;
                    &lt;span class="s2"&gt;"name"&lt;/span&gt;: &lt;span class="s2"&gt;"BLOB_STORAGE_S3_REGION"&lt;/span&gt;,
                    &lt;span class="s2"&gt;"valueFrom"&lt;/span&gt;: &lt;span class="s2"&gt;"arn:aws:ssm:us-east-2:624184658995:parameter/BLOB_STORAGE_S3_REGION"&lt;/span&gt;
                &lt;span class="o"&gt;}&lt;/span&gt;,
                &lt;span class="o"&gt;{&lt;/span&gt;
                    &lt;span class="s2"&gt;"name"&lt;/span&gt;: &lt;span class="s2"&gt;"BLOB_STORAGE_S3_SECRET_ACCESS_KEY"&lt;/span&gt;,
                    &lt;span class="s2"&gt;"valueFrom"&lt;/span&gt;: &lt;span class="s2"&gt;"arn:aws:ssm:us-east-2:624184658995:parameter/BLOB_STORAGE_S3_SECRET_ACCESS_KEY"&lt;/span&gt;
                &lt;span class="o"&gt;}&lt;/span&gt;
            &lt;span class="o"&gt;]&lt;/span&gt;,
            &lt;span class="s2"&gt;"logConfiguration"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="s2"&gt;"logDriver"&lt;/span&gt;: &lt;span class="s2"&gt;"awslogs"&lt;/span&gt;,
                &lt;span class="s2"&gt;"options"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
                    &lt;span class="s2"&gt;"awslogs-group"&lt;/span&gt;: &lt;span class="s2"&gt;"/ecs/fider-app-task"&lt;/span&gt;,
                    &lt;span class="s2"&gt;"mode"&lt;/span&gt;: &lt;span class="s2"&gt;"non-blocking"&lt;/span&gt;,
                    &lt;span class="s2"&gt;"awslogs-create-group"&lt;/span&gt;: &lt;span class="s2"&gt;"true"&lt;/span&gt;,
                    &lt;span class="s2"&gt;"max-buffer-size"&lt;/span&gt;: &lt;span class="s2"&gt;"25m"&lt;/span&gt;,
                    &lt;span class="s2"&gt;"awslogs-region"&lt;/span&gt;: &lt;span class="s2"&gt;"us-east-2"&lt;/span&gt;,
                    &lt;span class="s2"&gt;"awslogs-stream-prefix"&lt;/span&gt;: &lt;span class="s2"&gt;"ecs"&lt;/span&gt;
                &lt;span class="o"&gt;}&lt;/span&gt;,
                &lt;span class="s2"&gt;"secretOptions"&lt;/span&gt;: &lt;span class="o"&gt;[]&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;,
            &lt;span class="s2"&gt;"systemControls"&lt;/span&gt;: &lt;span class="o"&gt;[]&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;]&lt;/span&gt;,
    &lt;span class="s2"&gt;"family"&lt;/span&gt;: &lt;span class="s2"&gt;"fider-app-task-definition"&lt;/span&gt;,
    &lt;span class="s2"&gt;"taskRoleArn"&lt;/span&gt;: &lt;span class="s2"&gt;"arn:aws:iam::624184658995:role/ecsTaskExecutionRole"&lt;/span&gt;,
    &lt;span class="s2"&gt;"executionRoleArn"&lt;/span&gt;: &lt;span class="s2"&gt;"arn:aws:iam::624184658995:role/ecsTaskExecutionRole"&lt;/span&gt;,
    &lt;span class="s2"&gt;"networkMode"&lt;/span&gt;: &lt;span class="s2"&gt;"awsvpc"&lt;/span&gt;,
    &lt;span class="s2"&gt;"revision"&lt;/span&gt;: 7,
    &lt;span class="s2"&gt;"volumes"&lt;/span&gt;: &lt;span class="o"&gt;[]&lt;/span&gt;,
    &lt;span class="s2"&gt;"status"&lt;/span&gt;: &lt;span class="s2"&gt;"ACTIVE"&lt;/span&gt;,
    &lt;span class="s2"&gt;"requiresAttributes"&lt;/span&gt;: &lt;span class="o"&gt;[&lt;/span&gt;
        &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="s2"&gt;"name"&lt;/span&gt;: &lt;span class="s2"&gt;"ecs.capability.execution-role-awslogs"&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;,
        &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="s2"&gt;"name"&lt;/span&gt;: &lt;span class="s2"&gt;"com.amazonaws.ecs.capability.ecr-auth"&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;,
        &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="s2"&gt;"name"&lt;/span&gt;: &lt;span class="s2"&gt;"com.amazonaws.ecs.capability.docker-remote-api.1.28"&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;,
        &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="s2"&gt;"name"&lt;/span&gt;: &lt;span class="s2"&gt;"com.amazonaws.ecs.capability.docker-remote-api.1.21"&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;,
        &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="s2"&gt;"name"&lt;/span&gt;: &lt;span class="s2"&gt;"com.amazonaws.ecs.capability.task-iam-role"&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;,
        &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="s2"&gt;"name"&lt;/span&gt;: &lt;span class="s2"&gt;"ecs.capability.execution-role-ecr-pull"&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;,
        &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="s2"&gt;"name"&lt;/span&gt;: &lt;span class="s2"&gt;"ecs.capability.secrets.ssm.environment-variables"&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;,
        &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="s2"&gt;"name"&lt;/span&gt;: &lt;span class="s2"&gt;"com.amazonaws.ecs.capability.docker-remote-api.1.18"&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;,
        &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="s2"&gt;"name"&lt;/span&gt;: &lt;span class="s2"&gt;"ecs.capability.task-eni"&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;,
        &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="s2"&gt;"name"&lt;/span&gt;: &lt;span class="s2"&gt;"com.amazonaws.ecs.capability.docker-remote-api.1.29"&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;,
        &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="s2"&gt;"name"&lt;/span&gt;: &lt;span class="s2"&gt;"com.amazonaws.ecs.capability.logging-driver.awslogs"&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;,
        &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="s2"&gt;"name"&lt;/span&gt;: &lt;span class="s2"&gt;"com.amazonaws.ecs.capability.docker-remote-api.1.19"&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;]&lt;/span&gt;,
    &lt;span class="s2"&gt;"placementConstraints"&lt;/span&gt;: &lt;span class="o"&gt;[]&lt;/span&gt;,
    &lt;span class="s2"&gt;"compatibilities"&lt;/span&gt;: &lt;span class="o"&gt;[&lt;/span&gt;
        &lt;span class="s2"&gt;"EC2"&lt;/span&gt;,
        &lt;span class="s2"&gt;"FARGATE"&lt;/span&gt;
    &lt;span class="o"&gt;]&lt;/span&gt;,
    &lt;span class="s2"&gt;"requiresCompatibilities"&lt;/span&gt;: &lt;span class="o"&gt;[&lt;/span&gt;
        &lt;span class="s2"&gt;"FARGATE"&lt;/span&gt;
    &lt;span class="o"&gt;]&lt;/span&gt;,
    &lt;span class="s2"&gt;"cpu"&lt;/span&gt;: &lt;span class="s2"&gt;"2048"&lt;/span&gt;,
    &lt;span class="s2"&gt;"memory"&lt;/span&gt;: &lt;span class="s2"&gt;"8192"&lt;/span&gt;,
    &lt;span class="s2"&gt;"runtimePlatform"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="s2"&gt;"cpuArchitecture"&lt;/span&gt;: &lt;span class="s2"&gt;"X86_64"&lt;/span&gt;,
        &lt;span class="s2"&gt;"operatingSystemFamily"&lt;/span&gt;: &lt;span class="s2"&gt;"LINUX"&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;,
    &lt;span class="s2"&gt;"registeredAt"&lt;/span&gt;: &lt;span class="s2"&gt;"2025-02-18T06:06:55.123Z"&lt;/span&gt;,
    &lt;span class="s2"&gt;"registeredBy"&lt;/span&gt;: &lt;span class="s2"&gt;"arn:aws:sts::624184658995:assumed-role/AWSReservedSSO_AdministratorAccess_263ab8d7ae88c1c4/wasim"&lt;/span&gt;,
    &lt;span class="s2"&gt;"tags"&lt;/span&gt;: &lt;span class="o"&gt;[]&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I have referenced the environment variables created in systems manager parameter store by their resource arn.&lt;/p&gt;

&lt;p&gt;You also need the following policies to be attached to &lt;em&gt;ecsTaskExecutionRole&lt;/em&gt; along with &lt;em&gt;AmazonECSTaskExecutionRolePolicy&lt;/em&gt;,&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;AmazonSSMFullAccess&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;ROSAKMSProviderPolicy&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsasjikg6da3zgmfqnzbx.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%2Fsasjikg6da3zgmfqnzbx.png" alt="Image description" width="800" height="298"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The policies attached provides the ECS agent the required permissions to fetch the KMS encrypted environment variables from the systems manager parameter store, decrypt it and provide it to the application hosted inside the ECS tasks.&lt;/p&gt;

&lt;p&gt;I have provided full access permission policies, but it's usually not the right way to do it. You can craft the fine grained access policies and attach the same to the &lt;em&gt;ecsTaskExecutionRole&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create an ECS service
&lt;/h2&gt;

&lt;h4&gt;
  
  
  Step 1:
&lt;/h4&gt;

&lt;p&gt;Navigate to the ECS cluster you created and click on it, once inside the cluster click on service.&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%2F8whkplvje0h4uagsp848.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%2F8whkplvje0h4uagsp848.png" alt="Image description" width="800" height="312"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 2: Configuration parameters
&lt;/h4&gt;

&lt;p&gt;In the compute configuration choose the capacity provider strategy and choose FARGATE as capacity provider with base 1 and weight as 100&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%2Fcdbmo77mllbvaul9l8c6.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%2Fcdbmo77mllbvaul9l8c6.png" alt="Image description" width="800" height="236"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Choose service as the application type in the deployment configuration and Replica as the service type with Desired tasks set to 1. You can also set it 2, I have used one for this guide.&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%2F2soq3b73kuhf5w22or10.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%2F2soq3b73kuhf5w22or10.png" alt="Image description" width="800" height="384"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the networking configuration choose the VPC you created.&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%2F3xot5yi7k0n25s76b0sz.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%2F3xot5yi7k0n25s76b0sz.png" alt="Image description" width="800" height="330"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Turn off the public IP address as the tasks will use NAT gateway.&lt;/p&gt;

&lt;p&gt;Configure the Application load balancer and place it inside the public subnet as front-facing for your application. &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%2Fayjfu3b39yhqinvmjo35.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%2Fayjfu3b39yhqinvmjo35.png" alt="Image description" width="800" height="330"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Create a target group on the fly in the ECS console itself and set up an HTTPS listener for your ALB. For this deployment, I’ve chosen to handle the HTTP to HTTPS redirect at the CDN level, so you don’t need an HTTP listener for your ALB, as redirects won’t happen at that level.&lt;/p&gt;

&lt;p&gt;For the SSL certificate to appear in the dropdown, skip to &lt;strong&gt;step 3&lt;/strong&gt; from here and request a public certificate from the ACM 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%2Ffdprrl5n8z9gvct4g921.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%2Ffdprrl5n8z9gvct4g921.png" alt="Image description" width="800" height="393"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The health check path for Fider is &lt;strong&gt;_/health&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I have not enabled service auto scaling for this guide, you can enable it if you want.&lt;/p&gt;

&lt;p&gt;Click on create in the end and navigate to Cloudformation to view the status. &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%2F7jkx2y4twa4hdciswvbn.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%2F7jkx2y4twa4hdciswvbn.png" alt="Image description" width="800" height="372"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyhsp91fwb7pbkvwn6doc.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%2Fyhsp91fwb7pbkvwn6doc.png" alt="Image description" width="800" height="379"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The password authentication with RDS PostgreSQL is successful and the records got migrated!&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%2F8x9414ee8apxfnuuefej.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%2F8x9414ee8apxfnuuefej.png" alt="Image description" width="800" height="370"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since we have also enabled container insights, we can check the performance metrics of clusters, services and tasks.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: If you are not able to run the tasks or if you ran into errors like this,&lt;/p&gt;
&lt;/blockquote&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%2Fltyupghdwjyjtc7z00fm.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%2Fltyupghdwjyjtc7z00fm.png" alt="Image description" width="800" height="144"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It's because you placed the task in the public subnet with no public IP assigned to it, when you created the application load balancer in the ECS service console itself. Even i ran into this issue and then i had to troubleshoot the failed tasks.&lt;/p&gt;

&lt;p&gt;I used the Systems Manager's &lt;strong&gt;AWSSupport-TroubleshootECSTaskFailedToStart&lt;/strong&gt; automation document. It diagnosed the issue for me. &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%2Fbchd8rmhmq31miiedn5l.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%2Fbchd8rmhmq31miiedn5l.png" alt="Image description" width="800" height="290"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Create the application load balancer and target group separately, note the availability zones(AZs) you assinged for ALB, and when you spin up the ECS tasks via service make sure you provide the private subnets from the same availability zones(AZs).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Feel free to check this documentation when you run into 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%2Fcgz8pz27m7u3ovy2m1uh.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%2Fcgz8pz27m7u3ovy2m1uh.png" alt="Image description" width="800" height="290"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 3: Use ACM for SSL certificates
&lt;/h4&gt;

&lt;p&gt;The service is up and running now.&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%2Fldial6vlxgh3o51md25z.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%2Fldial6vlxgh3o51md25z.png" alt="Image description" width="800" height="282"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Navigate to the ACM console and click on request a certificate.&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%2Fsl0653a9fm7y68ws3l0s.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%2Fsl0653a9fm7y68ws3l0s.png" alt="Image description" width="800" height="389"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Request a public certificate and click on Next.&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%2F55pl0dpbntd697xn17y7.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%2F55pl0dpbntd697xn17y7.png" alt="Image description" width="800" height="404"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Provide your domain name and choose the DNS validation method. &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%2Fedujs6gc75ri364xy90v.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%2Fedujs6gc75ri364xy90v.png" alt="Image description" width="800" height="790"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click on request and ACM provides you with the DNS records, verify the records by mapping them to your DNS registrar settings. &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%2F2q1psiv8oof0e6tybba3.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%2F2q1psiv8oof0e6tybba3.png" alt="Image description" width="800" height="168"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Your SSL certificate is now ready to be used with the Application Load Balancer. You can map it under the Listener configuration for the ALB in the certificate section of the ECS service 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%2Fbj6ky2d9x4wv8r7z3807.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%2Fbj6ky2d9x4wv8r7z3807.png" alt="Image description" width="800" height="205"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since I’m setting up CloudFront, I’ve also chosen to enable WAF at the CDN level for this deployment, as CloudFront acts as the first point of contact and can filter traffic before it reaches the ALB.&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%2Fiah2wej9ufmnur6zu8w9.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%2Fiah2wej9ufmnur6zu8w9.png" alt="Image description" width="800" height="330"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since WAF is enabled at the CloudFront level and all traffic originates from there, you don’t need to enable WAF at the ALB level. Instead, I’ve added an &lt;strong&gt;X-Custom-Header&lt;/strong&gt; to restrict access to the Application Load Balancer (ALB), ensuring that only requests from CloudFront are allowed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create Cloudfront distribution with WAF
&lt;/h2&gt;

&lt;p&gt;Navigate to the integrations tab under the application load balancer &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%2Furuyit24ium6weumgwsl.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%2Furuyit24ium6weumgwsl.png" alt="Image description" width="800" height="303"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click on manage cloudfront + WAF integration.&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%2Fo96l7t0k4e9vfbz3s6w3.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%2Fo96l7t0k4e9vfbz3s6w3.png" alt="Image description" width="800" height="402"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;click on the check box to enable the cloudfront IPs as inbound to your application load balancer.&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%2Fy5jgcckcbrpook7gq0sf.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%2Fy5jgcckcbrpook7gq0sf.png" alt="Image description" width="800" height="146"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;create an SSL/TLS certificate for your domain, from which you would like to host Fider on. Cloudfront only accepts SSL/TLS certificates from us-east-1(N.virginia) region.&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%2Fgu3j568e21v1ahjokeo3.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%2Fgu3j568e21v1ahjokeo3.png" alt="Image description" width="800" height="375"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once you click create, WAF rules and Cloudfront distribution will get enabled for your ALB.&lt;/p&gt;

&lt;p&gt;Now,navigate to the Cloudfront distribution and click on the behaviours tab to edit the settings of default cache behaviour,&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%2Fupwcuxu49508tz5ywd7f.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%2Fupwcuxu49508tz5ywd7f.png" alt="Image description" width="800" height="538"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I chose to handle the HTTP to HTTPS redirect at this level.&lt;/p&gt;

&lt;p&gt;Since Fider is a dynamic application, Features like submitting feedback, voting, and commenting are processed in real-time, and the application relies on a database to store and manage user data dynamically. So set the Allowed HTTP methods to &lt;strong&gt;GET,HEAD,OPTIONS,PUT,POST,PATCH,DELETE&lt;/strong&gt; . If not you won't be able to perform PUT operation if you choose from first two.&lt;/p&gt;

&lt;p&gt;Save it and now hover over to the origins tab and choose to edit the ALB origin settings.&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%2Fc4ii2lz1ef51tmet8o7i.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%2Fc4ii2lz1ef51tmet8o7i.png" alt="Image description" width="800" height="325"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw39h4ahjqgetwcp69art.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%2Fw39h4ahjqgetwcp69art.png" alt="Image description" width="800" height="453"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Set the protocol to HTTPS only, I choose to let Cloudfront talk to my ALB over HTTPS and scroll further down,&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%2Fb6nrqtyoawyxbp7yxfzl.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%2Fb6nrqtyoawyxbp7yxfzl.png" alt="Image description" width="800" height="226"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Add the Custom header which you added in the previous step with ALB, Cloudfront passes this header in every request it makes to the ALB. Hitting the ALB DNS without this header would result in 403 forbidden error. &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%2F780su9gk0xq6v39od63t.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%2F780su9gk0xq6v39od63t.png" alt="Image description" width="800" height="285"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is how your ALB resource map should look. It will only forward traffic to the target group if the string you configured in CloudFront is present in the request header.&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%2Fzw9n2ogzuz7vt8ey0syg.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%2Fzw9n2ogzuz7vt8ey0syg.png" alt="Image description" width="800" height="131"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, map the distribution domain to the domain where you want to host Fider in your DNS records(CNAME record). Don't hit the distribution domain name it will only result in 502 error.&lt;/p&gt;

&lt;p&gt;Hit the domain name you configured in the DNS entry, You should be able to see the landing page.&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%2Foqg1xftpigfvgtskl4l8.jpeg" 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%2Foqg1xftpigfvgtskl4l8.jpeg" alt="Image description" width="800" height="252"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The images you upload to the webiste will get stored as an object in the S3 bucket, and when you access it from the browser via UI. you should be able to see this in the network tab under response headers (X-cache: Hit from Cloudfront)&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%2Fdphbjkzjmg32fzrl7as2.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%2Fdphbjkzjmg32fzrl7as2.png" alt="Image description" width="800" height="386"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can navigate to the AWS WAF in the console and look for the request metrics.&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%2Fibci5lurc2fhvtzl25e0.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%2Fibci5lurc2fhvtzl25e0.png" alt="Image description" width="800" height="375"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuxsw2e3n3cgk4ledbbcj.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%2Fuxsw2e3n3cgk4ledbbcj.png" alt="Image description" width="800" height="375"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can also configure the managed rules in the WAF console under the Rules tab.&lt;/p&gt;

&lt;p&gt;Viola! There you have it. Fider's up and running with the tasks securely inside their own private subnet, and the ALB routing requests to them. Additionally, with CloudFront and AWS WAF configured, traffic is efficiently delivered via the CDN while being protected at the edge, ensuring secure and optimized access.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>cloud</category>
      <category>ecs</category>
      <category>aws</category>
    </item>
  </channel>
</rss>
