<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Muhammad Ahmad Khan</title>
    <description>The latest articles on Forem by Muhammad Ahmad Khan (@muhammad_ahmad_khan).</description>
    <link>https://forem.com/muhammad_ahmad_khan</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%2F741875%2Fc1ba7bc1-8f83-420d-ae48-71a09d71357c.jpg</url>
      <title>Forem: Muhammad Ahmad Khan</title>
      <link>https://forem.com/muhammad_ahmad_khan</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/muhammad_ahmad_khan"/>
    <language>en</language>
    <item>
      <title>From Static to Runtime: Choosing Between CloudFront + S3, Amplify, and ECS for Frontends on AWS</title>
      <dc:creator>Muhammad Ahmad Khan</dc:creator>
      <pubDate>Sun, 26 Apr 2026 14:20:44 +0000</pubDate>
      <link>https://forem.com/muhammad_ahmad_khan/from-static-to-runtime-choosing-between-cloudfront-s3-amplify-and-ecs-for-frontends-on-aws-15i5</link>
      <guid>https://forem.com/muhammad_ahmad_khan/from-static-to-runtime-choosing-between-cloudfront-s3-amplify-and-ecs-for-frontends-on-aws-15i5</guid>
      <description>&lt;p&gt;Most AWS frontend journeys begin with the same honest instinct: keep the first version simple. A build pipeline pushes static assets, S3 holds the files, CloudFront handles TLS and caching, and the team ships. For a genuinely static site, that decision is still excellent. It is cheap to operate, easy to explain, and fast all over the world.&lt;/p&gt;

&lt;p&gt;The problem appears later. The frontend starts asking for behavior that is no longer static: deeper routing, request-time rendering, image or runtime optimizations, environment-specific configuration, or production controls that need to look like the rest of the platform. At that point, the hosting model is no longer a packaging detail. It becomes architecture.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The mistake is usually not starting with CloudFront + S3. The mistake is keeping a static-hosting model after the frontend begins to need request-time behavior, runtime configuration, or deployment controls that look like the rest of the production platform. &lt;br&gt;
The real question: can this frontend be fully generated at build time and served as files, or does it now need server behavior? That single question usually points you to the right AWS model faster than any brand comparison does.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;My practical framing is simple. Use CloudFront + S3 when the app is truly static. Use Amplify Hosting when you want a managed path to modern frontend behavior. Use ECS on Fargate when the frontend has crossed the line into being an application service.&lt;/p&gt;




&lt;h1&gt;
  
  
  Option 1 — CloudFront + S3
&lt;/h1&gt;

&lt;h3&gt;
  
  
  Why teams start here
&lt;/h3&gt;

&lt;p&gt;CloudFront + S3 is still the cleanest answer for static frontends. That includes documentation portals, marketing sites, lightweight SPAs, and Next.js projects that can be exported as static output via the &lt;code&gt;output: 'export'&lt;/code&gt; setting in &lt;code&gt;next.config.js&lt;/code&gt;. Operationally, this model stays small: no app server fleet, no container runtime, and no load balancer in the middle.&lt;/p&gt;

&lt;p&gt;It also scales in the most comfortable way. Most requests are served from the edge, the origin is just a bucket, and your deployment story is mostly about building assets and invalidating cache when needed.&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%2F7d6uogh2ddhhcd5q68k2.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%2F7d6uogh2ddhhcd5q68k2.png" alt="CloudFront + S3 architecture" width="800" height="505"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Figure 1 — Build artifacts flow into S3; users hit CloudFront; the edge cache absorbs most traffic.&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%2Fht0w7kdsikmq7wpiwzgo.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%2Fht0w7kdsikmq7wpiwzgo.png" alt="CloudFront + S3 sequence" width="800" height="505"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Figure 2 — S3 is only contacted when the edge cache misses.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Where the model starts to hurt
&lt;/h3&gt;

&lt;p&gt;This is where many teams get tripped up. They do not make a bad initial choice; they simply keep a static model after the app stops behaving like a static site. Once request-time behavior enters the picture, CloudFront + S3 begins to accumulate workarounds.&lt;/p&gt;

&lt;p&gt;Two details matter more than most teams expect. First, S3 website endpoints support only HTTP, not HTTPS. Second, if you use a website endpoint as the CloudFront origin so you can get website-style behavior such as automatic index document handling, CloudFront treats it as a custom origin, which means Origin Access Control (OAC) is not available.&lt;/p&gt;

&lt;p&gt;Deep-link routing for SPAs also needs care. Since S3 only serves objects that actually exist, a direct browser request to a route such as &lt;code&gt;/dashboard&lt;/code&gt; or &lt;code&gt;/products/123&lt;/code&gt; may not map to a real object in the bucket and may return a 403 or 404. For SPA-style routing, this is usually handled with CloudFront custom error responses that return &lt;code&gt;/index.html&lt;/code&gt; for 403 and 404 responses, or with a CloudFront Function that rewrites clean URLs to the appropriate &lt;code&gt;index.html&lt;/code&gt; path.&lt;/p&gt;

&lt;p&gt;That works for static sites and SPAs, but it is still a routing workaround, not a runtime application model. If routes require request-time rendering, server-side data fetching, middleware, personalization, or framework runtime behavior, CloudFront + S3 starts to become a poor fit.&lt;/p&gt;




&lt;h1&gt;
  
  
  Option 2 — Amplify Hosting
&lt;/h1&gt;

&lt;h3&gt;
  
  
  The middle ground many teams should evaluate earlier
&lt;/h3&gt;

&lt;p&gt;Amplify is the answer when CloudFront + S3 feels too static, but running containers for a frontend still feels unnecessary. It gives you Git-based deployments, a managed CDN, and managed SSR support without asking the team to own the underlying runtime in the same way ECS does.&lt;/p&gt;

&lt;p&gt;That makes Amplify particularly attractive for modern frontend teams: product keeps a fast deploy loop, QA gets predictable branch-based delivery, and engineering can support SSR behavior without building a bespoke hosting stack first.&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%2Fodd6mji96ca8brc0j46k.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%2Fodd6mji96ca8brc0j46k.png" alt="Amplify Hosting architecture" width="800" height="505"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Figure 3 — Git push triggers a managed build; the CDN routes requests to either static assets or managed SSR compute.&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%2Fc9uqm7btm8yi3nzv0plt.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%2Fc9uqm7btm8yi3nzv0plt.png" alt="Amplify Hosting sequence" width="800" height="479"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Figure 4 — Static routes are served from cache; SSR routes are rendered on managed compute.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Why this is not just "CloudFront + S3 with nicer buttons"
&lt;/h3&gt;

&lt;p&gt;Amplify is opinionated in a useful way. AWS documents managed SSR support for modern Next.js versions, automatic framework detection, and a deployment model that is closer to a frontend platform than a raw infrastructure stack. For many teams, that is exactly the point.&lt;/p&gt;

&lt;p&gt;The tradeoff is that you are accepting the boundaries of a managed service. Server-side environment variables, for example, are not exposed automatically the way client-side ones are; they need to be set up explicitly via the Amplify console or build spec. And new framework features sometimes ship to self-hosted Next.js before they reach Amplify. If your goal is to minimize hosting decisions and keep the frontend delivery experience as product-friendly as possible, Amplify is often the cleanest middle path.&lt;/p&gt;




&lt;h1&gt;
  
  
  Option 3 — ECS on Fargate
&lt;/h1&gt;

&lt;h3&gt;
  
  
  When the frontend becomes part of the application platform
&lt;/h3&gt;

&lt;p&gt;There is a moment when the right question stops being "How do we host this site?" and becomes "How do we run this service?" That is the handoff point to ECS on Fargate.&lt;/p&gt;

&lt;p&gt;This model makes sense when the frontend needs runtime secrets, deliberate health checks, explicit rollout control via mechanisms like the ECS deployment circuit breaker, target-tracking autoscaling, private networking, and the same operational standards as the rest of your production estate. It is not the lowest-effort option. It is the highest-control option.&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%2Fenpk73ox0ylacv36qumd.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%2Fenpk73ox0ylacv36qumd.png" alt="ECS on Fargate architecture" width="800" height="505"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Figure 5 — The pipeline pushes images to ECR and updates the ECS service; the ALB routes user traffic to healthy tasks.&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%2F9hswmd2y07fl87guk1kz.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%2F9hswmd2y07fl87guk1kz.png" alt="ECS on Fargate sequence" width="800" height="505"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Figure 6 — Deployment phase rolls out new tasks behind health checks; steady-state requests reach only healthy targets.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The thing Next.js teams underestimate in containers
&lt;/h3&gt;

&lt;p&gt;When teams move Next.js into containers, they usually think first about deployment. They should also think about cache behavior. Self-hosted Next.js is straightforward on a single instance, but multi-instance behavior is a design topic, not an implementation detail. Once multiple tasks can serve the same content, the Next.js incremental cache (used by ISR and the data cache) needs an explicit shared cache handler — a Redis or S3 backend, for example — instead of the per-task default. Otherwise you get inconsistent pages between replicas.&lt;/p&gt;

&lt;p&gt;That is why ECS is best reserved for frontends that have clearly joined the application platform. Once you truly need runtime control, the extra operational responsibility is justified. Before that point, it is often more platform than the problem requires.&lt;/p&gt;




&lt;h1&gt;
  
  
  A simple decision matrix
&lt;/h1&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;Best when&lt;/th&gt;
&lt;th&gt;Biggest strength&lt;/th&gt;
&lt;th&gt;Main tradeoff&lt;/th&gt;
&lt;th&gt;Default advice&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CloudFront + S3&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The app is truly static or can be fully generated at build time.&lt;/td&gt;
&lt;td&gt;Very low ops, very fast, globally cached.&lt;/td&gt;
&lt;td&gt;Starts to fight you once request-time behavior enters the picture.&lt;/td&gt;
&lt;td&gt;Start here for static sites.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Amplify Hosting&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;You want modern frontend behavior without running containers.&lt;/td&gt;
&lt;td&gt;Managed Git-based delivery with built-in SSR support.&lt;/td&gt;
&lt;td&gt;Opinionated platform; some Next.js features land later.&lt;/td&gt;
&lt;td&gt;Best middle ground.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ECS on Fargate&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The frontend now behaves like an application service.&lt;/td&gt;
&lt;td&gt;Full runtime, rollout, and scaling control.&lt;/td&gt;
&lt;td&gt;Highest operational responsibility.&lt;/td&gt;
&lt;td&gt;Use when the frontend joins the platform.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h1&gt;
  
  
  What I would actually choose
&lt;/h1&gt;

&lt;p&gt;If the frontend is still static, I would not move away from CloudFront + S3. It remains a strong design. If the team needs modern frontend behavior but wants to stay out of container operations, I would take Amplify seriously and early. If the frontend now needs to live under the same release, security, and observability standards as backend services, I would move it to ECS and treat it accordingly.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The best AWS frontend architecture is usually not the most powerful one. It is the one whose operating model still matches the app's actual behavior.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>devops</category>
      <category>aws</category>
      <category>automation</category>
    </item>
    <item>
      <title>Amazon ECS Deployment Strategies: Legacy CodeDeploy and the Native ECS Controller</title>
      <dc:creator>Muhammad Ahmad Khan</dc:creator>
      <pubDate>Sun, 26 Apr 2026 13:37:41 +0000</pubDate>
      <link>https://forem.com/muhammad_ahmad_khan/amazon-ecs-deployment-strategies-legacy-codedeploy-and-the-native-ecs-controller-46cg</link>
      <guid>https://forem.com/muhammad_ahmad_khan/amazon-ecs-deployment-strategies-legacy-codedeploy-and-the-native-ecs-controller-46cg</guid>
      <description>&lt;p&gt;If you have worked with Amazon ECS long enough, you have probably heard two different stories that both sound true.&lt;/p&gt;

&lt;p&gt;The first says that if you want blue/green on ECS, you use AWS CodeDeploy. The second says that Amazon ECS now supports rolling, blue/green, canary, and linear deployments directly. Both are true — they just belong to different generations of the ECS deployment model.&lt;/p&gt;

&lt;p&gt;The real distinction is not the rollout names; it is the control plane. In the legacy path, CodeDeploy owns the blue/green workflow and ECS participates through task sets. In the newer path, the ECS deployment controller owns the service deployment directly and tracks service revisions inside ECS itself.&lt;/p&gt;

&lt;p&gt;That shift matters because most real environments are mixed. Existing services may still depend on the older CodeDeploy pattern, while new services can often standardize on the native ECS strategies. If you lead platform or DevOps work, you need to be comfortable in both worlds.&lt;/p&gt;

&lt;h2&gt;
  
  
  The one distinction that clears up most confusion
&lt;/h2&gt;

&lt;p&gt;Amazon ECS separates the deployment controller from the deployment strategy.&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;deployment controller&lt;/strong&gt; is the control plane that runs the release. In practice, that means &lt;strong&gt;ECS&lt;/strong&gt; for native ECS-managed deployments, &lt;strong&gt;CODE_DEPLOY&lt;/strong&gt; for CodeDeploy-managed ECS blue/green, and &lt;strong&gt;EXTERNAL&lt;/strong&gt; for a third-party deployment controller.&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;deployment strategy&lt;/strong&gt; is the rollout shape used by that controller. With the ECS deployment controller, ECS supports &lt;strong&gt;ROLLING&lt;/strong&gt;, &lt;strong&gt;BLUE_GREEN&lt;/strong&gt;, &lt;strong&gt;CANARY&lt;/strong&gt;, and &lt;strong&gt;LINEAR&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Older write-ups often equate "ECS blue/green" with "CodeDeploy," but current AWS docs also describe native ECS blue/green, canary, and linear. Advanced ECS traffic shifting used to be a CodeDeploy-only capability. It is now also an ECS-native one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick comparison: what changes in the traffic layer?
&lt;/h2&gt;

&lt;p&gt;The fastest way to explain the entire topic is through the traffic layer.&lt;/p&gt;

&lt;p&gt;Built-in rolling usually keeps the same listener and target-group path, with ECS replacing tasks in place.&lt;/p&gt;

&lt;p&gt;Legacy CodeDeploy blue/green introduces the dual-target-group model, a production listener, and an optional test listener, with CodeDeploy deciding how traffic moves between blue and green task sets.&lt;/p&gt;

&lt;p&gt;Built-in ECS blue/green, canary, and linear also use a dual-environment traffic model for managed shifting, but ECS now owns the listener-rule changes and the rollout behavior.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;Native rolling&lt;/th&gt;
&lt;th&gt;Legacy CodeDeploy blue/green&lt;/th&gt;
&lt;th&gt;Native blue/green, canary, linear&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Controller&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;ECS&lt;/td&gt;
&lt;td&gt;CODE_DEPLOY&lt;/td&gt;
&lt;td&gt;ECS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Target groups&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Same target group; in-place&lt;/td&gt;
&lt;td&gt;Two target groups (blue and green)&lt;/td&gt;
&lt;td&gt;Primary + alternate (ALB)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Listener changes&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Usually none&lt;/td&gt;
&lt;td&gt;CodeDeploy updates production listener&lt;/td&gt;
&lt;td&gt;ECS updates listener rules&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Rollout owner&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;ECS scheduler&lt;/td&gt;
&lt;td&gt;CodeDeploy&lt;/td&gt;
&lt;td&gt;ECS deployment controller&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Unit of deployment&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Tasks within service&lt;/td&gt;
&lt;td&gt;Task sets&lt;/td&gt;
&lt;td&gt;Service revisions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Test environment&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Not applicable&lt;/td&gt;
&lt;td&gt;Optional test listener&lt;/td&gt;
&lt;td&gt;Optional test listener / rule&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;em&gt;Quick matrix — what changes in the traffic layer across built-in and legacy ECS deployment models.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Full comparison diagram
&lt;/h2&gt;

&lt;p&gt;The same idea is clearer with the legacy and native models side by side.&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%2F80svf9ny4enward9vv3a.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%2F80svf9ny4enward9vv3a.png" alt="Legacy CodeDeploy vs native ECS controller comparison" width="800" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Figure 1 — Legacy CodeDeploy controller vs native ECS controller. Both use a dual-target-group pattern; the difference is who owns the rollout.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Part 1: The legacy model — CodeDeploy blue/green for Amazon ECS
&lt;/h2&gt;

&lt;p&gt;For a long time, if a team wanted something safer than a standard ECS rolling update, it usually meant stepping into CodeDeploy-controlled blue/green.&lt;/p&gt;

&lt;p&gt;In that model, the ECS service uses &lt;strong&gt;deploymentController = CODE_DEPLOY&lt;/strong&gt;, the deployment is started through CodeDeploy, CodeDeploy creates a replacement &lt;em&gt;green task set&lt;/em&gt;, traffic is shifted away from the original &lt;em&gt;blue task set&lt;/em&gt;, and after success, blue is terminated according to deployment settings.&lt;/p&gt;

&lt;p&gt;From the load-balancer point of view, the pattern is deliberate and explicit. Legacy CodeDeploy ECS blue/green requires two target groups, a production listener, and an optional test listener. One target group serves blue and the other serves green during deployment.&lt;/p&gt;

&lt;p&gt;Note that legacy ECS traffic shifting can be all-at-once, canary, or linear — but those are CodeDeploy deployment configurations, not separate ECS-native strategies.&lt;/p&gt;

&lt;h3&gt;
  
  
  Legacy diagram 1 — CodeDeploy blue/green, all-at-once
&lt;/h3&gt;

&lt;p&gt;The fastest legacy cutover. It is still blue/green because CodeDeploy creates and validates a replacement task set, then moves production traffic in one step.&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%2Frxpsfm850yhlilqsxddf.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%2Frxpsfm850yhlilqsxddf.png" alt="Legacy CodeDeploy blue/green all-at-once sequence" width="800" height="705"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Figure 2 — Legacy CodeDeploy blue/green, all-at-once. A single 0% → 100% production shift after green is provisioned.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Legacy diagram 2 — CodeDeploy blue/green, canary
&lt;/h3&gt;

&lt;p&gt;The legacy two-phase rollout. CodeDeploy shifts a small percentage first, evaluates health and alarms during the canary interval, and only then shifts the remainder.&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%2Fzp837ta7ue2xyqmizds2.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%2Fzp837ta7ue2xyqmizds2.png" alt="Legacy CodeDeploy blue/green canary sequence" width="800" height="768"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Figure 3 — Legacy CodeDeploy blue/green, canary. ALB-based; uses a CodeDeployDefault.ECSCanary* deployment configuration.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Legacy diagram 3 — CodeDeploy blue/green, linear
&lt;/h3&gt;

&lt;p&gt;The most gradual traffic-shift pattern in the legacy model. CodeDeploy increases traffic to green in equal steps, giving you more than one checkpoint before full cutover.&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%2Fmv2yia458e2i02gyqwqw.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%2Fmv2yia458e2i02gyqwqw.png" alt="Legacy CodeDeploy blue/green linear sequence" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Figure 4 — Legacy CodeDeploy blue/green, linear. ALB-based; uses a CodeDeployDefault.ECSLinear* deployment configuration.&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Important legacy caveat&lt;/strong&gt; — when the legacy model uses a Network Load Balancer, AWS documents all-at-once as the supported traffic-shift mode. That is why legacy canary and linear are presented here as ALB-based examples.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Part 2: The modern model — native Amazon ECS service deployments
&lt;/h2&gt;

&lt;p&gt;The modern ECS deployment story is cleaner because the ECS deployment controller now owns the deployment behavior directly.&lt;/p&gt;

&lt;p&gt;Instead of relying on CodeDeploy to manage replacement task sets, the native model works with ECS service deployments and service revisions. That reduces control-plane sprawl and turns the strategy choice into a single ECS concern rather than a multi-service orchestration problem.&lt;/p&gt;

&lt;p&gt;For managed traffic shifting, AWS documents support through Application Load Balancer, Network Load Balancer, or Service Connect, depending on the pattern. The diagrams below use Application Load Balancer because that makes the traffic behavior easiest to follow visually.&lt;/p&gt;

&lt;h3&gt;
  
  
  Native strategy 1 — Rolling
&lt;/h3&gt;

&lt;p&gt;Rolling remains the simplest native option. ECS launches new tasks, stops old tasks as deployment progresses, and follows the deployment configuration controlled by &lt;strong&gt;minimumHealthyPercent&lt;/strong&gt; and &lt;strong&gt;maximumPercent&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;From the traffic-layer point of view, rolling changes the least. The service typically stays on the same target-group path and ECS replaces tasks in place. ECS can support multiple target groups for some service designs, but that is a service capability, not a blue/green traffic-shift requirement.&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%2Fiwuqkij1wn4f4gysu97e.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%2Fiwuqkij1wn4f4gysu97e.png" alt="Native ECS rolling deployment sequence" width="800" height="643"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Figure 5 — Native ECS rolling deployment. Same target-group path; tasks are replaced in place under min-healthy / max-percent constraints.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Native strategy 2 — Blue/Green
&lt;/h3&gt;

&lt;p&gt;Native ECS BLUE_GREEN is the closest built-in replacement for teams that used blue/green for safer cutovers and quick rollback.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The most important point:&lt;/strong&gt; native ECS blue/green is &lt;em&gt;not&lt;/em&gt; "blue/green with optional canary or linear." In the native model, &lt;strong&gt;BLUE_GREEN&lt;/strong&gt;, &lt;strong&gt;CANARY&lt;/strong&gt;, and &lt;strong&gt;LINEAR&lt;/strong&gt; are separate strategies. Native blue/green performs an all-at-once production cutover after validation.&lt;/p&gt;

&lt;p&gt;For ALB-based native blue/green, ECS uses a primary target group for blue, an alternate target group for green, a production listener rule for production traffic, and an optional test listener (or test rule) for validation traffic. ECS updates the listener rules during deployment.&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%2F5v9u21uf8ar83sgj9w7a.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%2F5v9u21uf8ar83sgj9w7a.png" alt="Native ECS blue/green deployment sequence" width="800" height="705"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Figure 6 — Native ECS blue/green. ECS owns the listener-rule swap. Primary TG = blue, alternate TG = green.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Native strategy 3 — Canary
&lt;/h3&gt;

&lt;p&gt;Native ECS CANARY is the small-blast-radius strategy.&lt;/p&gt;

&lt;p&gt;ECS launches the green service revision, can optionally validate it with test traffic, then shifts a configured small percentage of production traffic to green. ECS waits through the canary bake time, and if the deployment stays healthy, shifts the remaining traffic and waits through the deployment bake time before terminating blue.&lt;/p&gt;

&lt;p&gt;The traffic model is straightforward: a small percentage first, the rest later.&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%2Feg4joy8gt90zmesi4ru6.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%2Feg4joy8gt90zmesi4ru6.png" alt="Native ECS canary deployment sequence" width="800" height="768"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Figure 7 — Native ECS canary. A single small slice, a bake window, then a full shift.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Native strategy 4 — Linear
&lt;/h3&gt;

&lt;p&gt;Native ECS LINEAR is the most gradual rollout.&lt;/p&gt;

&lt;p&gt;ECS launches green, optionally validates it, then shifts production traffic in equal percentage increments with a configured step bake time between increments. After reaching 100% green, ECS waits through the deployment bake time before terminating blue.&lt;/p&gt;

&lt;p&gt;This fits services where you want more than one checkpoint and you expect some issues to appear only as traffic gradually increases.&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%2Fe60nga9k7qv1q55j8fse.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%2Fe60nga9k7qv1q55j8fse.png" alt="Native ECS linear deployment sequence" width="800" height="831"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Figure 8 — Native ECS linear. Equal-step shifts with a bake between each, then a deployment bake before teardown.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What actually changes between rolling and blue/green-style deployments?
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Rolling&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Usually the same listener and same target-group path. No separate green traffic lane required. The ECS scheduler handles in-place task replacement.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Legacy CodeDeploy blue/green&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Two target groups required. A production listener is required. An optional test listener is available. CodeDeploy controls traffic movement between blue and green task sets.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Native ECS blue/green, canary, linear&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Managed traffic shifting requires ALB, NLB, or Service Connect. ALB examples use primary and alternate target groups. ECS controls the listener-rule changes and traffic movement.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Key distinction&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Canary and linear are NOT sub-modes of blue/green in native ECS. They are separate native strategies.&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;em&gt;Summary of the practical differences across deployment models.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I would choose in practice
&lt;/h2&gt;

&lt;p&gt;If I were setting standards for an AWS platform team today, I would keep the defaults simple.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use rolling when&lt;/strong&gt; the service is low risk, backward compatibility is strong, and traffic shaping is unnecessary.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use native blue/green when&lt;/strong&gt; you want safer releases than rolling, you want validation before production cutover, and you do not need a gradual production ramp.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use native canary when&lt;/strong&gt; blast radius matters most and you want a small real-user slice before full cutover.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use native linear when&lt;/strong&gt; you want the smoothest progressive rollout and more checkpoints than canary provides.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Keep legacy CodeDeploy blue/green when&lt;/strong&gt; services already run it successfully and migration would create more near-term risk than value.&lt;/p&gt;

&lt;p&gt;Legacy CodeDeploy still matters because many production estates still run it, but the center of gravity for new ECS design has clearly moved toward native ECS deployment strategies.&lt;/p&gt;

&lt;h2&gt;
  
  
  The most common mistakes to avoid
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Do not say native blue/green "contains" canary and linear. In native ECS, those are separate strategies.&lt;/li&gt;
&lt;li&gt;Do not draw legacy canary or linear as NLB-based. The legacy NLB path supports all-at-once.&lt;/li&gt;
&lt;li&gt;Do not imply rolling always uses exactly one target group. Rolling usually stays on the same target-group path, but ECS can support multiple target groups for some service designs.&lt;/li&gt;
&lt;li&gt;Do not treat native ECS traffic shifting as a CodeDeploy feature. ECS supports native BLUE_GREEN, CANARY, and LINEAR with the ECS deployment controller.&lt;/li&gt;
&lt;li&gt;Do not mix task-set language with service-revision language. Legacy CodeDeploy ECS blue/green uses task sets; native ECS deployments use service revisions.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Where to go next
&lt;/h2&gt;

&lt;p&gt;This post compares legacy and ECS-native deployment strategies, but ECS-native deployments now include more capabilities than can comfortably fit into a comparison article. Built-in blue/green, canary, and linear deployment strategies support &lt;strong&gt;deployment lifecycle hooks&lt;/strong&gt; — Lambda functions invoked at &lt;a href="https://docs.aws.amazon.com/AmazonECS/latest/developerguide/deployment-lifecycle-hooks.html" rel="noopener noreferrer"&gt;defined stages of a deployment&lt;/a&gt;, so teams can run automated validation, smoke tests, or custom checks before traffic shifts further.&lt;/p&gt;

&lt;p&gt;These hooks pair naturally with a &lt;strong&gt;test listener or listener rule&lt;/strong&gt; for pre-production validation against the green target group, as described in the &lt;a href="https://docs.aws.amazon.com/AmazonECS/latest/developerguide/blue-green-deployment-how-it-works.html" rel="noopener noreferrer"&gt;ECS blue/green deployment workflow&lt;/a&gt;. They also work well with &lt;strong&gt;CloudWatch alarms and bake windows&lt;/strong&gt;, where a failing alarm can trigger an automated rollback instead of requiring manual intervention. ECS canary and linear deployments extend the same idea by shifting production traffic gradually, giving teams multiple checkpoints to detect issues before 100% of traffic reaches the new revision.&lt;/p&gt;

&lt;p&gt;If you already run CodeDeploy blue/green deployments and are wondering whether to migrate, AWS has published an official AWS Containers Blog post, &lt;a href="https://aws.amazon.com/blogs/containers/migrating-from-aws-codedeploy-to-amazon-ecs-for-blue-green-deployments/" rel="noopener noreferrer"&gt;&lt;em&gt;Migrating from AWS CodeDeploy to Amazon ECS for blue/green deployments&lt;/em&gt;&lt;/a&gt;, that walks through the practical migration steps and key ECS deployment model changes. Pair it with the &lt;a href="https://docs.aws.amazon.com/AmazonECS/latest/developerguide/migrate-codedeploy-to-ecs-bluegreen.html" rel="noopener noreferrer"&gt;Migrate CodeDeploy blue/green deployments to Amazon ECS&lt;/a&gt; docs page for the reference details. Migration is not free — CodeDeploy deployments, ECS service revisions, and task sets use different mental models — but for most greenfield ECS services, the ECS-native model is now the better default going forward.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>aws</category>
      <category>ecs</category>
    </item>
    <item>
      <title>Monitoring &amp; Alerting for AWS EKS Using Grafana, Prometheus &amp; Alertmanager with SNS Integration</title>
      <dc:creator>Muhammad Ahmad Khan</dc:creator>
      <pubDate>Sun, 02 Feb 2025 20:53:27 +0000</pubDate>
      <link>https://forem.com/muhammad_ahmad_khan/monitoring-alerting-for-aws-eks-using-grafana-prometheus-alertmanager-with-sns-integration-5593</link>
      <guid>https://forem.com/muhammad_ahmad_khan/monitoring-alerting-for-aws-eks-using-grafana-prometheus-alertmanager-with-sns-integration-5593</guid>
      <description>&lt;p&gt;In this blog post, we'll walk through setting up a robust monitoring and alerting system for a Kubernetes cluster on AWS EKS. We'll use the &lt;code&gt;kube-prometheus-stack&lt;/code&gt; to deploy Prometheus and Grafana, configure an ALB Ingress for external access, and set up Amazon SNS as the receiver for Alertmanager to handle alerts.&lt;/p&gt;

&lt;p&gt;Using Amazon SNS as the alert receiver is particularly useful when an organization does not use Slack or wants a centralized way to distribute alerts via email. While Alertmanager supports email notifications, setting up email as a direct receiver requires configuring an SMTP configuration resulting in administrative overhead. With SNS, you can subscribe multiple email addresses to a single topic without additional SMTP setup, making it a simpler and more scalable solution.&lt;/p&gt;

&lt;p&gt;However, it's important to note that SNS has limitations when handling HTML content. Alerts sent via SNS will be received as plain text rather than formatted HTML, which may impact readability.&lt;/p&gt;




&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before we begin, ensure you have the following:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;An AWS account with a running EKS cluster.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;kubectl&lt;/code&gt; and &lt;code&gt;helm&lt;/code&gt; installed on your local machine.&lt;/li&gt;
&lt;li&gt;AWS CLI configured with the necessary credentials.&lt;/li&gt;
&lt;li&gt;AWS Load Balancer Controller installed in the EKS cluster.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;kubectl&lt;/code&gt; pointing to the correct cluster context.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Step 1: Install the &lt;code&gt;kube-prometheus-stack&lt;/code&gt; Using Helm
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;kube-prometheus-stack&lt;/code&gt; is a collection of Kubernetes manifests, Grafana dashboards, and Prometheus rules that provide easy deployment and management of Prometheus and Grafana on Kubernetes.&lt;/p&gt;

&lt;h3&gt;
  
  
  1.1 Add the Prometheus Community Helm Repository
&lt;/h3&gt;

&lt;p&gt;First, add the Prometheus Community Helm repository and update it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  1.2 Deploy the &lt;code&gt;kube-prometheus-stack&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Create a &lt;code&gt;prom-operator-values.yaml&lt;/code&gt; file to customize the deployment. For example, set up the Grafana admin password, some custom dashboards, grafana ingress and other configurations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;grafana&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;adminPassword&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;your-secure-password"&lt;/span&gt;
  &lt;span class="c1"&gt;### Provision grafana-dashboards-kubernetes ###&lt;/span&gt;
  &lt;span class="na"&gt;dashboardProviders&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;dashboardproviders.yaml&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
      &lt;span class="na"&gt;providers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;grafana-dashboards-kubernetes'&lt;/span&gt;
        &lt;span class="na"&gt;orgId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
        &lt;span class="na"&gt;folder&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Kubernetes'&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;file&lt;/span&gt;
        &lt;span class="na"&gt;disableDeletion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
        &lt;span class="na"&gt;editable&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
        &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/var/lib/grafana/dashboards/grafana-dashboards-kubernetes&lt;/span&gt;
  &lt;span class="na"&gt;dashboards&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;grafana-dashboards-kubernetes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;k8s-system-api-server&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://raw.githubusercontent.com/dotdc/grafana-dashboards-kubernetes/master/dashboards/k8s-system-api-server.json&lt;/span&gt;
        &lt;span class="na"&gt;token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;
      &lt;span class="na"&gt;k8s-system-coredns&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://raw.githubusercontent.com/dotdc/grafana-dashboards-kubernetes/master/dashboards/k8s-system-coredns.json&lt;/span&gt;
        &lt;span class="na"&gt;token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;
      &lt;span class="na"&gt;k8s-views-global&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://raw.githubusercontent.com/dotdc/grafana-dashboards-kubernetes/master/dashboards/k8s-views-global.json&lt;/span&gt;
        &lt;span class="na"&gt;token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;
      &lt;span class="na"&gt;k8s-views-namespaces&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://raw.githubusercontent.com/dotdc/grafana-dashboards-kubernetes/master/dashboards/k8s-views-namespaces.json&lt;/span&gt;
        &lt;span class="na"&gt;token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;
      &lt;span class="na"&gt;k8s-views-nodes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://raw.githubusercontent.com/dotdc/grafana-dashboards-kubernetes/master/dashboards/k8s-views-nodes.json&lt;/span&gt;
        &lt;span class="na"&gt;token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;
      &lt;span class="na"&gt;k8s-views-pods&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://raw.githubusercontent.com/dotdc/grafana-dashboards-kubernetes/master/dashboards/k8s-views-pods.json&lt;/span&gt;
        &lt;span class="na"&gt;token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;
  &lt;span class="na"&gt;ingress&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;annotations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;kubernetes.io/ingress.class&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;alb&lt;/span&gt;
      &lt;span class="na"&gt;alb.ingress.kubernetes.io/load-balancer-name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;grafana-alb&lt;/span&gt;
      &lt;span class="na"&gt;alb.ingress.kubernetes.io/scheme&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;internet-facing&lt;/span&gt;
      &lt;span class="na"&gt;alb.ingress.kubernetes.io/target-type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ip&lt;/span&gt;
      &lt;span class="na"&gt;alb.ingress.kubernetes.io/listen-ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;[{"HTTP":&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;80},&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;{"HTTPS":&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;443}]'&lt;/span&gt;
      &lt;span class="na"&gt;alb.ingress.kubernetes.io/ssl-redirect&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;443'&lt;/span&gt;
      &lt;span class="na"&gt;alb.ingress.kubernetes.io/certificate-arn&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;arn:aws:acm:&amp;lt;AWS_REGION&amp;gt;:&amp;lt;ACCOUNT_ID&amp;gt;:certificate/a0ff498e-62fd-4397-8d4a-626360465d32&lt;/span&gt;
    &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="na"&gt;hosts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;grafana-test.apps.xyz-company.com&lt;/span&gt;
    &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{}&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Deploy the &lt;code&gt;kube-prometheus-stack&lt;/code&gt; using Helm:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;helm upgrade &lt;span class="nt"&gt;--install&lt;/span&gt; k-prom-stack prometheus-community/kube-prometheus-stack &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--namespace&lt;/span&gt; monitoring &lt;span class="nt"&gt;--create-namespace&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--values&lt;/span&gt; prom-operator-values.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command installs Prometheus, Grafana, and Alertmanager in the &lt;code&gt;monitoring&lt;/code&gt; namespace.&lt;/p&gt;

&lt;h3&gt;
  
  
  1.3 Access Prometheus and Alertmanager UIs
&lt;/h3&gt;

&lt;p&gt;To access the Prometheus and Alertmanager web UIs, use &lt;code&gt;kubectl port-forward&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Prometheus UI:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  kubectl port-forward svc/prometheus-operated 9090:9090 &lt;span class="nt"&gt;--namespace&lt;/span&gt; monitoring
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open your browser and go to &lt;code&gt;http://localhost:9090&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Alertmanager UI:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  kubectl port-forward svc/alertmanager-operated 9093:9093 &lt;span class="nt"&gt;--namespace&lt;/span&gt; monitoring
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open your browser and go to &lt;code&gt;http://localhost:9093&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;To access the Grafana web UI:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Create a DNS entry mapping the domain define in ingress with created loadbalancer url.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Use &lt;code&gt;admin&lt;/code&gt; as the username and the password defined in &lt;code&gt;prom-operator-values.yaml&lt;/code&gt; to log in to Grafana.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Step 2: Set Up Amazon SNS for Alertmanager
&lt;/h2&gt;

&lt;p&gt;To receive alerts via email or other channels, we'll configure Amazon SNS as the receiver for Alertmanager.&lt;/p&gt;

&lt;h3&gt;
  
  
  2.1 Create an SNS Topic
&lt;/h3&gt;

&lt;p&gt;Create an SNS topic to receive alerts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws sns create-topic &lt;span class="nt"&gt;--name&lt;/span&gt; alertTopic
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note the ARN (Amazon Resource Name) from the output, as it will be needed in later steps.&lt;/p&gt;

&lt;h3&gt;
  
  
  2.2 Create an Email Subscription for SNS
&lt;/h3&gt;

&lt;p&gt;Subscribe your email to the SNS topic to receive notifications:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws sns subscribe &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--topic-arn&lt;/span&gt; arn:aws:sns:&amp;lt;AWS_REGION&amp;gt;:&amp;lt;ACCOUNT_ID&amp;gt;:alertTopic &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--protocol&lt;/span&gt; email &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--notification-endpoint&lt;/span&gt; &amp;lt;your-email@example.com&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check your email inbox for a confirmation message and click the link to activate the subscription.&lt;/p&gt;

&lt;h3&gt;
  
  
  2.3 Create a New IAM Role for EKS Node Group to Assume and Publish to SNS
&lt;/h3&gt;

&lt;h4&gt;
  
  
  2.3.1 Create a Trust Relationship
&lt;/h4&gt;

&lt;p&gt;Create a trust relationship policy to allow the EKS node group role to assume the new role:&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;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt; &amp;gt; trust-policy.json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::&amp;lt;ACCOUNT_ID&amp;gt;:role/eks-node-group-role"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create the IAM role:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws iam create-role &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--role-name&lt;/span&gt; alertmanager_role &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--assume-role-policy-document&lt;/span&gt; file://trust-policy.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  2.3.2 Attach Permissions to Publish to SNS
&lt;/h4&gt;

&lt;p&gt;Create a policy to allow the role to publish messages to the SNS topic:&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;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt; &amp;gt; sns-policy.json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "sns:Publish",
      "Resource": "arn:aws:sns:&amp;lt;AWS_REGION&amp;gt;:&amp;lt;ACCOUNT_ID&amp;gt;:alertTopic"
    }
  ]
}
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Attach the policy to the new role:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws iam put-role-policy &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--role-name&lt;/span&gt; alertmanager_role &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--policy-name&lt;/span&gt; SNSPublishPolicy &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--policy-document&lt;/span&gt; file://sns-policy.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2.4 Create a Resource-Based Policy for SNS
&lt;/h3&gt;

&lt;p&gt;Allow this new role named &lt;code&gt;alertmanager_role&lt;/code&gt; to publish alerts to the SNS topic by attaching a resource-based policy:&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;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt; &amp;gt; sns-resource-policy.json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::&amp;lt;ACCOUNT_ID&amp;gt;:role/alertmanager_role"
      },
      "Action": "sns:Publish",
      "Resource": "arn:aws:sns:&amp;lt;AWS_REGION&amp;gt;:&amp;lt;ACCOUNT_ID&amp;gt;:alertTopic"
    }
  ]
}
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Apply the policy to the SNS topic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws sns set-topic-attributes &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--topic-arn&lt;/span&gt; arn:aws:sns:&amp;lt;AWS_REGION&amp;gt;:&amp;lt;ACCOUNT_ID&amp;gt;:alertTopic &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--attribute-name&lt;/span&gt; Policy &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--attribute-value&lt;/span&gt; file://sns-resource-policy.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2.5 Update the IAM Role for the EKS Node Group
&lt;/h3&gt;

&lt;p&gt;Allow the EKS node group to assume this new role named &lt;code&gt;alertmanager_role&lt;/code&gt; by attaching a policy:&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;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt; &amp;gt; eks-assume-policy.json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "sts:AssumeRole",
      "Resource": "arn:aws:iam::&amp;lt;ACCOUNT_ID&amp;gt;:role/alertmanager_role"
    }
  ]
}
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Attach the policy to the EKS node group role:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws iam put-role-policy &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--role-name&lt;/span&gt; eks-node-group-role &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--policy-name&lt;/span&gt; assume_alertmanager_role_policy &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--policy-document&lt;/span&gt; file://eks-assume-policy.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;your EKS node Group role might be different than mine 
e.g. &lt;code&gt;eks-node-group-role&lt;/code&gt; &lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Step 3: Configure Alertmanager to Send Alerts to SNS
&lt;/h2&gt;

&lt;p&gt;Update the &lt;code&gt;alertmanager.yml&lt;/code&gt; configuration to send alerts to the SNS topic in same helm values file named as &lt;code&gt;prom-operator-values.yaml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;alertmanager&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;global&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;resolve_timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;5m&lt;/span&gt;
    &lt;span class="na"&gt;route&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;group_by&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;job'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;alertname'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;priority'&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;group_wait&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;30s&lt;/span&gt;
      &lt;span class="na"&gt;group_interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;5m&lt;/span&gt;
      &lt;span class="na"&gt;repeat_interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;12h&lt;/span&gt;
      &lt;span class="na"&gt;receiver&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;sns-receivers'&lt;/span&gt;
    &lt;span class="na"&gt;receivers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;null'&lt;/span&gt;  &lt;span class="c1"&gt;# Add this to your config as well&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sns-receivers&lt;/span&gt;
      &lt;span class="na"&gt;sns_configs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;api_url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://sns.&amp;lt;AWS_REGION&amp;gt;.amazonaws.com&lt;/span&gt;
          &lt;span class="na"&gt;topic_arn&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;arn:aws:sns:&amp;lt;AWS_REGION&amp;gt;:&amp;lt;ACCOUNT_ID&amp;gt;:alertTopic&lt;/span&gt;
          &lt;span class="na"&gt;sigv4&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;region&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;AWS_REGION&amp;gt;&lt;/span&gt;
            &lt;span class="na"&gt;role_arn&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;arn:aws:iam::&amp;lt;ACCOUNT_ID&amp;gt;:role/alertmanager_role&lt;/span&gt;
          &lt;span class="na"&gt;subject&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;[{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;.Status&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;|&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;toUpper&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;if&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;eq&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;.Status&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;"firing"&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}:{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;.Alerts.Firing&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;|&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;len&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;end&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}]'&lt;/span&gt;
          &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|-&lt;/span&gt;
            &lt;span class="s"&gt;{{ if gt (len .Alerts.Firing) 0 }}&lt;/span&gt;
            &lt;span class="s"&gt;Alerts Firing:&lt;/span&gt;
            &lt;span class="s"&gt;{{ template "__text_alert_list_markdown" .Alerts.Firing }}&lt;/span&gt;
            &lt;span class="s"&gt;{{ end }}&lt;/span&gt;
            &lt;span class="s"&gt;{{ if gt (len .Alerts.Resolved) 0 }}&lt;/span&gt;
            &lt;span class="s"&gt;Alerts Resolved:&lt;/span&gt;
            &lt;span class="s"&gt;{{ template "__text_alert_list_markdown" .Alerts.Resolved }}&lt;/span&gt;
            &lt;span class="s"&gt;{{ end }}&lt;/span&gt;
          &lt;span class="na"&gt;send_resolved&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;  &lt;span class="c1"&gt;# Sends notification when alert is resolved&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Apply the updated configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;helm upgrade &lt;span class="nt"&gt;--install&lt;/span&gt; k-prom-stack prometheus-community/kube-prometheus-stack &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--namespace&lt;/span&gt; monitoring &lt;span class="nt"&gt;--create-namespace&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--values&lt;/span&gt; prom-operator-values.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 4: Configure custom rules for custom alerts
&lt;/h2&gt;

&lt;p&gt;Update the &lt;code&gt;prom-operator-values.yaml&lt;/code&gt; to add:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;additionalPrometheusRulesMap&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;custom-rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;groups&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;customGroupA.rules&lt;/span&gt;
      &lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;alert&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Custom-Alert Instance High CPU Utilization&lt;/span&gt;
        &lt;span class="na"&gt;annotations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;CPU usage had been over 75% for 5 minutes | Current usage is {{ $value | printf "%.2f" }}%&lt;/span&gt;
          &lt;span class="na"&gt;summary&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;CPU usage is over 75% (instance {{ $labels.instance }})&lt;/span&gt;
        &lt;span class="na"&gt;expr&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;100 - (avg by(instance) (irate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) &amp;gt; &lt;/span&gt;&lt;span class="m"&gt;75&lt;/span&gt;
        &lt;span class="na"&gt;for&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;5m&lt;/span&gt;
        &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;severity&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;critical&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;alert&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Custom-Alert Instance High Memory Utilization&lt;/span&gt;
        &lt;span class="na"&gt;annotations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Memory usage had been over 75% for 5 minutes | Current usage is {{ $value | printf "%.2f" }}%&lt;/span&gt;
          &lt;span class="na"&gt;summary&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Memory usage is over 75% (instance {{ $labels.instance }})&lt;/span&gt;
        &lt;span class="na"&gt;expr&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;100 - (sum by(instance) (node_memory_MemAvailable_bytes) / sum by(instance) (node_memory_MemTotal_bytes) * 100) &amp;gt; &lt;/span&gt;&lt;span class="m"&gt;75&lt;/span&gt;
        &lt;span class="na"&gt;for&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;5m&lt;/span&gt;
        &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;severity&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;critical&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Apply the updated configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;helm upgrade &lt;span class="nt"&gt;--install&lt;/span&gt; k-prom-stack prometheus-community/kube-prometheus-stack &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--namespace&lt;/span&gt; monitoring &lt;span class="nt"&gt;--create-namespace&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--values&lt;/span&gt; prom-operator-values.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;The complete helm values file can be found on GitHub &lt;a href="https://github.com/muhammad-ahmad-khan/EKS-Monitoring-Solution/blob/main/EKS-Monitoring-With-SNS/prom-operator-values.yaml" rel="noopener noreferrer"&gt;here&lt;/a&gt; &lt;/p&gt;

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

&lt;p&gt;In this blog post, we set up a comprehensive monitoring and alerting system for a Kubernetes cluster running on AWS EKS. We deployed Prometheus and Grafana using the &lt;code&gt;kube-prometheus-stack&lt;/code&gt;, configured an ALB Ingress for external access to Grafana, and set up Amazon SNS as the receiver for Alertmanager to receive alerts.&lt;/p&gt;

&lt;p&gt;With this setup, you can now monitor your Kubernetes cluster, visualize metrics using Grafana, and receive alerts via Alertmanager and SNS. This ensures that your cluster is both observable and resilient to issues.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>aws</category>
      <category>eks</category>
      <category>monitoring</category>
    </item>
    <item>
      <title>Rapidly Deploy ECS Infrastructure on AWS with AWS CDK (TypeScript)</title>
      <dc:creator>Muhammad Ahmad Khan</dc:creator>
      <pubDate>Sat, 25 Jan 2025 02:22:52 +0000</pubDate>
      <link>https://forem.com/muhammad_ahmad_khan/rapidly-deploy-ecs-infrastructure-on-aws-with-aws-cdk-typescript-1fpk</link>
      <guid>https://forem.com/muhammad_ahmad_khan/rapidly-deploy-ecs-infrastructure-on-aws-with-aws-cdk-typescript-1fpk</guid>
      <description>&lt;p&gt;When building and deploying containerized applications on AWS, Amazon ECS (Elastic Container Service) provides a powerful and flexible solution. AWS CDK (Cloud Development Kit) simplifies the process of creating ECS infrastructure by providing a programmatic approach to define cloud resources in code. This blog post walks through how to rapidly deploy ECS infrastructure using the &lt;code&gt;aws-cdk-lib/aws-ecs-patterns&lt;/code&gt; module, which abstracts complex tasks like load balancing, service management, and autoscaling.&lt;/p&gt;

&lt;p&gt;We’ll cover how to create an ECS Fargate service, set up an Application Load Balancer (ALB), enable auto-scaling, and integrate AWS Secrets Manager for securely managing sensitive data, all with just a few lines of TypeScript code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before getting started, make sure you have the following:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Node.js&lt;/strong&gt; and &lt;strong&gt;npm&lt;/strong&gt; installed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AWS CLI&lt;/strong&gt; configured with appropriate permissions to create resources. You can configure it with:
&lt;/li&gt;
&lt;/ol&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;ol&gt;
&lt;li&gt;
&lt;strong&gt;AWS CDK&lt;/strong&gt; installed globally:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; aws-cdk
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Docker&lt;/strong&gt; installed (optional but useful for container builds).&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Setting Up the Project
&lt;/h2&gt;

&lt;p&gt;To begin, create a new directory for your CDK project and initialize it with TypeScript:&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;mkdir &lt;/span&gt;ecs-fargate
&lt;span class="nb"&gt;cd &lt;/span&gt;ecs-fargate
cdk init app &lt;span class="nt"&gt;--language&lt;/span&gt; typescript
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will generate the basic project structure for an AWS CDK application. Next, install the necessary dependencies for ECS, VPC, ECR, and other related services:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @aws-cdk/aws-ec2 @aws-cdk/aws-ecs @aws-cdk/aws-ecr @aws-cdk/aws-ecs-patterns @aws-cdk/aws-iam @aws-cdk/aws-elasticloadbalancingv2 @aws-cdk/aws-certificatemanager @aws-cdk/aws-secretsmanager
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you're ready to start defining your infrastructure!&lt;/p&gt;

&lt;h2&gt;
  
  
  Using &lt;code&gt;aws-cdk-lib/aws-ecs-patterns&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;aws-cdk-lib/aws-ecs-patterns&lt;/code&gt; module simplifies ECS service setup by providing high-level abstractions for common ECS patterns. It includes constructs for easy configuration of services like Application Load Balanced Fargate services, which handle the complexity of setting up load balancers, auto-scaling, and other integrations.&lt;/p&gt;

&lt;p&gt;For example, the &lt;code&gt;ApplicationLoadBalancedFargateService&lt;/code&gt; construct automatically creates an ECS Fargate service with an Application Load Balancer (ALB) in front of it, managing much of the networking and routing for you.&lt;/p&gt;

&lt;p&gt;Here’s a quick look at some key benefits of using the &lt;code&gt;ApplicationLoadBalancedFargateService&lt;/code&gt; construct from the &lt;code&gt;aws-cdk-lib/aws-ecs-patterns&lt;/code&gt; module:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Simplified Infrastructure Setup&lt;/strong&gt;: You don't need to manually define an Application Load Balancer or its target group—everything is handled for you.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Built-in Auto-Scaling&lt;/strong&gt;: Easily configure auto-scaling for your Fargate tasks based on CPU or memory usage.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HTTPS Setup&lt;/strong&gt;: You can easily set up SSL certificates via ACM (AWS Certificate Manager) and configure the load balancer to use HTTPS.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For more details on other architectural patterns, you can refer to the official &lt;a href="https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_ecs_patterns-readme.html" rel="noopener noreferrer"&gt;AWS CDK ECS Patterns Documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Defining the ECS Fargate Infrastructure
&lt;/h2&gt;

&lt;p&gt;Here’s an example that demonstrates how to quickly deploy ECS infrastructure with AWS CDK using TypeScript.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example: ECS Fargate Service with Application Load Balancer
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;cdk&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws-cdk-lib&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Construct&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;constructs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;ec2&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws-cdk-lib/aws-ec2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;ecs&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws-cdk-lib/aws-ecs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;ecr&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws-cdk-lib/aws-ecr&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;ecs_patterns&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws-cdk-lib/aws-ecs-patterns&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;iam&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws-cdk-lib/aws-iam&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;elbv2&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws-cdk-lib/aws-elasticloadbalancingv2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;acm&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws-cdk-lib/aws-certificatemanager&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;secretsmanager&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws-cdk-lib/aws-secretsmanager&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;EcsFargateStackProps&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;cdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;StackProps&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;vpcId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// VPC Id&lt;/span&gt;
  &lt;span class="nl"&gt;ecrRepositoryName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// ECR repository name&lt;/span&gt;
  &lt;span class="nl"&gt;acmCertificateArn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// ACM certificate ARN for HTTPS&lt;/span&gt;
  &lt;span class="nl"&gt;demoServiceSecretArn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Secret ARN for demoService&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

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

    &lt;span class="c1"&gt;// Lookup existing VPC using VPC ID from props&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;vpc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ec2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromLookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;EcsVpc&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;vpcId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vpcId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="c1"&gt;// Reference the existing ECR repository&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ecrRepo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ecr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Repository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromRepositoryName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ExistingEcrRepository&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ecrRepositoryName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// ECS Cluster&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cluster&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;ecs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Cluster&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;EcsCluster&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;clusterName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;demo-service-EcsFargateCluster&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;vpc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="c1"&gt;// Task Definition&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;taskDefinition&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;ecs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;FargateTaskDefinition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;FargateTaskDef&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;memoryLimitMiB&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;512&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;cpu&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;256&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="c1"&gt;// Reference the secret from AWS Secrets Manager&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;taskSecret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;secretsmanager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Secret&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromSecretCompleteArn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;FargateTaskSecret&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;demoServiceSecretArn&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Add a container to the task definition&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;container&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;taskDefinition&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addContainer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;AppContainer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ecs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ContainerImage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromEcrRepository&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ecrRepo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;latest&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ecs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;LogDriver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;awsLogs&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;streamPrefix&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ecs-app&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
      &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;DEBUG&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;true&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Simple environment variable&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;secrets&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;SECRET_KEY&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ecs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Secret&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromSecretsManager&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;taskSecret&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SECRET_KEY&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="c1"&gt;// Referencing secret for SECRET_KEY&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="nx"&gt;container&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addPortMappings&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;containerPort&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Container runs on port 5000&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="c1"&gt;// ACM Certificate for HTTPS&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;certificate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;acm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Certificate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromCertificateArn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;AcmCertificate&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;acmCertificateArn&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Application Load Balanced Fargate Service&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fargateService&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;ecs_patterns&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ApplicationLoadBalancedFargateService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;FargateService&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;cluster&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;taskDefinition&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;enableExecuteCommand&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Enable execute command&lt;/span&gt;
      &lt;span class="nx"&gt;certificate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;publicLoadBalancer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;loadBalancerName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;demo-service-alb&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;sslPolicy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;elbv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SslPolicy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;RECOMMENDED&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;serviceName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;demo-service-EcsFargateService&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;redirectHTTP&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;protocol&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;elbv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ApplicationProtocol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;HTTPS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Default HTTPS&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="c1"&gt;// Adjust target group port to match the container port&lt;/span&gt;
    &lt;span class="nx"&gt;fargateService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;targetGroup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;configureHealthCheck&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;5000&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Health check targets container port 5000&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="c1"&gt;// Autoscaling&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;scaling&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fargateService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;autoScaleTaskCount&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;minCapacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;maxCapacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="nx"&gt;scaling&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scaleOnCpuUtilization&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;CpuScaling&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;targetUtilizationPercent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="nx"&gt;scaling&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scaleOnMemoryUtilization&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;MemoryScaling&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;targetUtilizationPercent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="c1"&gt;// Outputs&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;cdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;CfnOutput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ClusterName&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cluster&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clusterName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ECS Cluster Name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;cdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;CfnOutput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ServiceName&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;fargateService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;serviceName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ECS Service Name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Breakdown of the Code:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;VPC and ECS Cluster&lt;/strong&gt;: We use &lt;code&gt;ec2.Vpc.fromLookup()&lt;/code&gt; to reference an existing VPC based on the VPC ID passed in as a parameter, and create an ECS cluster within that VPC.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;ECR Repository&lt;/strong&gt;: The Docker image for the application is stored in an existing ECR repository. We reference this repository using &lt;code&gt;ecr.Repository.fromRepositoryName()&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Task Definition&lt;/strong&gt;: We define an ECS Fargate task with specified memory and CPU limits, referencing a Docker image from ECR. We also integrate environment variables and sensitive data from AWS Secrets Manager.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Load Balancer and HTTPS&lt;/strong&gt;: The &lt;code&gt;ApplicationLoadBalancedFargateService&lt;/code&gt; construct from &lt;code&gt;aws-cdk-lib/aws-ecs-patterns&lt;/code&gt; automatically provisions an ALB, configuring SSL termination using an ACM certificate for HTTPS.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Auto-Scaling&lt;/strong&gt;: We configure auto-scaling for the Fargate service based on CPU and memory utilization, ensuring the service can dynamically adjust to demand.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Outputs&lt;/strong&gt;: Finally, we output the ECS cluster name and service name, so they can be easily referenced.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Deploying the Stack
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1: Install Dependencies
&lt;/h3&gt;

&lt;p&gt;Ensure that all dependencies are installed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Bootstrap the CDK Environment
&lt;/h3&gt;

&lt;p&gt;Run the bootstrap command to prepare your AWS account for CDK:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;cdk bootstrap &lt;span class="nt"&gt;--profile&lt;/span&gt; your-aws-profile
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3: Synthesize the Stack
&lt;/h3&gt;

&lt;p&gt;Synthesize the CloudFormation template to verify the stack:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;cdk synth &lt;span class="nt"&gt;--context&lt;/span&gt; &lt;span class="nv"&gt;vpcId&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;vpc-xxxxxxxx &lt;span class="nt"&gt;--context&lt;/span&gt; &lt;span class="nv"&gt;ecrRepositoryName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;demo-service &lt;span class="nt"&gt;--context&lt;/span&gt; &lt;span class="nv"&gt;acmCertificateArn&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;arn:aws:acm:region:account-id:certificate/certificate-id &lt;span class="nt"&gt;--context&lt;/span&gt; &lt;span class="nv"&gt;demoServiceSecretArn&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;arn:aws:secretsmanager:region:account-id:secret:secret-id &lt;span class="nt"&gt;--profile&lt;/span&gt; your-aws-profile
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 4: Deploy the Stack
&lt;/h3&gt;

&lt;p&gt;Deploy the ECS Fargate stack to AWS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;cdk deploy &lt;span class="nt"&gt;--context&lt;/span&gt; &lt;span class="nv"&gt;vpcId&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;vpc-xxxxxxxx &lt;span class="nt"&gt;--context&lt;/span&gt; &lt;span class="nv"&gt;ecrRepositoryName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;demo-service &lt;span class="nt"&gt;--context&lt;/span&gt; &lt;span class="nv"&gt;acmCertificateArn&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;arn:aws:acm:region:account-id:certificate/certificate-id &lt;span class="nt"&gt;--context&lt;/span&gt; &lt;span class="nv"&gt;demoServiceSecretArn&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;arn:aws:secretsmanager:region:account-id:secret:secret-id &lt;span class="nt"&gt;--profile&lt;/span&gt; your-aws-profile
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 4: Verify the Deployment
&lt;/h3&gt;

&lt;p&gt;After the stack is successfully deployed, AWS will automatically provision the ECS service, ALB, and other necessary resources. You can access the service via the load balancer’s public endpoint.&lt;/p&gt;

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

&lt;p&gt;In this guide, we’ve rapidly provisioned ECS infrastructure using AWS CDK and TypeScript. By leveraging the &lt;code&gt;ApplicationLoadBalancedFargateService&lt;/code&gt; construct from &lt;code&gt;aws-cdk-lib/aws-ecs-patterns&lt;/code&gt;, we were able to simplify the creation of a fully managed ECS service that includes an ALB, auto-scaling, and HTTPS support. &lt;/p&gt;

&lt;p&gt;This approach significantly reduces the complexity of deploying containerized applications on AWS, allowing you to focus on developing your application instead of managing infrastructure.&lt;/p&gt;

&lt;p&gt;For more detailed information, check out the official &lt;a href="https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_ecs_patterns.ApplicationLoadBalancedFargateService.html" rel="noopener noreferrer"&gt;AWS CDK ECS Patterns Documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  GitHub Repository
&lt;/h3&gt;

&lt;p&gt;You can find the complete code for this deployment in my &lt;a href="https://github.com/muhammad-ahmad-khan/AWS-CDK-ECS-Infra" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>aws</category>
      <category>ecs</category>
      <category>cdk</category>
    </item>
    <item>
      <title>Integrating Multiple EKS Clusters with ArgoCD for Simplifying Kubernetes Operations</title>
      <dc:creator>Muhammad Ahmad Khan</dc:creator>
      <pubDate>Tue, 26 Mar 2024 19:49:52 +0000</pubDate>
      <link>https://forem.com/muhammad_ahmad_khan/integrating-multiple-eks-clusters-with-argocd-for-simplifying-kubernetes-operations-nja</link>
      <guid>https://forem.com/muhammad_ahmad_khan/integrating-multiple-eks-clusters-with-argocd-for-simplifying-kubernetes-operations-nja</guid>
      <description>&lt;p&gt;In the rapidly evolving landscape of cloud-native technologies, Kubernetes has emerged as the de facto standard for container orchestration. As organizations scale their infrastructure, managing multiple Kubernetes clusters becomes inevitable. With this growth comes the challenge of ensuring consistency, reliability, and efficiency across all clusters. Enter ArgoCD, a powerful tool for continuous delivery and GitOps workflows in Kubernetes. In this blog post, we'll explore why integrating multiple clusters in ArgoCD is essential and how we can integrate multiple AWS EKS clusters in ArgoCD.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Integration is Required
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Centralized Management:&lt;/strong&gt; Managing multiple Kubernetes clusters manually can be daunting and error-prone. Integrating them with ArgoCD provides a centralized platform for managing and deploying applications across all clusters, streamlining operations and reducing complexity.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Consistency and Standardization:&lt;/strong&gt; Different clusters may have varying configurations, making it difficult to maintain consistency in deployments. ArgoCD ensures that configurations and deployments are standardized across all clusters, promoting best practices and ensuring uniformity.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scalability:&lt;/strong&gt; As organizations grow, they often adopt a multi-cluster strategy to distribute workloads and improve fault tolerance. Integrating these clusters with ArgoCD enables seamless scaling of applications across clusters, allowing organizations to leverage resources efficiently.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Monitoring [Health Checks and Logging]:&lt;/strong&gt; ArgoCD offers insights into the deployment status and health of applications across clusters through its user interface and API. By integrating multiple clusters, organizations gain centralized visibility and can monitor the status of applications across all clusters from a single dashboard.&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%2Fboz3fp10s8eigg383fpw.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%2Fboz3fp10s8eigg383fpw.png" alt="Image description" width="800" height="344"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Integrate Multiple AWS EKS Clusters in ArgoCD
&lt;/h2&gt;

&lt;p&gt;Let's say we have AWS accounts as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Account &lt;code&gt;A&lt;/code&gt; with account id: &lt;code&gt;111111111111&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Account &lt;code&gt;B&lt;/code&gt; with account id: &lt;code&gt;222222222222&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Account &lt;code&gt;C&lt;/code&gt; with account id: &lt;code&gt;333333333333&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Account &lt;code&gt;A&lt;/code&gt; is where ArgoCD runs.&lt;/p&gt;

&lt;p&gt;To authenticate and access the external cluster we need to add the configuration as follows:&lt;/p&gt;

&lt;p&gt;In Account &lt;code&gt;A&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create an IAM role named &lt;code&gt;argocd-manager&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Create a role policy named &lt;code&gt;argocd-role-policy&lt;/code&gt; and attach it to a role named &lt;code&gt;argocd-manager&lt;/code&gt; having the assume role policy given below&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;RolePolicyDocument&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cat &amp;gt;argocd-role-policy.json &amp;lt;&amp;lt;EOF
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "sts:AssumeRole",
            "Resource": "*"
        }
    ]
}
EOF
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;AssumeRolePolicyDocument&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Federated": "arn:aws:iam::111111111111:oidc-provider/oidc.eks.region-code.amazonaws.com/id/EXAMPLED539D4633E53DE1B71EXAMPLE"
            },
            "Action": "sts:AssumeRoleWithWebIdentity",
            "Condition": {
                "StringEquals": {
                    "oidc.eks.region-code.amazonaws.com/id/EXAMPLED539D4633E53DE1B71EXAMPLE:sub": [ "system:serviceaccount:argocd:argocd-server", "system:serviceaccount:argocd:argocd-application-controller" ]
                    "oidc.eks.region-code.amazonaws.com/id/EXAMPLED539D4633E53DE1B71EXAMPLE:aud": "sts.amazonaws.com"
                }
            }
        }
    ]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now In Account &lt;code&gt;B&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create an IAM role named &lt;code&gt;deployer&lt;/code&gt; having trust relationship as follows:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::11111111111:role/argocd-manager"
            },
            "Action": "sts:AssumeRole"
        }
    ]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Map this role in aws &lt;code&gt;auth-config&lt;/code&gt; configmap Kubernetes object in Account &lt;code&gt;B&lt;/code&gt; EKS cluster
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl edit -n kube-system configmap/aws-auth


# Please edit the object below. Lines beginning with a '#' will be ignored,
# and an empty file will abort the edit. If an error occurs while saving this file will be
# reopened with the relevant failures.
#
apiVersion: v1
data:
  mapRoles: |
    - groups:
      - system:bootstrappers
      - system:nodes
      rolearn: arn:aws:iam::222222222222:role/my-role
      username: system:node:{{EC2PrivateDNSName}}
    - groups:
      - system:masters
      rolearn: arn:aws:iam::222222222222:role/deployer # deployer role arn
      username: deployer
  mapUsers: |
    - groups:
      - system:masters
      userarn: arn:aws:iam::222222222222:user/admin
      username: admin
    - groups:
      - system:masters      
      userarn: arn:aws:iam::222222222222:user/alpha-user
      username: my-user
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Follow the same procedure in Account &lt;code&gt;C&lt;/code&gt; as we have followed in Account &lt;code&gt;B&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In Account &lt;code&gt;A&lt;/code&gt; (where argocd is installed), add the following configuration in argocd helm chart values&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt;  IAM role &lt;code&gt;deployer&lt;/code&gt; must be created first in Account &lt;code&gt;B&lt;/code&gt; or &lt;code&gt;C&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;arn:aws:iam::222222222222:role/deployer&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;arn:aws:iam::333333333333:role/deployer&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;global:
  securityContext: # Set deployments securityContext/fsGroup to 999 so that the user of the docker image can use IAM Authenticator. We need this because the IAM Authenticator will try to mount a secret on /var/run/secrets/eks.amazonaws.com/serviceaccount/token. If the correct fsGroup (999 corresponds to the argocd user) isn’t set, this will fail.
    runAsGroup: 999
    fsGroup: 999

controller:
  serviceAccount:
    create: true
    name: argocd-application-controller
    annotations: {eks.amazonaws.com/role-arn: arn:aws:iam::111111111111:role/argocd-manager} # Account A - IAM role service account
    automountServiceAccountToken: true

server:
  serviceAccount:
    create: true
    name: argocd-server
    annotations: {eks.amazonaws.com/role-arn: arn:aws:iam::111111111111:role/argocd-manager} # Account A - IAM role service account
    automountServiceAccountToken: true

 configs:
  # -- Provide one or multiple [external cluster credentials]
  # @default -- `[]` (See [values.yaml])
  ## Ref:
  ## - https://argo-cd.readthedocs.io/en/stable/operator-manual/declarative-setup/#clusters
  ## - https://argo-cd.readthedocs.io/en/stable/operator-manual/security/#external-cluster-credentials
  ## - https://argo-cd.readthedocs.io/en/stable/user-guide/projects/#project-scoped-repositories-and-clusters
  clusterCredentials:
    - name: development
      server: https://xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.abc.region.eks.amazonaws.com # EKS cluster API server endpoint of Account B
      config:
        awsAuthConfig:
          clusterName: eks-development
          roleARN: arn:aws:iam::222222222222:role/deployer # Deployer role arn of Account B
        tlsClientConfig:
          # Base64 encoded PEM-encoded bytes (typically read from a client certificate file).
          caData: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx........==" # EKS cluster certificate authority
    - name: staging
      server: https://xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.abc.region.eks.amazonaws.com # EKS cluster API server endpoint of Account C
      config:
        awsAuthConfig:
          clusterName: eks-staging
          roleARN: arn:aws:iam::333333333333:role/deployer # Deployer role arn of Account C
        tlsClientConfig:
          # Base64 encoded PEM-encoded bytes (typically read from a client certificate file).
          caData: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx........==" # EKS cluster certificate authority
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Obtain the EKS certificate of the respective cluster using AWS CLI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;aws eks describe-cluster \
        --region=${AWS_DEFAULT_REGION} \
        --name=${CLUSTER_NAME} \
        --output=text \
        --query 'cluster.{certificateAuthorityData: certificateAuthority.data}' | base64 -D
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important thing to note is that we need to set deployments &lt;code&gt;securityContext/fsGroup&lt;/code&gt; to &lt;code&gt;999&lt;/code&gt; so that the user of the docker image can use IAM Authenticator. We need this because the IAM Authenticator will try to mount a secret on &lt;code&gt;/var/run/secrets/eks.amazonaws.com/serviceaccount/token&lt;/code&gt;. If the correct fsGroup (999 corresponds to the argocd user) isn’t set, this will fail.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>aws</category>
      <category>eks</category>
      <category>argocd</category>
    </item>
    <item>
      <title>Upgrading EKS Version with Zero Downtime Incorporating Custom Blue-Green Methodology</title>
      <dc:creator>Muhammad Ahmad Khan</dc:creator>
      <pubDate>Sat, 23 Mar 2024 21:22:21 +0000</pubDate>
      <link>https://forem.com/muhammad_ahmad_khan/upgrading-eks-version-with-zero-downtime-incorporating-custom-blue-green-methodology-3eop</link>
      <guid>https://forem.com/muhammad_ahmad_khan/upgrading-eks-version-with-zero-downtime-incorporating-custom-blue-green-methodology-3eop</guid>
      <description>&lt;p&gt;One of the most significant operational overheads of EKS is its version upgrade, which we have to tackle each quarter. Although AWS has announced extended support for older versions of EKS, the cost of extended support is something you do not want to incur. AWS maintains its standard support and extended support timeline in the following documentation: &lt;a href="https://docs.aws.amazon.com/eks/latest/userguide/kubernetes-versions.html#kubernetes-release-calendar" rel="noopener noreferrer"&gt;Kubernetes Release Calendar.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Keeping EKS clusters up to date with the latest versions is essential for security, performance, and access to new features. However, upgrading Kubernetes clusters can be challenging, especially in production environments where downtime is not an option. In this blog post, we'll explore how you can leverage the custom blue-green methodology to perform version upgrades on AWS EKS clusters seamlessly and with minimal disruption.&lt;/p&gt;

&lt;p&gt;The blue-green deployment strategy is a technique used to reduce downtime and risk when deploying updates to applications or infrastructure. In the context of AWS EKS version upgrades, the traditional blue-green methodology involves creating a new cluster with the updated Kubernetes version (green), migrating workloads from the old cluster (blue) to the new one, and then decommissioning the old cluster. However, in our case, we will maintain a blue cluster only to serve traffic when we plan to upgrade our original cluster (green). This means that our original cluster will always remain green (before and after the EKS upgrade), and traffic will only shift from the green to the blue cluster while we perform upgrades on the original cluster. After the successful upgrade, traffic will shift back from the blue cluster to the original cluster (green).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Steps to Perform EKS Version Upgrade using Custom Blue-Green Methodology:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Prepare for Upgrade:&lt;/strong&gt; Before initiating the upgrade process, thoroughly review the release notes for the new Kubernetes version to understand any changes or potential compatibility issues with your workloads. This includes checking deprecated resources and APIs, whether in your self-maintained application chart or in some open-source community chart. If you are using an open-source community chart, please ensure that the EKS version you are upgrading to has been tested with your installed chart.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Create a New Blue Cluster or Upgrade the Already Created Blue Cluster:&lt;/strong&gt; Using AWS EKS, provision a new Kubernetes cluster with the desired version. Ensure that the cluster configuration, including node groups, networking, and IAM roles, aligns with your requirements. If you have created a new EKS cluster, make sure to configure DNS and Load Balancers by updating DNS records or adjusting load balancer settings to direct traffic to the new cluster gradually when workloads are migrated. If your cluster is large and you have many Kubernetes Ops tools installed in Kubernetes using Helm charts, it is a good idea to maintain this separate cluster for each upgrade cycle to save time. If you are already maintaining the cluster, then you have to prepare this cluster for upgrade by performing the following tasks:

&lt;ul&gt;
&lt;li&gt;Upgrade the EKS control plane from the console.&lt;/li&gt;
&lt;li&gt;Upgrade each EKS Node group by setting the right update config as per the priority from the console and using Force Update.&lt;/li&gt;
&lt;li&gt;Upgrade add-ons from the console.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deploy/Update Workloads:&lt;/strong&gt; Utilize Kubernetes tools such as kubectl or Helm charts to deploy workloads onto the new blue cluster or update in the existing blue cluster. Monitor performance and functionality to ensure a smooth transition.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Validate and Test:&lt;/strong&gt; Conduct thorough testing on this new/upgraded blue cluster to verify that all workloads and applications function as expected. Use automated testing tools and perform manual checks to validate performance, scalability, and reliability.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cut Over Traffic:&lt;/strong&gt; Once validation is complete, gradually shift production traffic from the old cluster to the new upgraded one using weighted routing. Monitor logs, pod scaling, and AWS Load Balancer metrics such as RequestCount as traffic increases. Monitor closely for any anomalies or performance issues during the transition.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Upgrade Original Cluster:&lt;/strong&gt; Now perform the same process for the original cluster, including upgrading the EKS control plane, node groups, add-ons, as well as Kubernetes workloads. Verify if everything is working fine and transition traffic gradually back from the blue cluster to this original one after the full upgrade.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Decommission/Scale Down Blue Cluster:&lt;/strong&gt; After confirming that the original cluster is handling all traffic effectively, decommission/scale down all node groups of the blue cluster to release resources and reduce operational overhead.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Best Practices for EKS Version Upgrades:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Automate wherever possible to streamline the upgrade process and reduce manual errors.&lt;/li&gt;
&lt;li&gt;Implement monitoring and alerting to detect and respond to any issues promptly.&lt;/li&gt;
&lt;li&gt;Maintain clear communication with stakeholders throughout the upgrade process to manage expectations and address concerns proactively.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;AWS EKS provides a robust platform for managing Kubernetes clusters, and staying current with the latest versions is crucial for leveraging new features and ensuring security and performance. By adopting the blue-green methodology for version upgrades, organizations can minimize downtime, reduce risks, and maintain a seamless experience for end-users. By following best practices and leveraging automation and monitoring capabilities, you can confidently navigate EKS version upgrades and keep your Kubernetes infrastructure up to date with minimal disruption.&lt;/p&gt;

&lt;p&gt;References: &lt;a href="https://kubernetes.io/docs/reference/using-api/deprecation-guide/#removed-apis-by-release" rel="noopener noreferrer"&gt;Kubernetes Deprecation Guide.&lt;/a&gt;&lt;/p&gt;

</description>
      <category>devops</category>
      <category>aws</category>
      <category>eks</category>
    </item>
    <item>
      <title>Distributing restricted static content through CloudFront using signed cookies.</title>
      <dc:creator>Muhammad Ahmad Khan</dc:creator>
      <pubDate>Mon, 21 Nov 2022 14:47:55 +0000</pubDate>
      <link>https://forem.com/muhammad_ahmad_khan/distributing-restricted-static-content-through-cloudfront-using-signed-cookies-20hp</link>
      <guid>https://forem.com/muhammad_ahmad_khan/distributing-restricted-static-content-through-cloudfront-using-signed-cookies-20hp</guid>
      <description>&lt;p&gt;Many times it's a business requirement that the content you share over the internet through your application including documents, business data, or media streams is intended to be consumed by selected users, for example, users who have paid a fee. Suppose that you have a similar situation and you are using an AWS CloudFront distribution (CDN) to distribute the static content including documents, images, and videos. But you want a restriction that this content is accessible through your app only to a set of selected users. Then we can use &lt;strong&gt;signed URLs&lt;/strong&gt; and &lt;strong&gt;signed cookies&lt;/strong&gt;.&lt;br&gt;
CloudFront signed URLs and signed cookies provide the same basic functionality. They allow you to control who can access your content but both are beneficial in entirely different situations based on different requirements. &lt;br&gt;
E.g.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Using signed URLs is beneficial when you want to restrict access to individual files, for example, an installation download for your application.&lt;/li&gt;
&lt;li&gt;Using signed cookies is beneficial when you want to provide access to multiple restricted files, and don't want to change your current URLs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this blog, we will discuss about using signed cookies to grant access to your private content to the relevant user using CloudFront.&lt;br&gt;
Signed URLs take precedence over signed cookies if you use both signed URLs and signed cookies to control access to your content.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How signed cookies works?&lt;/strong&gt;&lt;br&gt;
Here is an overview of how signed cookies work.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;In your CloudFront distribution, specify one or more trusted key groups, which contain the public keys that CloudFront can use to verify the URL signature. You use the corresponding private keys to sign the URLs.
Since you use private keys to sign the URLs you must store them somewhere and we will store them in &lt;strong&gt;AWS Secrets Manager&lt;/strong&gt;.
To create a KeyPair and to add a signer (trusted key group) in CloudFront distribution see the procedure &lt;a href="https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-trusted-signers.html" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;You develop your application to determine whether a user should have access to your content and if so, to send &lt;strong&gt;three Set-Cookie headers&lt;/strong&gt; to the viewer. (Each Set-Cookie header can contain only one name-value pair, and a CloudFront signed cookie requires three name-value pairs.) You must send the Set-Cookie headers to the viewer before the viewer requests your private content. 
Typically, your CloudFront distribution will have at least two cache behaviors, one that doesn't require authentication and one that does. The error page for the secure portion of the site includes a redirector or a link to a login page. If you configure your distribution to cache files based on cookies, CloudFront doesn't cache separate files based on the attributes in signed cookies.&lt;/li&gt;
&lt;li&gt;A user signs in to your website and either pays for content or meets some other requirement for access.&lt;/li&gt;
&lt;li&gt;Your application returns the Set-Cookie headers in the response, and the viewer stores the name-value pairs.&lt;/li&gt;
&lt;li&gt;The user requests a file. The user's browser or other viewer gets the name-value pairs from step 4 and adds them to the request in a Cookie header. This is the signed cookie.&lt;/li&gt;
&lt;li&gt;CloudFront uses the public key to validate the signature in the signed cookie and to confirm that the cookie hasn't been tampered with. If the signature is invalid, the request is rejected. If the signature in the cookie is valid, CloudFront looks at the policy statement in the cookie (or constructs one if you're using a canned policy) to confirm that the request is still valid. If the request meets the requirements in the policy statement, CloudFront serves your content as it does for content that isn't restricted: it determines whether the file is already in the edge cache, forwards the request to the origin if necessary, and returns the file to the user.&lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;There are two kinds of policies used for signed cookies&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Canned policies &lt;/li&gt;
&lt;li&gt;Custom policies&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When you create a signed cookie, you write a policy statement in JSON format that specifies the restrictions on the signed cookie, for example, how long the cookie is valid. You can use canned policies or custom policies. &lt;br&gt;
Canned policies allow you to specify an &lt;strong&gt;expiration date&lt;/strong&gt; only. Custom policies allow more complex restrictions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Canned policy example&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Statement"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Resource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"base URL or stream name"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Condition"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"DateLessThan"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"AWS:EpochTime"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;ending&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;date&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;time&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Unix&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;time&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;format&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;UTC&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Custom policy example&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Statement"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Resource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"URL of the file"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Condition"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"DateLessThan"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"AWS:EpochTime"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="err"&gt;required&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;ending&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;date&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;time&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Unix&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;time&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;format&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;UTC&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"DateGreaterThan"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"AWS:EpochTime"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="err"&gt;optional&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;beginning&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;date&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;time&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Unix&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;time&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;format&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;UTC&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"IpAddress"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"AWS:SourceIp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"optional IP address"&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since we want to give access to all of the files located on a certain path of our CloudFront URL using wildcard &lt;code&gt;*&lt;/code&gt; therefore we will use a custom policy.&lt;br&gt;
For example, we want to give access to all of the files located on this URL: &lt;code&gt;https://content.mysite.com/private-content/*&lt;/code&gt;&lt;br&gt;
Here is the simple code in PHP for creating signed cookies using custom policy.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="k"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'vendor/autoload.php'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Aws\Credentials\Credentials&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Aws\SecretsManager\SecretsManagerClient&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Aws\CloudFront\CloudFrontClient&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Aws\Exception\AwsException&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Check if user is login or check if user is paid the fee&lt;/span&gt;


&lt;span class="c1"&gt;// Initialize AWS credentials from IAM role&lt;/span&gt;
&lt;span class="nv"&gt;$credentials_uri&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'AWS_CONTAINER_CREDENTIALS_RELATIVE_URI'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$credentials_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'http://169.254.170.2'&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$credentials_uri&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$get_credentials&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;file_get_contents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$credentials_url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$credentials_array&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;json_decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$get_credentials&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$credentials&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Credentials&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$credentials_array&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"AccessKeyId"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nv"&gt;$credentials_array&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"SecretAccessKey"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nv"&gt;$credentials_array&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Token"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nb"&gt;strtotime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$credentials_array&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Expiration"&lt;/span&gt;&lt;span class="p"&gt;]));&lt;/span&gt;

&lt;span class="c1"&gt;// Create a Secrets Manager Client&lt;/span&gt;
&lt;span class="nv"&gt;$secretManagerClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;SecretsManagerClient&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="s1"&gt;'credentials'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$credentials&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'version'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'2017-10-17'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'region'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'us-east-1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;]);&lt;/span&gt;

&lt;span class="nv"&gt;$secretName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'private-content-access-key'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$secretResult&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$secretManagerClient&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getSecretValue&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
        &lt;span class="s1"&gt;'SecretId'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$secretName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;]);&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;AwsException&lt;/span&gt; &lt;span class="nv"&gt;$e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// output error message if fails&lt;/span&gt;
    &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$e&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getAwsErrorMessage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;signCookie&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$cloudFrontClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$policy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$privateKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$keyPairId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$cloudFrontClient&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getSignedCookie&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
            &lt;span class="s1"&gt;'policy'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$policy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'private_key'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$privateKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'key_pair_id'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$keyPairId&lt;/span&gt;
        &lt;span class="p"&gt;]);&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;AwsException&lt;/span&gt; &lt;span class="nv"&gt;$e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'Error'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$e&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getAwsErrorMessage&lt;/span&gt;&lt;span class="p"&gt;()];&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;signACookie&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$privateKey&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$resourcePath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'https://content.mysite.com/private-content/*'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;$expires&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;3600&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// 60 minutes (60 * 60 seconds) from now.&lt;/span&gt;
    &lt;span class="nv"&gt;$keyPairId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'ABCDWXYZ'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;$policy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="s1"&gt;'{'&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;
        &lt;span class="s1"&gt;'"Statement":['&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;
            &lt;span class="s1"&gt;'{'&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;
                &lt;span class="s1"&gt;'"Resource":"'&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$resourcePath&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'",'&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;
                &lt;span class="s1"&gt;'"Condition":{'&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;
                    &lt;span class="s1"&gt;'"DateLessThan":{"AWS:EpochTime":'&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$expires&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'}'&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;
                &lt;span class="s1"&gt;'}'&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;
            &lt;span class="s1"&gt;'}'&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;
        &lt;span class="s1"&gt;']'&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt;
    &lt;span class="s1"&gt;'}'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nv"&gt;$cloudFrontClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CloudFrontClient&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
        &lt;span class="s1"&gt;'credentials'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$credentials&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'version'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'2014-11-06'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'region'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'us-east-1'&lt;/span&gt;
    &lt;span class="p"&gt;]);&lt;/span&gt;

    &lt;span class="nv"&gt;$result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;signCookie&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nv"&gt;$cloudFrontClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nv"&gt;$policy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nv"&gt;$privateKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nv"&gt;$keyPairId&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nv"&gt;$cookies&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;array&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$result&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$key&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nb"&gt;array_push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nv"&gt;$cookies&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="k"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="s1"&gt;'name'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'value'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'expires'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$expires&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'path'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'/'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'domain'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;".mysite.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'secure'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'httpOnly'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$cookies&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$cookie&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nb"&gt;setcookie&lt;/span&gt;
            &lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nv"&gt;$cookie&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'name'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="nv"&gt;$cookie&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'value'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="nv"&gt;$cookie&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'expires'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="nv"&gt;$cookie&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'path'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="nv"&gt;$cookie&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'domain'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="nv"&gt;$cookie&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'secure'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="nv"&gt;$cookie&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'httpOnly'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;isset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$secretResult&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'SecretString'&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$privateKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$secretResult&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'SecretString'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="nf"&gt;signACookie&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$privateKey&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Give access to the content e.g https://content.mysite.com/private-content/doc.pdf&lt;/span&gt;



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

&lt;/div&gt;



&lt;p&gt;You can easily write similar code in Node.js (Javascript).&lt;br&gt;
The most important function to consider from the CloudFront client is &lt;strong&gt;getSignedCookie()&lt;/strong&gt; which takes three parameters: policy, private key, and key pair id (this is not key group Id).&lt;br&gt;
This function returns a response with the following values.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CloudFront-Policy=base64 encoded version of the policy statement;&lt;/li&gt;
&lt;li&gt;CloudFront-Signature=hashed and signed version of the policy statement; &lt;/li&gt;
&lt;li&gt;CloudFront-Key-Pair-Id=public key ID for the CloudFront public key whose corresponding private key you're using to generate the signature;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Finally, we will set the cookies by using values returned in the response.&lt;/p&gt;

</description>
      <category>learning</category>
    </item>
    <item>
      <title>SQS depth based ECS task auto-scaling using step scaling.</title>
      <dc:creator>Muhammad Ahmad Khan</dc:creator>
      <pubDate>Sun, 20 Nov 2022 21:20:10 +0000</pubDate>
      <link>https://forem.com/muhammad_ahmad_khan/sqs-depth-based-ecs-task-auto-scaling-using-step-scaling-3l8m</link>
      <guid>https://forem.com/muhammad_ahmad_khan/sqs-depth-based-ecs-task-auto-scaling-using-step-scaling-3l8m</guid>
      <description>&lt;p&gt;As mentioned in a previous blog post &lt;a href="https://dev.to/muhammad_ahmad_khan/ecs-auto-scaling-based-on-amazon-mq-for-rabbitmqs-queue-depth-4m5b"&gt;here&lt;/a&gt;, we can easily apply scaling using step scaling on SQS queue depth.&lt;/p&gt;

&lt;p&gt;Step scaling policies increase or decrease the current capacity of a scalable target based on a set of scaling adjustments, known as &lt;strong&gt;step adjustments&lt;/strong&gt;. The adjustments vary based on the size of the alarm breach. All alarms that are breached are evaluated by Application Auto Scaling as it receives the alarm messages.&lt;/p&gt;

&lt;p&gt;With step scaling, you choose scaling metrics and threshold values for the CloudWatch alarms that trigger the scaling process and thus it requires you to create CloudWatch alarms.&lt;/p&gt;

&lt;p&gt;When you create a step scaling policy, you add one or more step adjustments that enable you to scale based on the size of the alarm breach. Each step adjustment specifies the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A lower bound for the metric value&lt;/li&gt;
&lt;li&gt;An upper bound for the metric value&lt;/li&gt;
&lt;li&gt;The amount by which to scale, based on the scaling adjustment type&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Application Auto Scaling supports the following adjustment types for step scaling policies:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ChangeInCapacity&lt;/strong&gt;—Increase or decrease the current capacity of the scalable target by the specified value. A positive value increases the capacity and a negative value decreases the capacity. For example: If the current capacity is 3 and the adjustment is 5, then Application Auto Scaling adds 5 to the capacity for a total of 8.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ExactCapacity&lt;/strong&gt;—Change the current capacity of the scalable target to the specified value. Specify a positive value with this adjustment type. For example: If the current capacity is 3 and the adjustment is 5, then Application Auto Scaling changes the capacity to 5.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;PercentChangeInCapacity&lt;/strong&gt;—Increase or decrease the current capacity of the scalable target by the specified percentage. A positive value increases the capacity and a negative value decreases the capacity. For example: If the current capacity is 10 and the adjustment is 10 percent, then Application Auto Scaling adds 1 to the capacity for a total of 11.&lt;/p&gt;

&lt;p&gt;Below is the CloudFormation template with the implementation of step scaling with SQS.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;AWSTemplateFormatVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;2010-09-09&lt;/span&gt;
&lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Creates a fargate based auto-scaling environment that processes work from an SQS queue&lt;/span&gt;
&lt;span class="na"&gt;Parameters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;DockerImageUrl&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;String&lt;/span&gt;
    &lt;span class="na"&gt;Default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;latest&lt;/span&gt;

  &lt;span class="na"&gt;DockerContainerName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;String&lt;/span&gt;
    &lt;span class="na"&gt;Default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;consumer-service&lt;/span&gt;

  &lt;span class="na"&gt;EnvironmentName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;String&lt;/span&gt;
    &lt;span class="na"&gt;Default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dev&lt;/span&gt;

  &lt;span class="na"&gt;Memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;String&lt;/span&gt;
    &lt;span class="na"&gt;Default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;8GB&lt;/span&gt;

  &lt;span class="na"&gt;Cpu&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Number&lt;/span&gt;
    &lt;span class="na"&gt;Default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2048&lt;/span&gt; &lt;span class="c1"&gt;# 2 vCPU&lt;/span&gt;

  &lt;span class="na"&gt;ContainerPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Number&lt;/span&gt;
    &lt;span class="na"&gt;Default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3000&lt;/span&gt;

  &lt;span class="na"&gt;HealthCheckPath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;String&lt;/span&gt;
    &lt;span class="na"&gt;Default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http://localhost:3000/check&lt;/span&gt;

  &lt;span class="na"&gt;FaragateScalingEnvSSM&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::SSM::Parameter::Value&amp;lt;String&amp;gt;&lt;/span&gt;
    &lt;span class="na"&gt;Default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/config/ecs/FARGATE_SCALING_ENV"&lt;/span&gt;

  &lt;span class="na"&gt;QueueDepthScaleOutAlarmThresholdSSM&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::SSM::Parameter::Value&amp;lt;String&amp;gt;&lt;/span&gt;
    &lt;span class="na"&gt;Default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/config/ecs/consumer-service/QUEUE_DEPTH_SCALE_OUT_ALARM_THRESHOLD"&lt;/span&gt;

  &lt;span class="na"&gt;CpuUtilizationScaleInAlarmThresholdSSM&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::SSM::Parameter::Value&amp;lt;String&amp;gt;&lt;/span&gt;
    &lt;span class="na"&gt;Default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/config/ecs/consumer-service/CPU_UTILIZATION_SCALE_IN_ALARM_THRESHOLD"&lt;/span&gt;

  &lt;span class="na"&gt;CpuUtilizationNoComputeOrScaleInAlarmEvaluationPeriodsSSM&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::SSM::Parameter::Value&amp;lt;String&amp;gt;&lt;/span&gt;
    &lt;span class="na"&gt;Default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/config/ecs/consumer-service/CPU_UTILIZATION_NO_COMPUTE_OR_SCALE_IN_ALARM_EVALUATION_PERIODS"&lt;/span&gt;

  &lt;span class="na"&gt;ComputeAutoScalingTargetMaxCapacitySSM&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::SSM::Parameter::Value&amp;lt;String&amp;gt;&lt;/span&gt;
    &lt;span class="na"&gt;Default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/config/ecs/consumer-service/AUTO_SCALING_TARGET_MAX_CAPACITY"&lt;/span&gt;

&lt;span class="na"&gt;Conditions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;CreateNonProdResources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Equals&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="nv"&gt;FaragateScalingEnvSSM&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;non-prod'&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;CreateProdResources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Equals&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="nv"&gt;FaragateScalingEnvSSM&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;prod'&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;Resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;SQSQueue&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AWS::SQS::Queue'&lt;/span&gt;
    &lt;span class="c1"&gt;# Properties:&lt;/span&gt;
    &lt;span class="c1"&gt;#   ReceiveMessageWaitTimeSeconds: 20&lt;/span&gt;
    &lt;span class="c1"&gt;#   VisibilityTimeout: 1200 # 20 minutes&lt;/span&gt;
    &lt;span class="c1"&gt;#   MessageRetentionPeriod: 1209600 # 14 Days&lt;/span&gt;

  &lt;span class="na"&gt;QueueUrlParameter&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AWS::SSM::Parameter'&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;Name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Join&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;EnvironmentName&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/services/&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;DockerContainerName&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/SQS_QUEUE_URL&lt;/span&gt;
      &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;String&lt;/span&gt;
      &lt;span class="na"&gt;Value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;SQSQueue&lt;/span&gt;

  &lt;span class="na"&gt;ComputeTaskLogGroup&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AWS::Logs::LogGroup'&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;LogGroupName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Join&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/x-org&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;ecs&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="kt"&gt;!Sub&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;${AWS::StackName}'&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;logs&lt;/span&gt;

  &lt;span class="na"&gt;ComputeTaskRole&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AWS::IAM::Role'&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;AssumeRolePolicyDocument&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;Version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;2012-10-17&lt;/span&gt;
        &lt;span class="na"&gt;Statement&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Effect&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Allow&lt;/span&gt;
            &lt;span class="na"&gt;Principal&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;Service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;ecs-tasks.amazonaws.com&lt;/span&gt;
            &lt;span class="na"&gt;Action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;sts:AssumeRole'&lt;/span&gt;
      &lt;span class="na"&gt;Policies&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;PolicyName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Required_Access&lt;/span&gt;
          &lt;span class="na"&gt;PolicyDocument&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;Version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;2012-10-17&lt;/span&gt;
            &lt;span class="na"&gt;Statement&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Effect&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Allow&lt;/span&gt;
                &lt;span class="na"&gt;Action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;sqs:*'&lt;/span&gt;
                  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;secretsmanager:*'&lt;/span&gt;
                  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ssm:*'&lt;/span&gt;
                  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;logs:*'&lt;/span&gt;
                  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;dynamodb:*'&lt;/span&gt;
                  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s3:*'&lt;/span&gt;
                  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ecs:*'&lt;/span&gt;
                &lt;span class="na"&gt;Resource&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;*'&lt;/span&gt;
      &lt;span class="na"&gt;ManagedPolicyArns&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy'&lt;/span&gt;

  &lt;span class="na"&gt;ComputeTaskDefinition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AWS::ECS::TaskDefinition'&lt;/span&gt;
    &lt;span class="na"&gt;DependsOn&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ComputeTaskLogGroup&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;TaskRoleArn&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!GetAtt&lt;/span&gt; &lt;span class="s"&gt;ComputeTaskRole.Arn&lt;/span&gt;
      &lt;span class="na"&gt;ExecutionRoleArn&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!GetAtt&lt;/span&gt; &lt;span class="s"&gt;ComputeTaskRole.Arn&lt;/span&gt;
      &lt;span class="na"&gt;RequiresCompatibilities&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;FARGATE&lt;/span&gt;
      &lt;span class="na"&gt;NetworkMode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;awsvpc&lt;/span&gt;
      &lt;span class="na"&gt;Cpu&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;Cpu&lt;/span&gt;
      &lt;span class="na"&gt;Memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;Memory&lt;/span&gt;
      &lt;span class="na"&gt;ContainerDefinitions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Sub&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;${AWS::StackName}'&lt;/span&gt;
          &lt;span class="na"&gt;Image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;DockerImageUrl&lt;/span&gt;
          &lt;span class="na"&gt;LogConfiguration&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;LogDriver&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;awslogs&lt;/span&gt;
            &lt;span class="na"&gt;Options&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;awslogs-region&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;us-east-1&lt;/span&gt;
              &lt;span class="na"&gt;awslogs-group&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;ComputeTaskLogGroup&lt;/span&gt;
              &lt;span class="na"&gt;awslogs-stream-prefix&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ecs&lt;/span&gt;
          &lt;span class="na"&gt;HealthCheck&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;Command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;CMD-SHELL&lt;/span&gt;
              &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="kt"&gt;!Sub&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;curl&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-f&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;${HealthCheckPath}&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;||&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;exit&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;1'&lt;/span&gt;
            &lt;span class="na"&gt;Interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;30&lt;/span&gt;
            &lt;span class="na"&gt;Retries&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;
            &lt;span class="na"&gt;StartPeriod&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;300&lt;/span&gt;
          &lt;span class="na"&gt;PortMappings&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;ContainerPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;ContainerPort&lt;/span&gt;
              &lt;span class="na"&gt;Protocol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;tcp&lt;/span&gt;
          &lt;span class="na"&gt;Environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;EnvironmentName&lt;/span&gt;
              &lt;span class="na"&gt;Value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;EnvironmentName&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;SQS_QUEUE_URL&lt;/span&gt;
              &lt;span class="na"&gt;Value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;SQSQueue&lt;/span&gt;

  &lt;span class="na"&gt;ComputeCluster&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AWS::ECS::Cluster'&lt;/span&gt;
    &lt;span class="c1"&gt;# Properties:&lt;/span&gt;
    &lt;span class="c1"&gt;#   ClusterName: !Join ['-', [!Ref DockerContainerName, cluster]]&lt;/span&gt;

  &lt;span class="na"&gt;NonProdComputeService&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AWS::ECS::Service'&lt;/span&gt;
    &lt;span class="na"&gt;Condition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;CreateNonProdResources&lt;/span&gt; &lt;span class="c1"&gt;# only create if it is NonProd env&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;Cluster&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;ComputeCluster&lt;/span&gt;
      &lt;span class="na"&gt;TaskDefinition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;ComputeTaskDefinition&lt;/span&gt;
      &lt;span class="na"&gt;DeploymentConfiguration&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;MinimumHealthyPercent&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100&lt;/span&gt;
        &lt;span class="na"&gt;MaximumPercent&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;200&lt;/span&gt;
      &lt;span class="c1"&gt;# Desired count should be 0; Otherwise the Task Scheduler will restart number of desired containers once they are stopped&lt;/span&gt;
      &lt;span class="na"&gt;DesiredCount&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;
      &lt;span class="c1"&gt;# This may need to be adjusted if the container takes a while to start up&lt;/span&gt;
      &lt;span class="c1"&gt;# HealthCheckGracePeriodSeconds: 30&lt;/span&gt;
      &lt;span class="na"&gt;LaunchType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;FARGATE&lt;/span&gt;
      &lt;span class="na"&gt;NetworkConfiguration&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;AwsvpcConfiguration&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="c1"&gt;# Change it to DISABLED if you're using private subnets that have access to a NAT gateway&lt;/span&gt;
          &lt;span class="na"&gt;AssignPublicIp&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ENABLED&lt;/span&gt;
          &lt;span class="na"&gt;Subnets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="kt"&gt;!ImportValue&lt;/span&gt; &lt;span class="s"&gt;ComputeSubnetA&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="kt"&gt;!ImportValue&lt;/span&gt; &lt;span class="s"&gt;ComputeSubnetB&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="kt"&gt;!ImportValue&lt;/span&gt; &lt;span class="s"&gt;ComputeSubnetC&lt;/span&gt;
          &lt;span class="na"&gt;SecurityGroups&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="kt"&gt;!ImportValue&lt;/span&gt; &lt;span class="s"&gt;ComputeSecurityGroup&lt;/span&gt;

  &lt;span class="na"&gt;ProdComputeService&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AWS::ECS::Service'&lt;/span&gt;
    &lt;span class="na"&gt;Condition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;CreateProdResources&lt;/span&gt; &lt;span class="c1"&gt;# only create if it is Prod env&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;Cluster&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;ComputeCluster&lt;/span&gt;
      &lt;span class="na"&gt;TaskDefinition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;ComputeTaskDefinition&lt;/span&gt;
      &lt;span class="na"&gt;DeploymentConfiguration&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;MinimumHealthyPercent&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100&lt;/span&gt;
        &lt;span class="na"&gt;MaximumPercent&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;200&lt;/span&gt;
      &lt;span class="na"&gt;DesiredCount&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
      &lt;span class="c1"&gt;# This may need to be adjusted if the container takes a while to start up&lt;/span&gt;
      &lt;span class="c1"&gt;# HealthCheckGracePeriodSeconds: 30&lt;/span&gt;
      &lt;span class="na"&gt;LaunchType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;FARGATE&lt;/span&gt;
      &lt;span class="na"&gt;NetworkConfiguration&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;AwsvpcConfiguration&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="c1"&gt;# Change it to DISABLED if you're using private subnets that have access to a NAT gateway&lt;/span&gt;
          &lt;span class="na"&gt;AssignPublicIp&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ENABLED&lt;/span&gt;
          &lt;span class="na"&gt;Subnets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="kt"&gt;!ImportValue&lt;/span&gt; &lt;span class="s"&gt;ComputeSubnetA&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="kt"&gt;!ImportValue&lt;/span&gt; &lt;span class="s"&gt;ComputeSubnetB&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="kt"&gt;!ImportValue&lt;/span&gt; &lt;span class="s"&gt;ComputeSubnetC&lt;/span&gt;
          &lt;span class="na"&gt;SecurityGroups&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="kt"&gt;!ImportValue&lt;/span&gt; &lt;span class="s"&gt;ComputeSecurityGroup&lt;/span&gt;

  &lt;span class="na"&gt;ComputeAutoScalingRole&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AWS::IAM::Role'&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;AssumeRolePolicyDocument&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;Version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;2012-10-17&lt;/span&gt;
        &lt;span class="na"&gt;Statement&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Effect&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Allow&lt;/span&gt;
            &lt;span class="na"&gt;Principal&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;Service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;ecs-tasks.amazonaws.com&lt;/span&gt;
                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;application-autoscaling.amazonaws.com&lt;/span&gt;
            &lt;span class="na"&gt;Action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;sts:AssumeRole'&lt;/span&gt;
      &lt;span class="na"&gt;Path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/"&lt;/span&gt;
      &lt;span class="na"&gt;Policies&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;PolicyName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Sub&lt;/span&gt; &lt;span class="s"&gt;${DockerContainerName}-ECSAutoScalingRole&lt;/span&gt;
        &lt;span class="na"&gt;PolicyDocument&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;Statement&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Effect&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Allow&lt;/span&gt;
            &lt;span class="na"&gt;Action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;ecs:UpdateService&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;ecs:DescribeServices&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;application-autoscaling:*&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;cloudwatch:DescribeAlarms&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;cloudwatch:GetMetricStatistics&lt;/span&gt;
            &lt;span class="na"&gt;Resource&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;*"&lt;/span&gt;
      &lt;span class="na"&gt;ManagedPolicyArns&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceAutoscaleRole'&lt;/span&gt;

  &lt;span class="na"&gt;NonProdComputeAutoScalingTarget&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AWS::ApplicationAutoScaling::ScalableTarget'&lt;/span&gt;
    &lt;span class="na"&gt;Condition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;CreateNonProdResources&lt;/span&gt; &lt;span class="c1"&gt;# only create if it is NonProd env&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;MinCapacity&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="c1"&gt;# As desired task can be 0&lt;/span&gt;
      &lt;span class="na"&gt;MaxCapacity&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;ComputeAutoScalingTargetMaxCapacitySSM&lt;/span&gt;
      &lt;span class="na"&gt;ResourceId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Join&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/'&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;service&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;ComputeCluster&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="kt"&gt;!GetAtt&lt;/span&gt; &lt;span class="s"&gt;NonProdComputeService.Name&lt;/span&gt;
      &lt;span class="na"&gt;ScalableDimension&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ecs:service:DesiredCount&lt;/span&gt;
      &lt;span class="na"&gt;ServiceNamespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ecs&lt;/span&gt;
      &lt;span class="na"&gt;RoleARN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!GetAtt&lt;/span&gt; &lt;span class="s"&gt;ComputeAutoScalingRole.Arn&lt;/span&gt;

  &lt;span class="na"&gt;ProdComputeAutoScalingTarget&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AWS::ApplicationAutoScaling::ScalableTarget'&lt;/span&gt;
    &lt;span class="na"&gt;Condition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;CreateProdResources&lt;/span&gt; &lt;span class="c1"&gt;# only create if it is Prod env&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;MinCapacity&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="c1"&gt;# As desired task can be 1 but not 0&lt;/span&gt;
      &lt;span class="na"&gt;MaxCapacity&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;ComputeAutoScalingTargetMaxCapacitySSM&lt;/span&gt; 
      &lt;span class="na"&gt;ResourceId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Join&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/'&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;service&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;ComputeCluster&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="kt"&gt;!GetAtt&lt;/span&gt; &lt;span class="s"&gt;ProdComputeService.Name&lt;/span&gt;
      &lt;span class="na"&gt;ScalableDimension&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ecs:service:DesiredCount&lt;/span&gt;
      &lt;span class="na"&gt;ServiceNamespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ecs&lt;/span&gt;
      &lt;span class="na"&gt;RoleARN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!GetAtt&lt;/span&gt; &lt;span class="s"&gt;ComputeAutoScalingRole.Arn&lt;/span&gt;

  &lt;span class="c1"&gt;# https://docs.aws.amazon.com/autoscaling/application/userguide/application-auto-scaling-step-scaling-policies.html&lt;/span&gt;
  &lt;span class="na"&gt;NonProdNoComputeAutoScalingPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AWS::ApplicationAutoScaling::ScalingPolicy'&lt;/span&gt;
    &lt;span class="na"&gt;Condition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;CreateNonProdResources&lt;/span&gt; &lt;span class="c1"&gt;# only create if it is NonProd env&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;PolicyName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Sub&lt;/span&gt; &lt;span class="s"&gt;${DockerContainerName}-NonProdNoComputeAutoScalingPolicy&lt;/span&gt;
      &lt;span class="na"&gt;PolicyType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;StepScaling&lt;/span&gt;
      &lt;span class="na"&gt;ScalingTargetId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;NonProdComputeAutoScalingTarget&lt;/span&gt;
      &lt;span class="na"&gt;ScalableDimension&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ecs:service:DesiredCount&lt;/span&gt;
      &lt;span class="na"&gt;ServiceNamespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ecs&lt;/span&gt;
      &lt;span class="na"&gt;StepScalingPolicyConfiguration&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;AdjustmentType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ExactCapacity&lt;/span&gt; &lt;span class="c1"&gt;# Can use PercentChangeInCapacity but then need to come up with configuration including some estimated change in percent&lt;/span&gt;
        &lt;span class="na"&gt;Cooldown&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;60&lt;/span&gt;
        &lt;span class="na"&gt;MetricAggregationType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Average&lt;/span&gt; &lt;span class="c1"&gt;# Valid values are Minimum, Maximum, and Average. If the aggregation type is null, the value is treated as Average. &lt;/span&gt;
        &lt;span class="na"&gt;StepAdjustments&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;MetricIntervalLowerBound&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;AWS::NoValue&lt;/span&gt;
          &lt;span class="na"&gt;MetricIntervalUpperBound&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;
          &lt;span class="na"&gt;ScalingAdjustment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;

  &lt;span class="c1"&gt;# https://docs.aws.amazon.com/autoscaling/application/userguide/application-auto-scaling-step-scaling-policies.html&lt;/span&gt;
  &lt;span class="na"&gt;NonProdInitialComputeAutoScalingPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AWS::ApplicationAutoScaling::ScalingPolicy'&lt;/span&gt;
    &lt;span class="na"&gt;Condition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;CreateNonProdResources&lt;/span&gt; &lt;span class="c1"&gt;# only create if it is NonProd env&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;PolicyName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Sub&lt;/span&gt; &lt;span class="s"&gt;${DockerContainerName}-NonProdInitialComputeAutoScalingPolicy&lt;/span&gt;
      &lt;span class="na"&gt;PolicyType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;StepScaling&lt;/span&gt;
      &lt;span class="na"&gt;ScalingTargetId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;NonProdComputeAutoScalingTarget&lt;/span&gt;
      &lt;span class="na"&gt;ScalableDimension&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ecs:service:DesiredCount&lt;/span&gt;
      &lt;span class="na"&gt;ServiceNamespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ecs&lt;/span&gt;
      &lt;span class="na"&gt;StepScalingPolicyConfiguration&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;AdjustmentType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ChangeInCapacity&lt;/span&gt; &lt;span class="c1"&gt;# ChangeInCapacity —&amp;gt; Increase or decrease the current capacity of the scalable target by the specified value&lt;/span&gt;
        &lt;span class="na"&gt;Cooldown&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;60&lt;/span&gt; &lt;span class="c1"&gt;# 1 min delay &lt;/span&gt;
        &lt;span class="na"&gt;MetricAggregationType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Minimum&lt;/span&gt; &lt;span class="c1"&gt;# Valid values are Minimum, Maximum, and Average. If the aggregation type is null, the value is treated as Average.&lt;/span&gt;
        &lt;span class="na"&gt;StepAdjustments&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;MetricIntervalLowerBound&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;
          &lt;span class="na"&gt;MetricIntervalUpperBound&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;AWS::NoValue&lt;/span&gt;
          &lt;span class="na"&gt;ScalingAdjustment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="c1"&gt;# scaling up by 1 container when the alarm is greater than or equal to the Metric Threshold&lt;/span&gt;

  &lt;span class="c1"&gt;# https://docs.aws.amazon.com/autoscaling/application/userguide/application-auto-scaling-step-scaling-policies.html&lt;/span&gt;
  &lt;span class="na"&gt;NonProdComputeAutoScalingScaleOutPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AWS::ApplicationAutoScaling::ScalingPolicy'&lt;/span&gt;
    &lt;span class="na"&gt;Condition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;CreateNonProdResources&lt;/span&gt; &lt;span class="c1"&gt;# only create if it is NonProd env&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;PolicyName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Sub&lt;/span&gt; &lt;span class="s"&gt;${DockerContainerName}-NonProdComputeAutoScalingScaleOutPolicy&lt;/span&gt;
      &lt;span class="na"&gt;PolicyType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;StepScaling&lt;/span&gt;
      &lt;span class="na"&gt;ScalingTargetId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;NonProdComputeAutoScalingTarget&lt;/span&gt;
      &lt;span class="na"&gt;ScalableDimension&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ecs:service:DesiredCount&lt;/span&gt;
      &lt;span class="na"&gt;ServiceNamespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ecs&lt;/span&gt;
      &lt;span class="na"&gt;StepScalingPolicyConfiguration&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;AdjustmentType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ChangeInCapacity&lt;/span&gt; &lt;span class="c1"&gt;# ChangeInCapacity —&amp;gt; Increase or decrease the current capacity of the scalable target by the specified value&lt;/span&gt;
        &lt;span class="na"&gt;Cooldown&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;60&lt;/span&gt; &lt;span class="c1"&gt;# 1 min delay&lt;/span&gt;
        &lt;span class="na"&gt;MetricAggregationType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Minimum&lt;/span&gt; &lt;span class="c1"&gt;# Valid values are Minimum, Maximum, and Average. If the aggregation type is null, the value is treated as Average. &lt;/span&gt;
        &lt;span class="na"&gt;StepAdjustments&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;MetricIntervalLowerBound&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;  &lt;span class="c1"&gt;# 0 means exactly equal to Metric Threshold which is 10 defined using SSM parameter&lt;/span&gt;
          &lt;span class="na"&gt;MetricIntervalUpperBound&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;15&lt;/span&gt; &lt;span class="c1"&gt;# [Metrice Threshold + 15]&lt;/span&gt;
          &lt;span class="na"&gt;ScalingAdjustment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;MetricIntervalLowerBound&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;15&lt;/span&gt; &lt;span class="c1"&gt;# [Metrice Threshold + 15]&lt;/span&gt;
          &lt;span class="na"&gt;MetricIntervalUpperBound&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;25&lt;/span&gt; &lt;span class="c1"&gt;# [Metrice Threshold + 25]&lt;/span&gt;
          &lt;span class="na"&gt;ScalingAdjustment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;MetricIntervalLowerBound&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;25&lt;/span&gt; &lt;span class="c1"&gt;# [Metrice Threshold + 25]&lt;/span&gt;
          &lt;span class="na"&gt;MetricIntervalUpperBound&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;35&lt;/span&gt; &lt;span class="c1"&gt;# [Metrice Threshold + 35]&lt;/span&gt;
          &lt;span class="na"&gt;ScalingAdjustment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;MetricIntervalLowerBound&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;35&lt;/span&gt; &lt;span class="c1"&gt;# [Metrice Threshold + 35]&lt;/span&gt;
          &lt;span class="na"&gt;MetricIntervalUpperBound&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;45&lt;/span&gt; &lt;span class="c1"&gt;# [Metrice Threshold + 45]&lt;/span&gt;
          &lt;span class="na"&gt;ScalingAdjustment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;MetricIntervalLowerBound&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;45&lt;/span&gt; &lt;span class="c1"&gt;# [Metrice Threshold + 45]&lt;/span&gt;
          &lt;span class="na"&gt;ScalingAdjustment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;

  &lt;span class="c1"&gt;# https://docs.aws.amazon.com/autoscaling/application/userguide/application-auto-scaling-step-scaling-policies.html&lt;/span&gt;
  &lt;span class="na"&gt;ProdComputeAutoScalingScaleInPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AWS::ApplicationAutoScaling::ScalingPolicy'&lt;/span&gt;
    &lt;span class="na"&gt;Condition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;CreateProdResources&lt;/span&gt; &lt;span class="c1"&gt;# only create if it is Prod env&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;PolicyName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Sub&lt;/span&gt; &lt;span class="s"&gt;${DockerContainerName}-ProdComputeAutoScalingScaleInPolicy&lt;/span&gt;
      &lt;span class="na"&gt;PolicyType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;StepScaling&lt;/span&gt;
      &lt;span class="na"&gt;ScalingTargetId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;ProdComputeAutoScalingTarget&lt;/span&gt;
      &lt;span class="na"&gt;ScalableDimension&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ecs:service:DesiredCount&lt;/span&gt;
      &lt;span class="na"&gt;ServiceNamespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ecs&lt;/span&gt;
      &lt;span class="na"&gt;StepScalingPolicyConfiguration&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;AdjustmentType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ExactCapacity&lt;/span&gt; &lt;span class="c1"&gt;# Can use PercentChangeInCapacity but then need to come up with configuration including some estimated change in percent&lt;/span&gt;
        &lt;span class="na"&gt;Cooldown&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;60&lt;/span&gt;
        &lt;span class="na"&gt;MetricAggregationType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Average&lt;/span&gt; &lt;span class="c1"&gt;# Valid values are Minimum, Maximum, and Average. If the aggregation type is null, the value is treated as Average.&lt;/span&gt;
        &lt;span class="na"&gt;StepAdjustments&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;MetricIntervalLowerBound&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;AWS::NoValue&lt;/span&gt;
          &lt;span class="na"&gt;MetricIntervalUpperBound&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;
          &lt;span class="na"&gt;ScalingAdjustment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;

  &lt;span class="c1"&gt;# https://docs.aws.amazon.com/autoscaling/application/userguide/application-auto-scaling-step-scaling-policies.html&lt;/span&gt;
  &lt;span class="na"&gt;ProdComputeAutoScalingScaleOutPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AWS::ApplicationAutoScaling::ScalingPolicy'&lt;/span&gt;
    &lt;span class="na"&gt;Condition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;CreateProdResources&lt;/span&gt; &lt;span class="c1"&gt;# only create if it is Prod env&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;PolicyName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Sub&lt;/span&gt; &lt;span class="s"&gt;${DockerContainerName}-ProdComputeAutoScalingScaleOutPolicy&lt;/span&gt;
      &lt;span class="na"&gt;PolicyType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;StepScaling&lt;/span&gt;
      &lt;span class="na"&gt;ScalingTargetId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;ProdComputeAutoScalingTarget&lt;/span&gt;
      &lt;span class="na"&gt;ScalableDimension&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ecs:service:DesiredCount&lt;/span&gt;
      &lt;span class="na"&gt;ServiceNamespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ecs&lt;/span&gt;
      &lt;span class="na"&gt;StepScalingPolicyConfiguration&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;AdjustmentType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ChangeInCapacity&lt;/span&gt; &lt;span class="c1"&gt;# ChangeInCapacity —&amp;gt; Increase or decrease the current capacity of the scalable target by the specified value&lt;/span&gt;
        &lt;span class="na"&gt;Cooldown&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;60&lt;/span&gt; &lt;span class="c1"&gt;# 1 min delay&lt;/span&gt;
        &lt;span class="na"&gt;MetricAggregationType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Minimum&lt;/span&gt; &lt;span class="c1"&gt;# Valid values are Minimum, Maximum, and Average. If the aggregation type is null, the value is treated as Average.&lt;/span&gt;
        &lt;span class="na"&gt;StepAdjustments&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;MetricIntervalLowerBound&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;  &lt;span class="c1"&gt;# 0 means exactly equal to Metric Threshold which is 10 defined using SSM parameter&lt;/span&gt;
          &lt;span class="na"&gt;MetricIntervalUpperBound&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;15&lt;/span&gt; &lt;span class="c1"&gt;# [Metrice Threshold + 15]&lt;/span&gt;
          &lt;span class="na"&gt;ScalingAdjustment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;MetricIntervalLowerBound&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;15&lt;/span&gt; &lt;span class="c1"&gt;# [Metrice Threshold + 15]&lt;/span&gt;
          &lt;span class="na"&gt;MetricIntervalUpperBound&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;25&lt;/span&gt; &lt;span class="c1"&gt;# [Metrice Threshold + 25]&lt;/span&gt;
          &lt;span class="na"&gt;ScalingAdjustment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;MetricIntervalLowerBound&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;25&lt;/span&gt; &lt;span class="c1"&gt;# [Metrice Threshold + 25]&lt;/span&gt;
          &lt;span class="na"&gt;MetricIntervalUpperBound&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;35&lt;/span&gt; &lt;span class="c1"&gt;# [Metrice Threshold + 35]&lt;/span&gt;
          &lt;span class="na"&gt;ScalingAdjustment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;MetricIntervalLowerBound&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;35&lt;/span&gt; &lt;span class="c1"&gt;# [Metrice Threshold + 35]&lt;/span&gt;
          &lt;span class="na"&gt;MetricIntervalUpperBound&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;45&lt;/span&gt; &lt;span class="c1"&gt;# [Metrice Threshold + 45]&lt;/span&gt;
          &lt;span class="na"&gt;ScalingAdjustment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;MetricIntervalLowerBound&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;45&lt;/span&gt; &lt;span class="c1"&gt;# [Metrice Threshold + 45]&lt;/span&gt;
          &lt;span class="na"&gt;ScalingAdjustment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;

&lt;span class="c1"&gt;# ######### https://docs.aws.amazon.com/AmazonECS/latest/developerguide/cloudwatch-metrics.html #########&lt;/span&gt;
&lt;span class="c1"&gt;#                                       (Total CPU units used by tasks in service) x 100&lt;/span&gt;
&lt;span class="c1"&gt;# Service CPU utilization =  ----------------------------------------------------------------------------&lt;/span&gt;
&lt;span class="c1"&gt;#                            (Total CPU units specified in task definition) x (number of tasks in service)&lt;/span&gt;

  &lt;span class="na"&gt;NonProdCPUNoComputeAlarm&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::CloudWatch::Alarm&lt;/span&gt;
    &lt;span class="na"&gt;Condition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;CreateNonProdResources&lt;/span&gt; &lt;span class="c1"&gt;# only create alarm if it is NonProd env&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;AlarmName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Sub&lt;/span&gt; &lt;span class="s"&gt;${DockerContainerName}-NonProdCPUNoComputeAlarm&lt;/span&gt;
      &lt;span class="na"&gt;AlarmDescription&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Alarm if container utilize low CPU based on specified threshold!&lt;/span&gt;
      &lt;span class="na"&gt;Namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS/ECS&lt;/span&gt; &lt;span class="c1"&gt;# AWS::CloudWatch::Alarm.Period &amp;gt;= 60 for metrics in the AWS/ namespace&lt;/span&gt;
      &lt;span class="na"&gt;MetricName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;CPUUtilization&lt;/span&gt;
      &lt;span class="na"&gt;Dimensions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ServiceName&lt;/span&gt;
          &lt;span class="na"&gt;Value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;Fn::GetAtt&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;NonProdComputeService&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Name&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ClusterName&lt;/span&gt;
          &lt;span class="na"&gt;Value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;Ref&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ComputeCluster&lt;/span&gt;
      &lt;span class="na"&gt;Statistic&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Average&lt;/span&gt; &lt;span class="c1"&gt;# Not using Sum since the metric is CPUUtilization&lt;/span&gt;
      &lt;span class="na"&gt;Period&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;60&lt;/span&gt;  &lt;span class="c1"&gt;# 60 seconds ( Period must be 10, 30 or a multiple of 60 but 10 and 30 can not be used with namespaces with the following prefix: AWS/ )&lt;/span&gt;
      &lt;span class="na"&gt;EvaluationPeriods&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;CpuUtilizationNoComputeOrScaleInAlarmEvaluationPeriodsSSM&lt;/span&gt; &lt;span class="c1"&gt;# setting evaluation period 3 because when there is no task at all usually cpu starts with 0 (this way first evaluation period will already hit even before the task start doing anything) &amp;amp; 2 more as part of taking extra precautions! Change it to 2 if needed but not 1&lt;/span&gt;
      &lt;span class="na"&gt;Threshold&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;CpuUtilizationScaleInAlarmThresholdSSM&lt;/span&gt;
      &lt;span class="na"&gt;ComparisonOperator&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;LessThanOrEqualToThreshold&lt;/span&gt;
      &lt;span class="na"&gt;AlarmActions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Ref&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;NonProdNoComputeAutoScalingPolicy&lt;/span&gt;

  &lt;span class="na"&gt;NonProdInitialQueueDepthAlarm&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::CloudWatch::Alarm&lt;/span&gt;
    &lt;span class="na"&gt;Condition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;CreateNonProdResources&lt;/span&gt; &lt;span class="c1"&gt;# only create alarm if it is NonProd env&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;AlarmName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Sub&lt;/span&gt; &lt;span class="s"&gt;${DockerContainerName}-NonProdInitialQueueDepthAlarm&lt;/span&gt;
      &lt;span class="na"&gt;AlarmDescription&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Alarm if queue depth grows beyond specified threshold!&lt;/span&gt;
      &lt;span class="na"&gt;Namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS/SQS&lt;/span&gt; &lt;span class="c1"&gt;# AWS::CloudWatch::Alarm.Period &amp;gt;= 60 for metrics in the AWS/ namespace&lt;/span&gt;
      &lt;span class="na"&gt;MetricName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ApproximateNumberOfMessagesVisible&lt;/span&gt;
      &lt;span class="na"&gt;Dimensions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;QueueName&lt;/span&gt;
          &lt;span class="na"&gt;Value &lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!GetAtt&lt;/span&gt; &lt;span class="s"&gt;SQSQueue.QueueName&lt;/span&gt;
      &lt;span class="na"&gt;Statistic&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Sum&lt;/span&gt;
      &lt;span class="na"&gt;Period&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;60&lt;/span&gt; &lt;span class="c1"&gt;# 60 seconds ( Period must be 10, 30 or a multiple of 60 but 10 and 30 can not be used with namespaces with the following prefix: AWS/ )&lt;/span&gt;
      &lt;span class="na"&gt;EvaluationPeriods&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
      &lt;span class="na"&gt;Threshold&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="c1"&gt;# Threshold is 1 for initial depth&lt;/span&gt;
      &lt;span class="na"&gt;ComparisonOperator&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;GreaterThanOrEqualToThreshold&lt;/span&gt;
      &lt;span class="na"&gt;AlarmActions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Ref&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;NonProdInitialComputeAutoScalingPolicy&lt;/span&gt;

  &lt;span class="na"&gt;NonProdQueueDepthScaleOutAlarm&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::CloudWatch::Alarm&lt;/span&gt;
    &lt;span class="na"&gt;Condition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;CreateNonProdResources&lt;/span&gt; &lt;span class="c1"&gt;# only create alarm if it is NonProd env&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;AlarmName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Sub&lt;/span&gt; &lt;span class="s"&gt;${DockerContainerName}-NonProdQueueDepthScaleOutAlarm&lt;/span&gt;
      &lt;span class="na"&gt;AlarmDescription&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Alarm if queue depth grows beyond specified threshold!&lt;/span&gt;
      &lt;span class="na"&gt;Namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS/SQS&lt;/span&gt; &lt;span class="c1"&gt;# AWS::CloudWatch::Alarm.Period &amp;gt;= 60 for metrics in the AWS/ namespace&lt;/span&gt;
      &lt;span class="na"&gt;MetricName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ApproximateNumberOfMessagesVisible&lt;/span&gt;
      &lt;span class="na"&gt;Dimensions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;QueueName&lt;/span&gt;
          &lt;span class="na"&gt;Value &lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!GetAtt&lt;/span&gt; &lt;span class="s"&gt;SQSQueue.QueueName&lt;/span&gt;
      &lt;span class="na"&gt;Statistic&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Sum&lt;/span&gt;
      &lt;span class="na"&gt;Period&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;120&lt;/span&gt; &lt;span class="c1"&gt;# 120 seconds ( Period must be 10, 30 or a multiple of 60 but 10 and 30 can not be used with namespaces with the following prefix: AWS/ )&lt;/span&gt;
      &lt;span class="na"&gt;EvaluationPeriods&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
      &lt;span class="na"&gt;Threshold&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;QueueDepthScaleOutAlarmThresholdSSM&lt;/span&gt; &lt;span class="c1"&gt;# change this as needed&lt;/span&gt;
      &lt;span class="na"&gt;ComparisonOperator&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;GreaterThanOrEqualToThreshold&lt;/span&gt;
      &lt;span class="na"&gt;AlarmActions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Ref&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;NonProdComputeAutoScalingScaleOutPolicy&lt;/span&gt;

  &lt;span class="na"&gt;ProdCPUScaleInAlarm&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::CloudWatch::Alarm&lt;/span&gt;
    &lt;span class="na"&gt;Condition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;CreateProdResources&lt;/span&gt; &lt;span class="c1"&gt;# only create alarm if it is Prod env&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;AlarmName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Sub&lt;/span&gt; &lt;span class="s"&gt;${DockerContainerName}-ProdCPUScaleInAlarm&lt;/span&gt;
      &lt;span class="na"&gt;AlarmDescription&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Alarm if container utilize low cpu based on specified threshold!&lt;/span&gt;
      &lt;span class="na"&gt;Namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS/ECS&lt;/span&gt; &lt;span class="c1"&gt;# AWS::CloudWatch::Alarm.Period &amp;gt;= 60 for metrics in the AWS/ namespace&lt;/span&gt;
      &lt;span class="na"&gt;MetricName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;CPUUtilization&lt;/span&gt;
      &lt;span class="na"&gt;Dimensions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ServiceName&lt;/span&gt;
          &lt;span class="na"&gt;Value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;Fn::GetAtt&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;ProdComputeService&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Name&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ClusterName&lt;/span&gt;
          &lt;span class="na"&gt;Value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;Ref&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ComputeCluster&lt;/span&gt;
      &lt;span class="na"&gt;Statistic&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Average&lt;/span&gt; &lt;span class="c1"&gt;# Not using Sum since the metric is CPUUtilization&lt;/span&gt;
      &lt;span class="na"&gt;Period&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;60&lt;/span&gt;  &lt;span class="c1"&gt;# 60 seconds ( Period must be 10, 30 or a multiple of 60 but 10 and 30 can not be used with namespaces with the following prefix: AWS/ )&lt;/span&gt;
      &lt;span class="na"&gt;EvaluationPeriods&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;CpuUtilizationNoComputeOrScaleInAlarmEvaluationPeriodsSSM&lt;/span&gt; &lt;span class="c1"&gt;# setting this 3 as part of taking extra precautions! Change it to 1 if needed&lt;/span&gt;
      &lt;span class="na"&gt;Threshold&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;CpuUtilizationScaleInAlarmThresholdSSM&lt;/span&gt; 
      &lt;span class="na"&gt;ComparisonOperator&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;LessThanOrEqualToThreshold&lt;/span&gt;
      &lt;span class="na"&gt;AlarmActions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Ref&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ProdComputeAutoScalingScaleInPolicy&lt;/span&gt;

  &lt;span class="na"&gt;ProdQueueDepthScaleOutAlarm&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::CloudWatch::Alarm&lt;/span&gt;
    &lt;span class="na"&gt;Condition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;CreateProdResources&lt;/span&gt; &lt;span class="c1"&gt;# only create alarm if it is Prod env&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;AlarmName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Sub&lt;/span&gt; &lt;span class="s"&gt;${DockerContainerName}-ProdQueueDepthScaleOutAlarm&lt;/span&gt;
      &lt;span class="na"&gt;AlarmDescription&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Alarm if queue depth grows beyond specified threshold!&lt;/span&gt;
      &lt;span class="na"&gt;Namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS/SQS&lt;/span&gt; &lt;span class="c1"&gt;# AWS::CloudWatch::Alarm.Period &amp;gt;= 60 for metrics in the AWS/ namespace&lt;/span&gt;
      &lt;span class="na"&gt;MetricName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ApproximateNumberOfMessagesVisible&lt;/span&gt;
      &lt;span class="na"&gt;Dimensions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;QueueName&lt;/span&gt;
          &lt;span class="na"&gt;Value &lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!GetAtt&lt;/span&gt; &lt;span class="s"&gt;SQSQueue.QueueName&lt;/span&gt;
      &lt;span class="na"&gt;Statistic&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Sum&lt;/span&gt;
      &lt;span class="na"&gt;Period&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;120&lt;/span&gt; &lt;span class="c1"&gt;# 120 seconds ( Period must be 10, 30 or a multiple of 60 but 10 and 30 can not be used with namespaces with the following prefix: AWS/ )&lt;/span&gt;
      &lt;span class="na"&gt;EvaluationPeriods&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
      &lt;span class="na"&gt;Threshold&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;QueueDepthScaleOutAlarmThresholdSSM&lt;/span&gt; &lt;span class="c1"&gt;# change this as needed&lt;/span&gt;
      &lt;span class="na"&gt;ComparisonOperator&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;GreaterThanOrEqualToThreshold&lt;/span&gt;
      &lt;span class="na"&gt;AlarmActions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Ref&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ProdComputeAutoScalingScaleOutPolicy&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the above template, I have created two sets of resources &lt;strong&gt;NonProd&lt;/strong&gt; and &lt;strong&gt;Prod&lt;/strong&gt;. In lower environments, the &lt;strong&gt;DesiredCount&lt;/strong&gt; of ECS service is set as &lt;strong&gt;zero&lt;/strong&gt; to save cost. Since CloudWatch Alarm takes at least one minute to respond to scaling events and in prod we do not want any delay that's why the &lt;strong&gt;DesiredCount&lt;/strong&gt; is set as &lt;strong&gt;one&lt;/strong&gt; in prod.&lt;br&gt;
One more thing to note is that the &lt;strong&gt;scale-out&lt;/strong&gt; is based on &lt;strong&gt;queue depth&lt;/strong&gt; but &lt;strong&gt;scale-in&lt;/strong&gt; is based on &lt;strong&gt;CPU utilization&lt;/strong&gt; because the consumer-service consumes a message which can take several minutes to finish and I don't want Application Auto-Scaling to reduce tasks when the queue is empty and they are still processing something. In this regard, EC2 has something called &lt;br&gt;
&lt;strong&gt;instance scale-in protection&lt;/strong&gt; which allows you to have control over which queue workers are terminated when your Auto Scaling group scales in. It was not available for Fargate based ECS clusters but AWS just recently introduced a new feature for ECS called &lt;strong&gt;task scale-in protection&lt;/strong&gt;. You can see the blog post &lt;a href="https://aws.amazon.com/blogs/containers/announcing-amazon-ecs-task-scale-in-protection/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here is the bash script for creating SSM parameters.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;

&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="o"&gt;((&lt;/span&gt;&lt;span class="nv"&gt;$# &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; 1&lt;span class="o"&gt;))&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
  case&lt;/span&gt; &lt;span class="nv"&gt;$1&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
  &lt;span class="nt"&gt;--profile&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;PROFILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$2&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="p"&gt;;;&lt;/span&gt;
  &lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;break&lt;/span&gt; &lt;span class="p"&gt;;;&lt;/span&gt;
  &lt;span class="k"&gt;esac&lt;/span&gt;
  &lt;span class="nb"&gt;shift &lt;/span&gt;2
&lt;span class="k"&gt;done

&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Deleting Parameters..."&lt;/span&gt;
aws ssm delete-parameter &lt;span class="nt"&gt;--profile&lt;/span&gt; &lt;span class="nv"&gt;$PROFILE&lt;/span&gt; &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="s2"&gt;"/&lt;/span&gt;&lt;span class="nv"&gt;$PROFILE&lt;/span&gt;&lt;span class="s2"&gt;/services/consumer-service/AWS_REGION"&lt;/span&gt;
aws ssm delete-parameter &lt;span class="nt"&gt;--profile&lt;/span&gt; &lt;span class="nv"&gt;$PROFILE&lt;/span&gt; &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="s2"&gt;"/config/ecs/FARGATE_SCALING_ENV"&lt;/span&gt;
aws ssm delete-parameter &lt;span class="nt"&gt;--profile&lt;/span&gt; &lt;span class="nv"&gt;$PROFILE&lt;/span&gt; &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="s2"&gt;"/config/ecs/consumer-service/QUEUE_DEPTH_SCALE_OUT_ALARM_THRESHOLD"&lt;/span&gt;
aws ssm delete-parameter &lt;span class="nt"&gt;--profile&lt;/span&gt; &lt;span class="nv"&gt;$PROFILE&lt;/span&gt; &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="s2"&gt;"/config/ecs/consumer-service/CPU_UTILIZATION_SCALE_IN_ALARM_THRESHOLD"&lt;/span&gt;
aws ssm delete-parameter &lt;span class="nt"&gt;--profile&lt;/span&gt; &lt;span class="nv"&gt;$PROFILE&lt;/span&gt; &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="s2"&gt;"/config/ecs/consumer-service/CPU_UTILIZATION_NO_COMPUTE_OR_SCALE_IN_ALARM_EVALUATION_PERIODS"&lt;/span&gt;
aws ssm delete-parameter &lt;span class="nt"&gt;--profile&lt;/span&gt; &lt;span class="nv"&gt;$PROFILE&lt;/span&gt; &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="s2"&gt;"/config/ecs/consumer-service/AUTO_SCALING_TARGET_MAX_CAPACITY"&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Creating parameters..."&lt;/span&gt;
aws ssm put-parameter &lt;span class="nt"&gt;--profile&lt;/span&gt; &lt;span class="nv"&gt;$PROFILE&lt;/span&gt; &lt;span class="nt"&gt;--overwrite&lt;/span&gt; &lt;span class="nt"&gt;--cli-input-json&lt;/span&gt; &lt;span class="s1"&gt;'{"Type": "String", "Name": "/'&lt;/span&gt;&lt;span class="nv"&gt;$PROFILE&lt;/span&gt;&lt;span class="s1"&gt;'/services/consumer-service/AWS_REGION", "Value": "us-east-1"}'&lt;/span&gt;
aws ssm put-parameter &lt;span class="nt"&gt;--profile&lt;/span&gt; &lt;span class="nv"&gt;$PROFILE&lt;/span&gt; &lt;span class="nt"&gt;--overwrite&lt;/span&gt; &lt;span class="nt"&gt;--cli-input-json&lt;/span&gt; &lt;span class="s1"&gt;'{"Type": "String", "Name": "/config/ecs/FARGATE_SCALING_ENV", "Value": "non-prod"}'&lt;/span&gt; &lt;span class="c"&gt;# valid values are: non-prod or prod&lt;/span&gt;
aws ssm put-parameter &lt;span class="nt"&gt;--profile&lt;/span&gt; &lt;span class="nv"&gt;$PROFILE&lt;/span&gt; &lt;span class="nt"&gt;--overwrite&lt;/span&gt; &lt;span class="nt"&gt;--cli-input-json&lt;/span&gt; &lt;span class="s1"&gt;'{"Type": "String", "Name": "/config/ecs/consumer-service/QUEUE_DEPTH_SCALE_OUT_ALARM_THRESHOLD", "Value": "10"}'&lt;/span&gt; &lt;span class="c"&gt;# if you are increasing this then make sure you are also adjusting step scaling criteria&lt;/span&gt;
aws ssm put-parameter &lt;span class="nt"&gt;--profile&lt;/span&gt; &lt;span class="nv"&gt;$PROFILE&lt;/span&gt; &lt;span class="nt"&gt;--overwrite&lt;/span&gt; &lt;span class="nt"&gt;--cli-input-json&lt;/span&gt; &lt;span class="s1"&gt;'{"Type": "String", "Name": "/config/ecs/consumer-service/CPU_UTILIZATION_SCALE_IN_ALARM_THRESHOLD", "Value": "2"}'&lt;/span&gt; &lt;span class="c"&gt;# 2% &lt;/span&gt;
aws ssm put-parameter &lt;span class="nt"&gt;--profile&lt;/span&gt; &lt;span class="nv"&gt;$PROFILE&lt;/span&gt; &lt;span class="nt"&gt;--overwrite&lt;/span&gt; &lt;span class="nt"&gt;--cli-input-json&lt;/span&gt; &lt;span class="s1"&gt;'{"Type": "String", "Name": "/config/ecs/consumer-service/CPU_UTILIZATION_NO_COMPUTE_OR_SCALE_IN_ALARM_EVALUATION_PERIODS", "Value": "3"}'&lt;/span&gt; &lt;span class="c"&gt;# do not set this as 1 in non-prod&lt;/span&gt;
aws ssm put-parameter &lt;span class="nt"&gt;--profile&lt;/span&gt; &lt;span class="nv"&gt;$PROFILE&lt;/span&gt; &lt;span class="nt"&gt;--overwrite&lt;/span&gt; &lt;span class="nt"&gt;--cli-input-json&lt;/span&gt; &lt;span class="s1"&gt;'{"Type": "String", "Name": "/config/ecs/consumer-service/AUTO_SCALING_TARGET_MAX_CAPACITY", "Value": "6"}'&lt;/span&gt; &lt;span class="c"&gt;# put 1 if you want to disable autoscaling at all&lt;/span&gt;

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

&lt;/div&gt;



</description>
      <category>devops</category>
      <category>aws</category>
      <category>ecs</category>
      <category>autoscaling</category>
    </item>
    <item>
      <title>ECS auto-scaling based on Amazon MQ for RabbitMQ’s queue depth.</title>
      <dc:creator>Muhammad Ahmad Khan</dc:creator>
      <pubDate>Tue, 01 Nov 2022 15:15:40 +0000</pubDate>
      <link>https://forem.com/muhammad_ahmad_khan/ecs-auto-scaling-based-on-amazon-mq-for-rabbitmqs-queue-depth-4m5b</link>
      <guid>https://forem.com/muhammad_ahmad_khan/ecs-auto-scaling-based-on-amazon-mq-for-rabbitmqs-queue-depth-4m5b</guid>
      <description>&lt;p&gt;There is a very common use case in the industry about scaling the application based on queue depth. In this regard, AWS itself has published the documentation which can be found &lt;a href="https://docs.aws.amazon.com/autoscaling/ec2/userguide/as-using-sqs-queue.html" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In this blog post we will discuss about ECS auto-scaling based on queue depth through the use of AWS application auto-scaling.&lt;/p&gt;

&lt;p&gt;There are three solutions you can use with AWS application auto-scaling.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Target Tracking (Policy)&lt;/li&gt;
&lt;li&gt;Step Scaling (Policy)&lt;/li&gt;
&lt;li&gt;Scheduled scaling (Action)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here we will discuss about first two only.&lt;/p&gt;

&lt;h2&gt;
  
  
  Target Tracking Scaling Policies:
&lt;/h2&gt;

&lt;p&gt;Target tracking scaling policy increases or decreases the number of tasks that your service runs based on a target value for a specific CloudWatch metric (predefined or customized).&lt;br&gt;
Target tracking scaling policy assumes that it should scale out your ECS tasks when the specified metric is above the target value and scale in when it is below the target value.&lt;br&gt;
In short, in target tracking scaling policies, Amazon ECS creates and manages the CloudWatch alarms that trigger the scaling policy and calculates the scaling adjustment based on the CloudWatch metric and the target value that you specify. With target tracking, AWS controls the scaling adjustments automatically based on your target specified.&lt;/p&gt;
&lt;h2&gt;
  
  
  Step Scaling Policies:
&lt;/h2&gt;

&lt;p&gt;Step scaling policy increases or decreases the number of tasks that your service runs by considering the set of scaling adjustments, known as step adjustments, which means you can set multiple actions to vary the scaling depending on the size of the alarm breach.&lt;br&gt;
In short, step scaling policies increase or decrease current capacity based on a set of scaling adjustments (known as step adjustments) that you specify. With step scaling, you control the scaling adjustments.&lt;/p&gt;

&lt;p&gt;When working with both policies, it must be noted that you can easily scale your application based on queue depth using step scaling but not with target scaling because target scaling will only allow these three predefined metrics in ECS.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ALBRequestCountPerTarget (load balancer metric)&lt;/li&gt;
&lt;li&gt;ECSServiceAverageCPUUtilization&lt;/li&gt;
&lt;li&gt;ECSServiceAverageMemoryUtilization&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Further, since in target scaling AWS itself create CloudWatch Alarm and manage its scaling therefore you do not have the autonomy to set your desired metric for scaling without creating a custom metric. Thus you need to make a custom metric as described in AWS &lt;a href="https://docs.aws.amazon.com/autoscaling/ec2/userguide/as-using-sqs-queue.html" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In order to monitor the number of unconsumed message with SQS we can use metric named as &lt;strong&gt;ApproximateNumberOfMessagesVisible&lt;/strong&gt; and if we want to monitor messages from Amazon MQ for RabbitMQ we can use metric named as &lt;strong&gt;MessageReadyCount&lt;/strong&gt;.&lt;br&gt;
One more problem with using a CloudWatch SQS metric like &lt;strong&gt;ApproximateNumberOfMessagesVisible&lt;/strong&gt; for target tracking is that the number of messages in the queue might not change proportionally to the number of tasks within a service that processes messages from the queue. That's because the number of messages in your SQS queue does not solely define the number of tasks needed. The number of tasks in your ECS service can be driven by multiple factors, including how long it takes to process a message and the acceptable amount of latency (queue delay).&lt;br&gt;
AWS proposes a solution to this problem by introducing a backlog per instance metric with the target value being the acceptable backlog per instance.&lt;/p&gt;

&lt;p&gt;In AWS documentation, it has stated that:&lt;br&gt;
“&lt;em&gt;&lt;strong&gt;Backlog per instance&lt;/strong&gt;: To calculate your backlog per instance, start with the ApproximateNumberOfMessages queue attribute to determine the length of the SQS queue (number of messages available for retrieval from the queue). Divide that number by the fleet's running capacity, which for an Auto Scaling group is the number of instances in the InService state, to get the backlog per instance.&lt;br&gt;
&lt;strong&gt;Acceptable backlog per instance&lt;/strong&gt;: To calculate your target value, first determine what your application can accept in terms of latency. Then, take the acceptable latency value and divide it by the average time that an EC2 instance takes to process a message.&lt;/em&gt;”&lt;/p&gt;

&lt;p&gt;In other words,  backlog per instance is the value obtained by dividing the number of messages yet to be consumed within the queue with the total number of tasks running at the moment.&lt;br&gt;
Similarly, acceptable backlog per instance is the target value that we use in our scaling policy, and it is the acceptable latency value divided by the average time that an ECS task takes to process a message.&lt;br&gt;
This acceptable backlog per instance (target value) varies from application to application and use case to use case.&lt;/p&gt;

&lt;p&gt;Example from AWS documentation:&lt;br&gt;
“&lt;em&gt;As an example, let's say that you currently have an Auto Scaling group with 10 instances and the number of visible messages in the queue (ApproximateNumberOfMessages) is 1500. If the average processing time is 0.1 seconds for each message and the longest acceptable latency is 10 seconds, then the acceptable backlog per instance is 10 / 0.1, which equals 100 messages. This means that 100 is the target value for your target tracking policy. When the backlog per instance reaches the target value, a scale-out event will happen. Because the backlog per instance is already 150 messages (1500 messages / 10 instances), your group scales out, and it scales out by five instances to maintain proportion to the target value.&lt;/em&gt;”&lt;/p&gt;

&lt;p&gt;Following the AWS documentation, I have created a lambda that creates a custom metric in a custom namespace named as “&lt;strong&gt;Queue Based Scaling Metrics&lt;/strong&gt;”. But in this scenario, our application is using AmazonMQ for RabbitMQ rather than SQS.&lt;/p&gt;

&lt;p&gt;See the simple flow of architecture in the below diagram.&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%2F7sk84r1bqhcj75u9i031.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%2F7sk84r1bqhcj75u9i031.png" alt="Architectural flow diagram" width="800" height="634"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In our lambda, we will calculate all the values for our custom metric including backlog per instance as well as acceptable backlog per instance as mentioned in AWS documentation.&lt;br&gt;
For this purpose, we need to set two parameter values (acceptable_latency, time_process_per_message) as mentioned above and these parameters will vary from application to application. In my scenario, I have set these values as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;acceptable latency = 250&lt;/li&gt;
&lt;li&gt;time process per message = 0.8&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here is the lambda code for all the calculations:&lt;/p&gt;

&lt;p&gt;              &lt;code&gt;src/app.py&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;dateutil&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timedelta&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;lambda_handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;cw_client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;cloudwatch&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;ecs_client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ecs&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;cluster_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cluster_name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;service_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;service_name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;mq_cluster_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mq_cluster_name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;mq_queue_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mq_queue_name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;acceptable_latency&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;acceptable_latency&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="n"&gt;time_process_per_message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;time_process_per_message&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="nf"&gt;queue_attribute_calculation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cw_client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ecs_client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cluster_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;service_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mq_cluster_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mq_queue_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;acceptable_latency&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                &lt;span class="n"&gt;time_process_per_message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;queue_attribute_calculation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cw_client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ecs_client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cluster_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;service_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mq_cluster_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mq_queue_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;acceptable_latency&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                &lt;span class="n"&gt;time_process_per_message&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ecs_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;describe_services&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cluster&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;cluster_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;service_name&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="n"&gt;running_task_count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;services&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;runningCount&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Running Task: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;running_task_count&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="n"&gt;yesterday&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;today&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nf"&gt;timedelta&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;days&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;tomorrow&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;today&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nf"&gt;timedelta&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;days&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cw_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_metric_data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;MetricDataQueries&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Id&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;mq1&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;MetricStat&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Metric&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Namespace&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AWS/AmazonMQ&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;MetricName&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;MessageReadyCount&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Dimensions&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                            &lt;span class="p"&gt;{&lt;/span&gt;
                                &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Broker&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Value&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;mq_cluster_name&lt;/span&gt;
                            &lt;span class="p"&gt;},&lt;/span&gt;
                            &lt;span class="p"&gt;{&lt;/span&gt;
                                &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;VirtualHost&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Value&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
                            &lt;span class="p"&gt;},&lt;/span&gt;
                            &lt;span class="p"&gt;{&lt;/span&gt;
                                &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Queue&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Value&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;mq_queue_name&lt;/span&gt;
                            &lt;span class="p"&gt;}&lt;/span&gt;
                        &lt;span class="p"&gt;]&lt;/span&gt;
                    &lt;span class="p"&gt;},&lt;/span&gt;
                    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Period&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Stat&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Sum&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Unit&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Count&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
                &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="n"&gt;StartTime&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;yesterday&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;year&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;yesterday&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;month&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;yesterday&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;day&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;EndTime&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tomorrow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;year&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tomorrow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;month&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tomorrow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;day&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;queue_size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;  &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;MetricDataResults&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Values&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Running Task: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;running_task_count&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Queue Message Count (per second): &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;queue_size&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    Backlog Per Capacity Unit = Queue Size (MessageReadyCount) / Running Capacity of ECS Task Count
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;backlog_per_capacity_unit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;queue_size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;running_task_count&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;ZeroDivisionError&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Handling run-time error:&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;backlog_per_capacity_unit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Backlog Per Capacity Unit: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;backlog_per_capacity_unit&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    Acceptable backlog per capacity unit = Acceptable Message Processing Latency (seconds) / Average time to Process a Message each Task (seconds)
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;acceptablebacklogpercapacityunit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;float&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;acceptable_latency&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nf"&gt;float&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;time_process_per_message&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Acceptable backlog per capacity unit: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;acceptablebacklogpercapacityunit&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="nf"&gt;putMetricToCW&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cw_client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AmazonMQ&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mq_cluster_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Queue&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mq_queue_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;MessageReadyCount&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;queue_size&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Queue Based Scaling Metrics&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;putMetricToCW&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cw_client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AmazonMQ&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mq_cluster_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Queue&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mq_queue_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;BackLogPerCapacityUnit&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;backlog_per_capacity_unit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Queue Based Scaling Metrics&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;putMetricToCW&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cw_client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AmazonMQ&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mq_cluster_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Queue&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mq_queue_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AcceptableBackLogPerCapacityUnit&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;acceptablebacklogpercapacityunit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Queue Based Scaling Metrics&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;putMetricToCW&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dimension_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dimension_value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dimension_sub_attribute_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dimension_sub_attribute_value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;metric_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;metric_value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;namespace&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;cw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;put_metric_data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;Namespace&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;namespace&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;MetricData&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;MetricName&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;metric_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Dimensions&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                    &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;dimension_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Value&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;dimension_value&lt;/span&gt;
                    &lt;span class="p"&gt;},&lt;/span&gt;
                    &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;dimension_sub_attribute_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Value&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;dimension_sub_attribute_value&lt;/span&gt;
                    &lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="p"&gt;],&lt;/span&gt;
                &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Timestamp&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dateutil&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tz&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tzlocal&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
                &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Value&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;metric_value&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Note that in order to get AmazonMQ for RabbitMQ metric (MessageReadyCount) I have used CloudWatch client SDK call because I was unable to find any API call to get this metric directly from AmazonMQ for RabbitMQ broker client SDK.&lt;/p&gt;

&lt;p&gt;I have deployed this lambda using SAM template and here is the solution for it.&lt;br&gt;
              &lt;code&gt;template.yml&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;AWSTemplateFormatVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;2010-09-09'&lt;/span&gt;
&lt;span class="na"&gt;Transform&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::Serverless-2016-10-31&lt;/span&gt;
&lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="s"&gt;Stack which creates a lambda function that is invoked by a CloudWatch Event Rule every minute to update custom metric datapoints through aws sdk calls.&lt;/span&gt;

&lt;span class="na"&gt;Parameters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;String&lt;/span&gt;
    &lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Values can be dev, qa, devops, test, staging and prod respectively&lt;/span&gt;
  &lt;span class="na"&gt;ClusterNameParameter&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;String&lt;/span&gt;
  &lt;span class="na"&gt;ServiceNameParameter&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;String&lt;/span&gt;
  &lt;span class="na"&gt;MqClusterNameParameter&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;String&lt;/span&gt;
  &lt;span class="na"&gt;MqQueueNameParameter&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;String&lt;/span&gt;
  &lt;span class="na"&gt;AcceptableLatencyParameter&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Number&lt;/span&gt;
  &lt;span class="na"&gt;TimeProcessPerMessageParameter&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Number&lt;/span&gt;
  &lt;span class="na"&gt;SecurityGroupIdParameter&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;String&lt;/span&gt;
  &lt;span class="na"&gt;SubnetIdAParameter&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;String&lt;/span&gt;
  &lt;span class="na"&gt;SubnetIdBParameter&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;String&lt;/span&gt;
  &lt;span class="na"&gt;SubnetIdCParameter&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;String&lt;/span&gt;

&lt;span class="na"&gt;Resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;CustomMetricRole&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::IAM::Role&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;ManagedPolicyArns&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole'&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole'&lt;/span&gt;
      &lt;span class="na"&gt;AssumeRolePolicyDocument&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;Version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;2012-10-17'&lt;/span&gt;
        &lt;span class="na"&gt;Statement&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt;
            &lt;span class="na"&gt;Effect&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Allow&lt;/span&gt;
            &lt;span class="na"&gt;Principal&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;Service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;lambda.amazonaws.com'&lt;/span&gt;
                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;events.amazonaws.com'&lt;/span&gt;
            &lt;span class="na"&gt;Action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;sts:AssumeRole'&lt;/span&gt;
      &lt;span class="na"&gt;Policies&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;PolicyName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;LambdaBasicExecutionPolicy'&lt;/span&gt;
          &lt;span class="na"&gt;PolicyDocument&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;Version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;2012-10-17'&lt;/span&gt;
            &lt;span class="na"&gt;Statement&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Sid&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;LambdaExecutionPolicy&lt;/span&gt;
                &lt;span class="na"&gt;Effect&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Allow&lt;/span&gt;
                &lt;span class="na"&gt;Action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;ec2:Describe*&lt;/span&gt;
                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;ec2:CreateNetworkInterface&lt;/span&gt;
                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;ec2:DeleteNetworkInterface&lt;/span&gt;
                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;ec2:DescribeNetworkInterfaces&lt;/span&gt;
                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;cloudwatch:GetMetricData&lt;/span&gt;
                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;cloudwatch:PutMetricData&lt;/span&gt;
                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ecs:*"&lt;/span&gt;
                &lt;span class="na"&gt;Resource&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;*'&lt;/span&gt;
              &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Sid&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ExtraPolicy&lt;/span&gt;
                &lt;span class="na"&gt;Effect&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Allow&lt;/span&gt;
                &lt;span class="na"&gt;Action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;ssm:GetParameters&lt;/span&gt;
                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;ssm:GetParameter&lt;/span&gt;
                &lt;span class="na"&gt;Resource&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;*'&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;PolicyName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;LogPolicy'&lt;/span&gt;
          &lt;span class="na"&gt;PolicyDocument&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;Version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;2012-10-17'&lt;/span&gt;
            &lt;span class="na"&gt;Statement&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="pi"&gt;-&lt;/span&gt;
                &lt;span class="na"&gt;Effect&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Allow&lt;/span&gt;
                &lt;span class="na"&gt;Action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;logs:CreateLogGroup'&lt;/span&gt;
                  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;logs:CreateLogStream'&lt;/span&gt;
                  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;logs:PutLogEvents'&lt;/span&gt;
                &lt;span class="na"&gt;Resource&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;*'&lt;/span&gt;

  &lt;span class="na"&gt;CustomMetricFunction&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AWS::Serverless::Function'&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;FunctionName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Sub&lt;/span&gt; &lt;span class="s"&gt;create-custom-metric-for-scaling-${Env}-cluster&lt;/span&gt; &lt;span class="c1"&gt;# FunctionName uses Env variable to identify its belonging to the Env specific cluster&lt;/span&gt;
      &lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;A function that is invoked by a CloudWatch Event every minute to update custom metric datapoints through aws sdk calls.&lt;/span&gt;
      &lt;span class="na"&gt;CodeUri&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;src/&lt;/span&gt;
      &lt;span class="na"&gt;Handler&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;app.lambda_handler&lt;/span&gt;
      &lt;span class="na"&gt;Runtime&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;python3.9&lt;/span&gt;
      &lt;span class="na"&gt;Timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;900&lt;/span&gt;
      &lt;span class="na"&gt;Role&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!GetAtt&lt;/span&gt; &lt;span class="s"&gt;CustomMetricRole.Arn&lt;/span&gt;
      &lt;span class="na"&gt;Events&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;CloudWatchEventRule&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Schedule&lt;/span&gt;
          &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;Input&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Sub&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;{"cluster_name":&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;"${ClusterNameParameter}","service_name":&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;"${ServiceNameParameter}","mq_cluster_name":&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;"${MqClusterNameParameter}","mq_queue_name":&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;"${MqQueueNameParameter}","acceptable_latency":&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;"${AcceptableLatencyParameter}","time_process_per_message":&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;"${TimeProcessPerMessageParameter}"}'&lt;/span&gt;
            &lt;span class="c1"&gt;# Read more about schedule expressions here: https://docs.aws.amazon.com/AmazonCloudWatch/latest/events/ScheduledEvents.html&lt;/span&gt;
            &lt;span class="na"&gt;Schedule&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cron(* * * * ? *)&lt;/span&gt; &lt;span class="c1"&gt;# Invoke function every minute&lt;/span&gt;
      &lt;span class="na"&gt;VpcConfig&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;SecurityGroupIds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;SecurityGroupIdParameter&lt;/span&gt;
        &lt;span class="na"&gt;SubnetIds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;SubnetIdAParameter&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;SubnetIdBParameter&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;SubnetIdCParameter&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Here is the SAM template deployment command:&lt;br&gt;
First, create the SAM template’s parameter file.&lt;/p&gt;

&lt;p&gt;              &lt;code&gt;.sam-params-dev&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Env=dev
ClusterNameParameter=dev-worker-cluster
ServiceNameParameter=dev-worker-service
MqClusterNameParameter=scan-dev
MqQueueNameParameter=scans_dev
AcceptableLatencyParameter=250
TimeProcessPerMessageParameter=0.8
SecurityGroupIdParameter=sg-123456789
SubnetIdAParameter=subnet-123456789
SubnetIdBParameter=subnet-123456789
SubnetIdCParameter=subnet-123456789
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;sam deploy --stack-name create-custom-metric --template-file template.yml --s3-bucket test-deployment-bucket --capabilities CAPABILITY_IAM --region us-east-1 --parameter-overrides $(cat .sam-params-dev)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Finally, we will use AWS CLI to put the auto-scaling policy with a target value on ECS.&lt;br&gt;
First, create the JSON policy.&lt;/p&gt;

&lt;p&gt;              &lt;code&gt;config-dev.json&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"TargetValue"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;312.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"ScaleOutCooldown"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"ScaleInCooldown"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"CustomizedMetricSpecification"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="w"&gt;
       &lt;/span&gt;&lt;span class="nl"&gt;"MetricName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"BackLogPerCapacityUnit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
       &lt;/span&gt;&lt;span class="nl"&gt;"Namespace"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"Queue Based Scaling Metrics"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
       &lt;/span&gt;&lt;span class="nl"&gt;"Dimensions"&lt;/span&gt;&lt;span class="p"&gt;:[&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
             &lt;/span&gt;&lt;span class="nl"&gt;"Name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"AmazonMQ"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
             &lt;/span&gt;&lt;span class="nl"&gt;"Value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"scan-dev"&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"Queue"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"scans_dev"&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
       &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
       &lt;/span&gt;&lt;span class="nl"&gt;"Statistic"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"Average"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In above policy, the target value is &lt;strong&gt;Acceptable backlog per instance&lt;/strong&gt;. This means it is: &lt;br&gt;
&lt;code&gt;acceptable latency/time process per message = 250/0.8 = 312.5&lt;/code&gt; &lt;strong&gt;BackLogPerCapacityUnit&lt;/strong&gt; is the custom metric that we have created through our lambda in &lt;strong&gt;Queue Based Scaling Metrics&lt;/strong&gt; namespace.&lt;br&gt;
&lt;strong&gt;scan-dev&lt;/strong&gt; is the RabbitMQ cluster name and &lt;strong&gt;scans_dev&lt;/strong&gt; is the queue name.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;aws application-autoscaling register-scalable-target \&lt;br&gt;
--service-namespace ecs \&lt;br&gt;
--scalable-dimension ecs:service:DesiredCount \&lt;br&gt;
--resource-id service/dev-worker-cluster/dev-worker-service \&lt;br&gt;
--min-capacity 1 \&lt;br&gt;
--max-capacity 10 \&lt;br&gt;
--profile dev&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;aws application-autoscaling put-scaling-policy \&lt;br&gt;
--policy-name ecs-scaling-based-on-message-consumption-rate \&lt;br&gt;
--service-namespace ecs \&lt;br&gt;
--resource-id service/dev-worker-cluster/dev-worker-service \&lt;br&gt;
--scalable-dimension ecs:service:DesiredCount \&lt;br&gt;
--policy-type TargetTrackingScaling \&lt;br&gt;
--target-tracking-scaling-policy-configuration file://config-dev.json \&lt;br&gt;
--profile dev&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;We can automate the creation of the application auto-scaling policy with Terraform and CloudFormation if required.&lt;br&gt;
Note that we are just applying the autoscaling policy and AWS will create CloudWatch Alarm itself.&lt;/p&gt;

&lt;p&gt;See the git repository &lt;a href="https://github.com/muhammad-ahmad-khan/Queue-Depth-Based-ECS-Auto-Scaling" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

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