<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Felipe Trindade RSS Feed]]></title><description><![CDATA[A personal tech blog.]]></description><link>https://felipetrindade.com</link><generator>GatsbyJS</generator><lastBuildDate>Wed, 22 Apr 2026 17:16:20 GMT</lastBuildDate><item><title><![CDATA[Bridging the gap between IaC and GitOps with ArgoCD and Terraform]]></title><description><![CDATA[Hi there! Around 2 months ago, I published the following blog post: Bridging the gap between IaC and GitOps with FluxCD and Terraform. Now I…]]></description><link>https://felipetrindade.com/bridge-the-gap-argocd/</link><guid isPermaLink="false">https://felipetrindade.com/bridge-the-gap-argocd/</guid><pubDate>Wed, 22 Apr 2026 11:45:32 GMT</pubDate><content:encoded>&lt;p&gt;Hi there! Around 2 months ago, I published the following blog post: &lt;a href=&quot;https://felipetrindade.com/bridge-the-gap-flux/&quot;&gt;Bridging the gap between IaC and GitOps with FluxCD and Terraform&lt;/a&gt;. Now I&apos;m continuing the &quot;Bridge the Gap&quot; saga but, this time using Terraform and ArgoCD instead.&lt;/p&gt;
&lt;p&gt;As always, the entire code used in this post can be accessed in my &lt;a href=&quot;https://github.com/felipelaptrin/kubernetes-gitops-argocd&quot;&gt;GitHub Repository&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Bridge the Gap between IaC and ArgoCD&lt;/h2&gt;
&lt;p&gt;I’ll skip the explanation of what I mean by “bridge the gap between IaC and GitOps,” since this was fully covered in the &lt;a href=&quot;https://felipetrindade.com/bridge-the-gap-flux/&quot;&gt;Bridging the gap between IaC and GitOps with FluxCD and Terraform&lt;/a&gt; blog post. For this reason, I won’t repeat myself and will instead jump directly into the technical explanation!&lt;/p&gt;
&lt;h2&gt;Scenario&lt;/h2&gt;
&lt;p&gt;Our infrastructure goal is to have a new Kubernetes cluster deployed from scratch and bootstrap it with some good-to-have addons:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;ALB Controller:&lt;/strong&gt; To provision and manage Network Load Balance to expose Kubernetes apps.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;External-DNS&lt;/strong&gt;: To automatically create records in Route53 to expose our apps (domain level).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;External-Secrets&lt;/strong&gt;: To create Kubernetes secrets based on secrets in AWS Secrets Manager.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Istio&lt;/strong&gt;: Implements the Gateway API controller.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Karpenter&lt;/strong&gt;: Handles cluster node scalability.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Metrics-Server&lt;/strong&gt;: Collects metrics of the pods and allows us to scale them based on these metrics (e.g., CPU and Memory).&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Deploying the Infrastructure&lt;/h2&gt;
&lt;p&gt;The infrastructure needs to be deployed in a two-phase process when using Terraform. This is a limitation of the Kubernetes provider for Terraform, since &lt;a href=&quot;https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs#stacking-with-managed-kubernetes-cluster-resources&quot;&gt;it&apos;s required to have an EKS cluster provisioned before using the provider&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Based on that, first we need to deploy the &lt;strong&gt;core&lt;/strong&gt; infrastructure (usually network related and EKS cluster) before moving to the Kubernetes cluster bootstrap.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 501px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/5d5fc9616216f06634051ec7b391509b/55811/Infrastructure-Deployment.drawio.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 37.9746835443038%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAYAAAD5nd/tAAAACXBIWXMAAAsTAAALEwEAmpwYAAABh0lEQVR42m2QD4vaQBDF8/0/ytGCp20ptIUeakxPuFJEpV70khirMYkxyZrLX3/NLio96INhZ+bx3jxWy8cG+XQCVY3IU1a+yTpycEILK1jhJx4Sgdhjhy8tZ+MeHdWfSgFlxWurf300KNw12tP7d9ifP5GvXabWjI9Gj85Dh26/S2/U49fLTzKRMZz0uft6x33/nt6gy4eWW/kW0eKZ3bcvTLodhKGjHQcPJMaQJkmJ8iNP1hhjrjOaDRkvH1kfbZXQ9J4ZzgaMWk6fDjB+64TZgTqOSX7oRP3vKqlWeVvqJOZ8hlQkVE1Jfa5vdcoEeZ4jToKGmqbdybeqS4RI1bE6PlLttjR5gcYF59Zxt9txOEQEfqD6OE7YbP6Qpimu66pZ7sMwJAgCttvtTXtpWkM5XBaSkGmkyLZtsiyjaRrFlWVJFEXtgY0qmfhfo2vdEl6RJAnL5RLTNJXpFVVVMZ/P1X6xWOA4ztt0F7wxlCKZzvd99cqSBySKomC/3+N53uVrDvwPfwGP+1fXEz1aggAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Infrastructure Installation Flow&quot;
        title=&quot;&quot;
        src=&quot;/static/5d5fc9616216f06634051ec7b391509b/55811/Infrastructure-Deployment.drawio.png&quot;
        srcset=&quot;/static/5d5fc9616216f06634051ec7b391509b/c26ae/Infrastructure-Deployment.drawio.png 158w,
/static/5d5fc9616216f06634051ec7b391509b/6bdcf/Infrastructure-Deployment.drawio.png 315w,
/static/5d5fc9616216f06634051ec7b391509b/55811/Infrastructure-Deployment.drawio.png 501w&quot;
        sizes=&quot;(max-width: 501px) 100vw, 501px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;So basically we should:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;First deploy only core infrastructure (this can be easily achieved by using the &lt;code class=&quot;language-text&quot;&gt;--target&lt;/code&gt; flag in &lt;code class=&quot;language-text&quot;&gt;terraform apply&lt;/code&gt; CLI)&lt;/li&gt;
&lt;li&gt;Then deploy the rest of the infrastructure.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It&apos;s important to mention that after the core infrastructure is deploy there is no need to deploy the infrastructure in a two-phase approach, since the EKS cluster is already in place, we are good to deploy the infrastructure entirely.&lt;/p&gt;
&lt;h2&gt;ArgoCD Bootstrap&lt;/h2&gt;
&lt;p&gt;ArgoCD will be installed by the cluster using the &lt;a href=&quot;https://github.com/felipelaptrin/kubernetes-gitops-argocd/blob/f604794fce91e8f691cf74f00b0ee23649f13c6b/src/k8s.tf#L7&quot;&gt;helm_release&lt;/a&gt; resource from the Helm provider for Terraform. After ArgoCD is installed into the cluster, Terraform will never manage ArgoCD version anymore, since we can create an ArgoCD Application that will &lt;a href=&quot;https://sofianedjerbi.com/en/blog/argocd-manage-itself/&quot;&gt;manage ArgoCD installation&lt;/a&gt;. We will get there...&lt;/p&gt;
&lt;p&gt;Ok, ArgoCD installed, now what? Well, we can deploy an &lt;a href=&quot;https://github.com/felipelaptrin/kubernetes-gitops-argocd/blob/f604794fce91e8f691cf74f00b0ee23649f13c6b/src/k8s.tf#L41&quot;&gt;ArgoCD Application&lt;/a&gt; that will deploy other Applications. That&apos;s the &lt;a href=&quot;https://argo-cd.readthedocs.io/en/latest/operator-manual/cluster-bootstrapping/#app-of-apps-pattern-alternative&quot;&gt;App of Apps&lt;/a&gt; pattern, used to bootstrap the cluster installation. In this case, this Application will &lt;a href=&quot;https://github.com/felipelaptrin/kubernetes-gitops-argocd/blob/f604794fce91e8f691cf74f00b0ee23649f13c6b/src/k8s.tf#L56&quot;&gt;deploy all the manifests inside &apos;k8s/bootstrap&apos; folder&lt;/a&gt;. This &lt;a href=&quot;https://github.com/felipelaptrin/kubernetes-gitops-argocd/tree/f604794fce91e8f691cf74f00b0ee23649f13c6b/k8s/bootstrap&quot;&gt;folder&lt;/a&gt; contains basically three manifests:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;App-of-Apps ArgoCD ApplicationSet to manage installation of &lt;code class=&quot;language-text&quot;&gt;addons&lt;/code&gt; (e.g., external-dns, karpenter...)&lt;/li&gt;
&lt;li&gt;App-of-Apps ArgoCD ApplicationSet to manage installation of &lt;code class=&quot;language-text&quot;&gt;apps&lt;/code&gt; (e.g., your enterprise workloads...)&lt;/li&gt;
&lt;li&gt;ArgoCD ApplicationSet to manage ArgoCD&apos;s own installation, so we completely remove Terraform responsability to manage ArgoCD installation! So, the &lt;a href=&quot;https://github.com/felipelaptrin/kubernetes-gitops-argocd/blob/f604794fce91e8f691cf74f00b0ee23649f13c6b/src/k8s.tf#L7&quot;&gt;helm_release&lt;/a&gt; will become a &quot;ghost&quot; resource, only used during the first execution of the second phase &lt;code class=&quot;language-text&quot;&gt;terraform apply&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Bridging the gap with ArgoCD ApplicationSet&lt;/h2&gt;
&lt;p&gt;So far, we covered the ArgoCD bootstrap part but, what about the part that really matters? How can we pass values that comes from infrastructure to the manifests.&lt;/p&gt;
&lt;p&gt;ArgoCD offers a custom resource called &lt;a href=&quot;https://argo-cd.readthedocs.io/en/stable/operator-manual/applicationset/#the-applicationset-resource&quot;&gt;ApplicationSet&lt;/a&gt; that can be used to deploy a custom resource &lt;code class=&quot;language-text&quot;&gt;Application&lt;/code&gt; (that is templated) in a specific cluster given inputs in the &lt;code class=&quot;language-text&quot;&gt;spec.generators&lt;/code&gt; field. The magical part is that, because the &lt;code class=&quot;language-text&quot;&gt;Application&lt;/code&gt; is templated, we can use values that come from the &lt;code class=&quot;language-text&quot;&gt;spec.generators&lt;/code&gt; part. So, if we can somehow control what goes into the generators we can dynamically inject values into the &lt;code class=&quot;language-text&quot;&gt;Application&lt;/code&gt; custom resource.&lt;/p&gt;
&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;ApplicationSet&lt;/code&gt; custom resource is usually used to, with a single manifest, deploy an &lt;code class=&quot;language-text&quot;&gt;Application&lt;/code&gt; into different Kubernetes clusters (remember that ArgoCD can manage &lt;a href=&quot;https://argo-cd.readthedocs.io/en/stable/operator-manual/cluster-management/&quot;&gt;multiple clusters&lt;/a&gt;!). We can manually register these clusters, but as DevOps, we avoid manual steps, so we can &lt;a href=&quot;https://argo-cd.readthedocs.io/en/stable/operator-manual/declarative-setup/#clusters&quot;&gt;declaratively&lt;/a&gt; create a Kubernetes secret that contains information about this Kubernetes cluster (endpoint, name, TLS configuration). Within this secret, we can also add metadata (labels and annotations).&lt;/p&gt;
&lt;p&gt;Given that, bridging the gap became simple, since we can &lt;a href=&quot;https://github.com/felipelaptrin/kubernetes-gitops-argocd/blob/f604794fce91e8f691cf74f00b0ee23649f13c6b/src/k8s.tf#L95&quot;&gt;create this Kubernetes secret using Terraform&lt;/a&gt; to reference the cluster and inject metadata (within the annotations, for example) with values about our infrastructure: Vpc ID, SQS ARN, ACM Certificate ARN...&lt;/p&gt;
&lt;p&gt;Let&apos;s take two scenarios into consideration: deploy a Helm Chart that needs infrastructure information and deploy Kustomize manifests that needs infrastructure information.&lt;/p&gt;
&lt;h3&gt;Scenario 1: Helm Chart&lt;/h3&gt;
&lt;p&gt;Let&apos;s take, for example, the &lt;a href=&quot;https://kubernetes-sigs.github.io/aws-load-balancer-controller/latest/&quot;&gt;AWS ALB Controller&lt;/a&gt; that requires information about the infrastructure: cluster name, vpc id and AWS region.&lt;/p&gt;
&lt;p&gt;We can create an &lt;a href=&quot;https://github.com/felipelaptrin/kubernetes-gitops-argocd/blob/f604794fce91e8f691cf74f00b0ee23649f13c6b/k8s/addons/aws-load-balancer-controller.yaml&quot;&gt;ApplicationSet&lt;/a&gt; (remember that this ApplicationSet will be deployed automatically by the Addons App-of-Apps). This ApplicationSet will use &lt;a href=&quot;https://github.com/felipelaptrin/kubernetes-gitops-argocd/blob/f604794fce91e8f691cf74f00b0ee23649f13c6b/k8s/addons/aws-load-balancer-controller.yaml#L40&quot;&gt;information&lt;/a&gt; that comes from the metadata (annotations) of the Kubernetes Secret containing the cluster information. These metadata were added by the &lt;a href=&quot;https://github.com/felipelaptrin/kubernetes-gitops-argocd/blob/f604794fce91e8f691cf74f00b0ee23649f13c6b/src/k8s.tf#L107&quot;&gt;kubernetes_secret_v1&lt;/a&gt; resource.&lt;/p&gt;
&lt;h3&gt;Scenario 2: Kustomize&lt;/h3&gt;
&lt;p&gt;Let&apos;s take as an example the &lt;a href=&quot;EC2NodeClass&quot;&gt;EC2NodeClass&lt;/a&gt; resource of Karpenter. Suppose we already deployed Karpenter using Helm Chart, but now we would like to deploy the specific custom resources of Karpenter (such as EC2NodeClass and NodePool). We would like to pass to the EC2NodeClass the IAM Role that the EC2 created by Karpenter should use, so let&apos;s bridge the gap!&lt;/p&gt;
&lt;p&gt;First, we create an &lt;a href=&quot;https://github.com/felipelaptrin/kubernetes-gitops-argocd/blob/f604794fce91e8f691cf74f00b0ee23649f13c6b/k8s/addons/karpenter-resources.yaml&quot;&gt;ApplicationSet&lt;/a&gt; that will deploy the kustomize resources. Then, we use the Kustomize feature to &lt;a href=&quot;https://github.com/felipelaptrin/kubernetes-gitops-argocd/blob/f604794fce91e8f691cf74f00b0ee23649f13c6b/k8s/addons/karpenter-resources.yaml#L37&quot;&gt;patch the manifests&lt;/a&gt; with the values that comes from the metadata of the Kubernetes cluster Secret, which was created by &lt;a href=&quot;https://github.com/felipelaptrin/kubernetes-gitops-argocd/blob/main/src/k8s.tf#L117&quot;&gt;Terraform&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Try it!&lt;/h2&gt;
&lt;p&gt;This is one of the things that explaining won&apos;t help much: you must try and see it for yourself. Demo it! The repository is ready to be used. Take your time to read the manifests and understand how things are connected!&lt;/p&gt;
&lt;h2&gt;Cya&lt;/h2&gt;
&lt;p&gt;Hope you enjoyed this blog post! See you in the next post! 👋&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Self-Managed and AWS-Managed RAG System]]></title><description><![CDATA[Today we are going to do something different than what I usually post here. We are going to deploy a Self-Managed and AWS-managed RAG System…]]></description><link>https://felipetrindade.com/rag/</link><guid isPermaLink="false">https://felipetrindade.com/rag/</guid><pubDate>Sat, 28 Feb 2026 10:40:32 GMT</pubDate><content:encoded>&lt;p&gt;Today we are going to do something different than what I usually post here. We are going to deploy a Self-Managed and AWS-managed RAG System to explore it more and understand more about RAG systems. If you are not familiar with it, don&apos;t worry, we will get there! It&apos;s essential to note that I&apos;m not a Machine Learning Engineer and definitely not an expert in this matter, but I&apos;ve participated in several projects involving the RAG system in AWS, and I believe I can explain it in a way that other DevOps professionals could benefit from.&lt;/p&gt;
&lt;p&gt;Check out the code used on this &lt;a href=&quot;https://github.com/felipelaptrin/rag&quot;&gt;GitHub Repository&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;RAG explained&lt;/h2&gt;
&lt;p&gt;RAG (Retrieval Augmented Generation) is a pattern used with LLM to get answers given external knowledge/context to the LLM. Why is this important? Well, have you ever had an interaction with an LLM that couldn&apos;t provide a meaningful response? Maybe you asked about something that was specific to your company, and the LLM hallucinated or gave a wrong answer. This happened because the LLM didn&apos;t have the knowledge about what you were asking, maybe because the context you have is private and the LLM has no access (e.g., information that only exists in internal documents of your company), or maybe because the LLM was not trained with that piece of data (outdated data). The RAG system allows us to get better responses from the LLM given an external data collection. So, to summarize, RAG is a technique used to &lt;strong&gt;retrieve&lt;/strong&gt; data from an external data collection where this data is &lt;strong&gt;augmented&lt;/strong&gt; (injected into LLM prompt) at run-time and the LLM &lt;strong&gt;generates&lt;/strong&gt; a response given that extra piece of context. This technique is very common to be implemented in AI customer chatbots (RAG is used to retrieve information about refunds, shipping, policies...) and AI assistants (use RAG to have more context about a PDF, file you uploaded).&lt;/p&gt;
&lt;p&gt;A RAG system has two phases:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Indexing Phase&lt;/strong&gt;: This part we get the data collection (PDFs, Web pages, internal documentation...) and chunk the text into smaller pieces, embed each chunk into a vector, and finally store in a vector database&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Querying Phase&lt;/strong&gt;: The application will use the vector database to retrieve the most similar chunks and inject this in the prompt in run-time.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now, it&apos;s possible that you have multiple questions: What is this Vector Database? How was this database populated? What is this &quot;similar search&quot;? What is a vector? Don&apos;t worry, I plan to cover all these technicalities, so keep reading, and we will get there!&lt;/p&gt;
&lt;p&gt;Visually, the querying phase with a RAG system goes like this:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/a6eef4a10fbf90edf73dea688fdfe30a/01dae/RAG-Query-Phase.drawio.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 52.53164556962025%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAAsTAAALEwEAmpwYAAABYElEQVR42pVS207CQBDt/3+E0fiib/roi9cHTTCASUXUpFQS6JXalmW3l90ed1ZKSgIJTDJpd2f2zJkzYymlQN40DZbLJYQQIGvvjzWLAMqyRJ7nmM/nxhlj5o5zjqqqNslUtPtdH/7Pa7eklJtYXddbrChWFMUWCMWpeNeaLkNiQ0kEFscx0jSF0kBMSLg+gzNLEefS5GRZhjAM4XkekiSB0F3UUYT0c4zo3YbS7y1qkdoiYMdxMJm4qEqBIK3wPIpx23Px7ZWmIMWHwyFs28ag30eiCxRfn+idn+Hl9AR8/AGLqrYttU5WyQYrzTLnFTJWbLogCchJX6VzleAQcQSumcoVg9UVvKuTUhLJIoY3n+E3WZghHTTlXZckAenpez6mP1MEfmBWaqd1JmymvK+S5otCa7nizPxvrcoxDNuHQe7j+ukKlzcXeHMHhy/2PsAwDfA4eMD96x1Grn0w4B/bzlcy3QPuRAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Rag System Architecture&quot;
        title=&quot;&quot;
        src=&quot;/static/a6eef4a10fbf90edf73dea688fdfe30a/f058b/RAG-Query-Phase.drawio.png&quot;
        srcset=&quot;/static/a6eef4a10fbf90edf73dea688fdfe30a/c26ae/RAG-Query-Phase.drawio.png 158w,
/static/a6eef4a10fbf90edf73dea688fdfe30a/6bdcf/RAG-Query-Phase.drawio.png 315w,
/static/a6eef4a10fbf90edf73dea688fdfe30a/f058b/RAG-Query-Phase.drawio.png 630w,
/static/a6eef4a10fbf90edf73dea688fdfe30a/01dae/RAG-Query-Phase.drawio.png 721w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;First, the user asks something to the application, let&apos;s suppose an AI Chatbot. Let&apos;s say the user asked, &quot;The shirt I bought arrived yesterday, but it&apos;s too big for me. Can I exchange it for a smaller size?&quot;&lt;/li&gt;
&lt;li&gt;The app will get the user&apos;s question, transform it into a vector using an embedding model, and send this vector to the vector database to perform a similarity search. In this case, it will understand it&apos;s about &lt;code class=&quot;language-text&quot;&gt;Returns, Refunds, Exchanges&lt;/code&gt; and retrieve information about the return policy of the company.&lt;/li&gt;
&lt;li&gt;With the response from the vector database, the application can now inject this into a prompt (about the return policy) to an LLM to respond to the user&apos;s question.&lt;/li&gt;
&lt;li&gt;The application will return the LLM response with a more concise and better context response.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The indexing phase with a RAG system goes like this:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/4ed1a3099dd78ba2d195e056234147db/f5209/RAG-Indexing-Phase.drawio.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 24.050632911392405%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAFCAYAAABFA8wzAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA50lEQVR42nWQfWrEIBDFc/+blF6hpygttNkEStdESbJqtCZ+5m3G/a+0D2RG8P18M40QAqUUHMeB/5RzhnMO27YhxoiUUq1/+RpjTDWEEJBTQQwR3nvgOA35YSZY13UVSHcpJcZxhFIK+76f/gQfPXLJaIZhgLEGSioM7QS53DDNNxT3CW+/cWUcxqzo+x70ljEGCjHPM9Z1redybfH88oS3r1c0nPP6a/ABH+9tTUoaWQ8tp9pba2tCAlHaZVlAPq11HT2c6dSPggsODcWmPRBUiAecxLk4R9K1p10RlCqJIDQq1d+6A2VMgQPXB23SAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Rag System Architecture&quot;
        title=&quot;&quot;
        src=&quot;/static/4ed1a3099dd78ba2d195e056234147db/f058b/RAG-Indexing-Phase.drawio.png&quot;
        srcset=&quot;/static/4ed1a3099dd78ba2d195e056234147db/c26ae/RAG-Indexing-Phase.drawio.png 158w,
/static/4ed1a3099dd78ba2d195e056234147db/6bdcf/RAG-Indexing-Phase.drawio.png 315w,
/static/4ed1a3099dd78ba2d195e056234147db/f058b/RAG-Indexing-Phase.drawio.png 630w,
/static/4ed1a3099dd78ba2d195e056234147db/40601/RAG-Indexing-Phase.drawio.png 945w,
/static/4ed1a3099dd78ba2d195e056234147db/f5209/RAG-Indexing-Phase.drawio.png 1061w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;First, we collect all the documents we would like to index (PDF, texts, URL...) and store them somewhere (e.g., object store service like S3)&lt;/li&gt;
&lt;li&gt;Convert the data to text and perform cleanup on the data&lt;/li&gt;
&lt;li&gt;Chunk the converted text data into multiple chunks using a chunking strategy and add metadata to it&lt;/li&gt;
&lt;li&gt;Create the embeddings for each chunk&lt;/li&gt;
&lt;li&gt;Store vector embeddings, original text, and metadata in an index in the vector database&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Technicalities&lt;/h2&gt;
&lt;p&gt;Let&apos;s define some jargon that was used!&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Vector&lt;/strong&gt;: If you went to college in a STEM field (Science, Technology, Engineering, Math) you studied this for sure! A vector is simply a list of numbers living in a N-dimentional space. It&apos;s basically a generic math object that represents something (in physics, we use a lot to represent the direction of a force, for example).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Embedding&lt;/strong&gt;: It is a numerical representation of data that captures its relevant qualities in a way that ML algorithms can process. In other words, think of it as the mathematical representation of something (image, text, audio...) that captures the relevant qualities, such as: meaning/topic when it&apos;s a text, shapes/textures in pictures... We need to convert the data to numbers; that&apos;s how we can perform mathematical logic in ML. The data is embedded in n-dimensional space.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Vector Embedding&lt;/strong&gt;: Sometimes you will see people only saying &quot;embeddings&quot; instead of &quot;vector embedding&quot; because in the ML context, embeddings are almost always used in the vector form. The process of transforming the data to a vector embedding is performed by an &lt;code class=&quot;language-text&quot;&gt;embedding model&lt;/code&gt;, i.e., a machine learning model that is specialized in performing embedding.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Vector Similarity&lt;/strong&gt;: Now that we have our data transformed into a vector embeddings, we can mathematically perform operations to know if one vector is similar to another vector. There are multiple methods to perform this, but the main ones are: dot product, cosine similarity, and Euclidean distance. The differences between them are outside the scope of this blog post. Why is this important? Simple! We can retrieve from a text information about &lt;code class=&quot;language-text&quot;&gt;refunds&lt;/code&gt; even if the user said &lt;code class=&quot;language-text&quot;&gt;I want my money back&lt;/code&gt;, because they are similar.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Chunking&lt;/strong&gt;: Documents are too long to embed in a single vector, that&apos;s why chunking comes to rescue! Chunking means splitting the data into multiple smaller pieces that will later be embedded into vector embeddings. But there is a trade-off: smaller chunkings give a more precise retrieval but might miss context, while larger chunkings have more context but might be noisier. So the Machine Learning Engineer needs to use techniques to find the best chunking strategy.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Vector Database&lt;/strong&gt;: Traditional databases are not optimized to deal with high-dimensional data and perform ANN (Approximate Nearest Neighbor) at scale, i.e. vector embedding requires optimized databases to work with. Top Vector Databases are: Qdrant, Pgvector (an extension of PostgreSQL), Milvus, ElasticSearch.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ANN&lt;/strong&gt;: Approximate Nearest Neighbor is very popular algorithm that performs efficient search at scale. It&apos;s important not confuse it with the similarity metrics mentioned before (dot product, cosine similarity, and Euclidean distance). While the similarity metric answers &quot;How similar are these two vectors?&quot;, the ANN answers &quot;How do we find the top-K most similar vectors?&quot;. Imagine having to compare the embedded vector with millions of embedded vectors. This is an &lt;code class=&quot;language-text&quot;&gt;O(n)&lt;/code&gt; operation (using Big-O notation), and it&apos;s not the fast method... That&apos;s where ANN comes into play, by sacrificing a bit of accuracy (that&apos;s why it has &lt;code class=&quot;language-text&quot;&gt;Approximate&lt;/code&gt; in its name!) to gain speed.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Metadata&lt;/strong&gt;: So far, everytime I talked about similarity and retrieval I only mentioned the data itself, but in reality it&apos;s very common in professional RAG systems to also use metadadata. During the indexing phase for each chunk, you usually store three things: the data (e.g., the text of the chunked context), the embedding vector (the numeric representation), and the metadata. The metadata holds attributes about the chunk that can be used to filter during the querying phase. Example of metadata fields are: language, region, source, title, section, page number, department, owner, tenant id...&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I think this is the minimum you need to know in order to understand the basics of RAG. The Machine Learning Engineer needs to know way more than this and also apply the best strategy for chunking (size and overlap of each chunk), embedding (which embedding model to use), retrieval (how similar the words need to be and filters to use), and much more.&lt;/p&gt;
&lt;h2&gt;Hands On&lt;/h2&gt;
&lt;p&gt;Let&apos;s deploy the RAG architecture to serve a customer-facing chatbot responsible for answering eBay clients&apos; questions. Ebay offers extensive documentation for helping sellers and buyers: more than 10 different pages talking about different topics. That&apos;s a great dataset for us! For our self-managed RAG system, I will open each page and download the page as PDF, for the AWS-managed solution, I will simply use the URL of the website. This way, I can present two different ways of dealing with input data for RAG.&lt;/p&gt;
&lt;h3&gt;Self-Managed RAG System&lt;/h3&gt;
&lt;p&gt;For the self-managed RAG system, I propose an event-based architecture that, every time a new PDF is added to the S3 Bucket, the entire process starts and does the parsing (PDF to text), chunking, and embedding process. Since the PDFs are small and the duration to run these processes is not long, it makes sense to trigger the orchestration (Step Function Workflow) of the process using the S3 upload event as input, run each process in Lambda (serverless, cheap, easy to maintain), and store it in the Vector Database. For the database, I will use Qdrant, since it&apos;s an open-source solution with great community support. For simplicity (this is now prod-ready design...) I will run the database in EC2 (data stored in EBS volume) managed by ECS. I void the usage of Fargate because I would need to use NFS for storing the data, and Qdrant docs explicitly say that &lt;a href=&quot;https://qdrant.tech/documentation/guides/installation/#storage&quot;&gt;Qdrant does not work with Network file systems&lt;/a&gt;. And of course, an API to receive the user question, perform similarity search in the vector database, and talk to a generative LLM to get a response (given a prompt and returned data from the vector database). For this, I will stick with the classic API Gateway + Lambda approach! Oh, and we can even leverage the new &lt;a href=&quot;https://aws.amazon.com/about-aws/whats-new/2025/11/api-gateway-response-streaming-rest-apis/&quot;&gt;API Gateway streaming feature&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The orchestration is actually very simple: extract the PDF S3 Object key from the event and trigger the &lt;code class=&quot;language-text&quot;&gt;pdf-to-text&lt;/code&gt; Lambda, then the &lt;code class=&quot;language-text&quot;&gt;chunking&lt;/code&gt;, and finally the &lt;code class=&quot;language-text&quot;&gt;embedding&lt;/code&gt;. Clean, simple, and it works! Each PDF gets a &lt;code class=&quot;language-text&quot;&gt;doc_id&lt;/code&gt; and is stored in an organized way in the S3 bucket:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;raw/      # Prefix used to PDF files uploaded by employees
clean/    # JSON containing the extracted text from PDF
chunks/   # JSONL (each line is a JSON) containing the chunk&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;AWS managed RAG System&lt;/h3&gt;
&lt;p&gt;Well, for the AWS-managed RAG system the things get... way simple! Bedrock Knowledge Bases is an AWS service that simplifies by A LOT the RAG system workflow. Indeed, you only need to define two things: the storage configuration (where the data will be stored - vector database) and the data source (where the data comes from). In our case we will use &lt;code class=&quot;language-text&quot;&gt;WEB Crawler&lt;/code&gt; data source (URLs) and the &lt;a href=&quot;https://docs.aws.amazon.com/bedrock/latest/userguide/webcrawl-data-source-connector.html?utm_source=chatgpt.com&quot;&gt;AWS Documentation&lt;/a&gt; tell us that the only supported vector database supported is &lt;code class=&quot;language-text&quot;&gt;OpenSearch Serverless&lt;/code&gt;, so that&apos;s it. In the date I&apos;m writing this blog post (Feb 2026) the Web Crawler data source is in preview release and is subject to change, but let&apos;s use it anyway. Here we would still need to use an API to perform the RAG process but since Knowledge Bases offers an UI to test your knowledge base I will stick with it for this hands on experiment.&lt;/p&gt;
&lt;h3&gt;Proposed Architecture&lt;/h3&gt;
&lt;p&gt;Based on that, we have our proposed architecture for this project!&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/8345041abd3c3b9db664f506c4bc6498/98314/RAG-Architecture.drawio.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 111.39240506329114%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAWCAYAAADAQbwGAAAACXBIWXMAAAsTAAALEwEAmpwYAAAD2klEQVR42pWVS3PbVBiG88NYMAysWLBjwS+APQMLdqxYQRYwXbAgkJYy7bS0BAqkzqXTOEnjxo4vutiWHdnxVZbi+CpLlvTwWbmVMh3aM/OOdaRznnPe7/vO8RIRcRsmVfymTTRyIQyvJW3ueUwGA6ajEe6FomDxTSZH12PDmc9SKC/CICDyAwZuSLs/xhp62OMI+8zH6U5pOS5Gw8Go2VRbZ6I+vWGALd/svk9vFHI2jQj9OUuTyYR+v0+r0UI/6bH88AMS6UeoPSjcy9N8/3uUvEOh46FnHMrZPkprxlHxlOaHP1K9q1E4hWNrfg7sdrucOg6zyZS67fLH3gMOq8cYI1AyXXKrB2jNGSWZdFjo8PzwhHIf1JZHamWP/G6dso3MDc6BixBG4j+STrM/R5fBRrHMyeZqDClMZfX9R9QySRTZda4DtZ37VHPPOToDrR9SbAfUnAsgLwIdj8LeFqX9xxw//gHDtFGTW5QTN6kePqWkaGI7jbF5m0p2D0MfUNnR0QVYXwC9C2CcpQCa1hBr+W3M3d/Ij8FMbeAsv4Vu9lAH0LrzGZ2bn6DIzjXpV35ex/z0qwtgeA2MQjE+DzhxfHYOVPKGZFPiopgDkimVYtOlInYPcsfspQ0MK4yVVi12n1Uodl7a4aXlRj9AsSNKvZCSrKp3gzjbJZkQqyvxkn7RiuK+vpC8u7J8WTaddodms4mmtWl/dJPinSPSskP1yZ+cfvceZYllSSAFS+CJWzTufoHqgN7yZWEBtsK4bBabWhoMJW6WJcAGZmtC5utNCk/NONtFtYy2doOKNeZgF35djsjmTLRSne69z+n9/g0PM/dYSXzMce/cpZyUiEDiN3ddieGcg8aUgtTYwrIqqx/WxaodcpDyWPtpRL7uU5ZyUbbX0JMbbKtZ7u/cxpRQ/Ssp0XwuR06OnlhrCKAmA+r2ojY9eRZLtk/FmWJ0XKqdmRT/iGxtSrExIyOnYFHDL9VhwHTukm5UJbsGtdYxFbNOSW0zdEcEizGLO2DhSDaw+I119UzMuALKKDkZVd7duMW3RwnyZpLHCYVfvpyg1IpSpj7/1/4DLFsGN548YF1NUnI09tVD/l5PoTQUgmj+hsB5iHlqkqmmJCl5dEsl1TxirZpEtTRZ742BUpxnNdL1dCy9mWWlsMU7idvs1jKMRgPG4/EbAP1zYL6dQ+kU0LoK2/oeq8/+ItvIiQGfmdzcrwVEshR5Pp47YToZ4k7H5xoP8UTuZEwg1/uZfRqXRvQKxX8B1/joXK9o3sxje2sbu9fjytXlnBe0xGu2SAZrmkbvEviKxf8BSStqSmJG6soAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;RAG Architecture for AWS-Managed and Self-Managed RAG System&quot;
        title=&quot;&quot;
        src=&quot;/static/8345041abd3c3b9db664f506c4bc6498/f058b/RAG-Architecture.drawio.png&quot;
        srcset=&quot;/static/8345041abd3c3b9db664f506c4bc6498/c26ae/RAG-Architecture.drawio.png 158w,
/static/8345041abd3c3b9db664f506c4bc6498/6bdcf/RAG-Architecture.drawio.png 315w,
/static/8345041abd3c3b9db664f506c4bc6498/f058b/RAG-Architecture.drawio.png 630w,
/static/8345041abd3c3b9db664f506c4bc6498/98314/RAG-Architecture.drawio.png 817w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;It&apos;s very important to say that this is not a silver bullet architecture that works great for all scenarios. This is simply a recommended architecture for this hands-on blog post!&lt;/p&gt;
&lt;h3&gt;Deployment&lt;/h3&gt;
&lt;p&gt;As always, the code for this hands-on experiment is in my &lt;a href=&quot;http://github.com/felipelaptrin/rag&quot;&gt;GitHub Repository&lt;/a&gt;. As you might imagine, there are several parts in this project:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;pdf-to-text&lt;/code&gt; Lambda code&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;chunking&lt;/code&gt; Lambda code&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;embedding&lt;/code&gt; Lambda code&lt;/li&gt;
&lt;li&gt;API Lambda code&lt;/li&gt;
&lt;li&gt;Terraform code&lt;/li&gt;
&lt;li&gt;GitHub Actions CI/CD code&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And pasting all this code here would be too much, so I will intentionally not paste any code here and encourage you to check the repository. Since it&apos;s all Terraform, feel free to deploy in your environment and test it!&lt;/p&gt;
&lt;h3&gt;Results&lt;/h3&gt;
&lt;p&gt;I&apos;ve used a very simple prompt for the generative LLM that goes like this:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;txt&quot;&gt;&lt;pre class=&quot;language-txt&quot;&gt;&lt;code class=&quot;language-txt&quot;&gt;You are answering questions using only the provided eBay help-center context.
Treat the context as the only allowed source of truth.
If the answer is not clearly supported by the context, say:
&apos;I do not have enough information in the provided knowledge base to answer that clearly.&apos;
Do not guess.
Do not invent UI steps, actions, deadlines, or policies.
Do not combine buyer and seller flows unless the context explicitly says both apply.
If the user question is buyer-specific, prefer buyer instructions.
If the user question is seller-specific, prefer seller instructions.
If the role is unclear and the context differs by role, say so.
Ignore irrelevant context.
Return only plain English text, short and factual, in one paragraph.

Question: $query$

Context: $search_results$

Answer the question using only the context above.&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;where &lt;code class=&quot;language-text&quot;&gt;$query$&lt;/code&gt; corresponds to the user input question and &lt;code class=&quot;language-text&quot;&gt;$search_results$&lt;/code&gt; the results of the similarity search in the vector database.&lt;/p&gt;
&lt;h4&gt;Results - Self-Managed RAG&lt;/h4&gt;
&lt;p&gt;I&apos;ve input several questions into the system and compiled all the answers &lt;a href=&quot;https://github.com/felipelaptrin/rag/blob/main/self-managed-answers.txt&quot;&gt;here&lt;/a&gt;, some examples below:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;txt&quot;&gt;&lt;pre class=&quot;language-txt&quot;&gt;&lt;code class=&quot;language-txt&quot;&gt;Q: My package didn&apos;t arrive. What do I do?

A: If your item hasn&apos;t arrived, check your order confirmation email
for the expected delivery date and shipping address, and select
“View order details” to see the latest tracking updates. If the
estimated delivery date has passed, report that your item hasn&apos;t
arrived by contacting the seller through your order confirmation
email, selecting “I haven&apos;t received my item yet,” and choosing
“No, I want to contact the seller.” You have up to 30 calendar days
from the estimated delivery date to do so, and the seller should
respond within 3 business days with an update, replacement, or refund.&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;and&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;txt&quot;&gt;&lt;pre class=&quot;language-txt&quot;&gt;&lt;code class=&quot;language-txt&quot;&gt;Q: Item arrived broken, do I open a return or something else?

A: If an item arrives broken, you should go to your Purchases
and select “See request details” to close your request. Select
a reason for closing the return and add details if you want to,
then confirm.&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Sometimes the responses were on point, sometimes they looked weird. That&apos;s because we need to tune the system, perform better cleaning (PDF to text)... Not the perfect system, but the idea has been proven. And it works! In the logs of the API, you can even see the chunk returned by the similarity search operation.&lt;/p&gt;
&lt;h3&gt;Results - AWS Managed&lt;/h3&gt;
&lt;p&gt;I didn&apos;t compile all the responses that came from the AWS Knowledge Base, but I screenshot some responses.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/2a700270bcfaebbb8ab09b6e58bc1ed9/8698d/aws-managed-rag-response-1.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 67.08860759493672%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAYAAACpUE5eAAAACXBIWXMAAAsTAAALEwEAmpwYAAAB8ElEQVR42o1Ta4/jIAzM//9/++H21CRt836ngQAJZM522tVW2jtdpBEQ8DC2h2iaFxTthKafUQ80bybUvB41jPOw1mJdV3jvCeFHBMK6BfTaIxpmjSyvkN1ztG2LYRjRdT3UohDCAed2KOOJ1GLf/Y9g0nlZ0c0GEatougFFWVKww+tjMiZZVyPq58dD/h/HIQghfIHXD9pfCBGz50WBz98XtN2IqulFYV23UOpUyYeV0pK+pUutdVKC76SsdNv2k3Ch9Nq2Q0lk02JoYxO1r4PWbdDa0Dkto1Kr/CdhAjom474HRHwbk52164QMxzM1UufDjr5wSOMKSXpFEt+QXu9IkpsgTq6I4yvS9C6XRZzK5RKjLCt0/UDFNVJkR/LreTvVKUtBd/z6vFBghrJqkVMji7JBUTQU2+CW1VTn9Ux5GEfc7hkdKsgyHcZpEvJ21F+pGruRei9pMbw/3ubaBmgjNfRYqPg9ETAJ28UY6i6B97jLvObm/M2H4sUQ5Hx0pueIwEg3J1I3zWQTAnf0fwkZ3KhI2k2BTuxg38ZwhCeheQbs/zQ3IwokM846fFxyNDUVuKqp0CUeZA3+nFhGQz2h9QpNT5FHbiiD98+n6ZmQmjItyKlzXMeaSNnQ/H45Vb6d1R4vKz299z6H1JAt+AeVc+4ShkFUMgAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;The screenshot shows the Amazon Bedrock Knowledge Bases test interface for a knowledge base named knowledge-base-dev, with configuration options on the left and a preview chat on the right answering a question about an item that didn&amp;#39;t arrive.&quot;
        title=&quot;&quot;
        src=&quot;/static/2a700270bcfaebbb8ab09b6e58bc1ed9/f058b/aws-managed-rag-response-1.png&quot;
        srcset=&quot;/static/2a700270bcfaebbb8ab09b6e58bc1ed9/c26ae/aws-managed-rag-response-1.png 158w,
/static/2a700270bcfaebbb8ab09b6e58bc1ed9/6bdcf/aws-managed-rag-response-1.png 315w,
/static/2a700270bcfaebbb8ab09b6e58bc1ed9/f058b/aws-managed-rag-response-1.png 630w,
/static/2a700270bcfaebbb8ab09b6e58bc1ed9/40601/aws-managed-rag-response-1.png 945w,
/static/2a700270bcfaebbb8ab09b6e58bc1ed9/8698d/aws-managed-rag-response-1.png 1239w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/826c5dfc0f1f4f1cc22cb1dd49bbe486/8698d/aws-managed-rag-response-2.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 67.08860759493672%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAYAAACpUE5eAAAACXBIWXMAAAsTAAALEwEAmpwYAAAB+0lEQVR42nVTCXKjMBDk/9/beJ2snWCHy4C5hTgE9E4P8ValnKWqa3SMWj09wsvyAic/wsc1xiXMcL7EeL9ECG4VxmnFMAzoews3O6zrimV5xiqwkluYBV43LLilBYIgQNt2uunmRRIFboFzQmpHVGZGXtQyXzDP38GcsqzRNB28cXIyadDLoe8Klv12WSNJ8XWA37Y9YxwnqWSAx+QoiuBfQ0xy27buJMIlRA7WDqLCCSYt35heLXig63q9cF03jR4HVVUjDGMEgihKEMUp4rxGLv7WVaPEpp3QtaNesJPt0QimadayCY+lpWkmHoaiYD9grZUSqGxGL4rcMqPOHM6vMV4Ov3E4vOHXy6vG4/Ek4yOOrycYUevRp7quESeJKirKCkUhKEvcJeZVr/4O44gkSfH25wP+JZD8TITcpaGCW47PMEXTCiH9uRcl0ixHHCeqkKpZppPIj6W1rZG9Ucpz/zr7AEWZgXDwaHKc3PQQveAmtHObGm3nTXwatItc++kdPl4DhaiHLPf87sP3r4hFftO0cgFLHRAVFk1HT0e94H+EhHaZg67rpGzxT0rnmGTTNGnZ27romAo535/Qjuf5FyG72rQtyqoSr3Z1xhiNVMZkxl7y+BbpM6P+lrJmFbst3v4rzariQURQKUEiKmEyfaKPP4F7fHZ/AT0A7QsH5yEUAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;The screenshot shows the Amazon Bedrock Knowledge Bases test interface for a knowledge base named knowledge-base-dev, with configuration options on the left and a preview chat on the right answering a question about a broken item return.&quot;
        title=&quot;&quot;
        src=&quot;/static/826c5dfc0f1f4f1cc22cb1dd49bbe486/f058b/aws-managed-rag-response-2.png&quot;
        srcset=&quot;/static/826c5dfc0f1f4f1cc22cb1dd49bbe486/c26ae/aws-managed-rag-response-2.png 158w,
/static/826c5dfc0f1f4f1cc22cb1dd49bbe486/6bdcf/aws-managed-rag-response-2.png 315w,
/static/826c5dfc0f1f4f1cc22cb1dd49bbe486/f058b/aws-managed-rag-response-2.png 630w,
/static/826c5dfc0f1f4f1cc22cb1dd49bbe486/40601/aws-managed-rag-response-2.png 945w,
/static/826c5dfc0f1f4f1cc22cb1dd49bbe486/8698d/aws-managed-rag-response-2.png 1239w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;For these questions, very similar results.&lt;/p&gt;
&lt;h2&gt;Cya!&lt;/h2&gt;
&lt;p&gt;Well, I hope you liked this blog post!&lt;/p&gt;
&lt;p&gt;See you around! 👋&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Bridging the gap between IaC and GitOps with FluxCD and Terraform]]></title><description><![CDATA[This blog post is a very special one for two main reasons: It marks two full years of my blog posts! On average, I delivered one technical…]]></description><link>https://felipetrindade.com/bridge-the-gap-flux/</link><guid isPermaLink="false">https://felipetrindade.com/bridge-the-gap-flux/</guid><pubDate>Mon, 05 Jan 2026 11:45:32 GMT</pubDate><content:encoded>&lt;p&gt;This blog post is a very special one for two main reasons:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It marks two full years of my blog posts! On average, I delivered one technical blog post per month (some months I posted two!).&lt;/li&gt;
&lt;li&gt;This post tackles a real problem that I&apos;ve faced for years, and never really liked how others addressed this issue… I&apos;m really proud of the proposed solution that will be described here, and I&apos;m pretty sure it has room for improvement, but it&apos;s a good start.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As always, the entire code used in this post can be accessed in my &lt;a href=&quot;https://github.com/felipelaptrin/kubernetes-gitops-flux&quot;&gt;GitHub Repository&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Bridge the Gap between IaC and FluxCD&lt;/h2&gt;
&lt;p&gt;Let&apos;s explain a bit about the problem. Cloud resources are created with the IaC tool, and they need to be referenced in Kubernetes (e.g., Bucket name, Certificate ARN, Subnet IDs, SQS Queue ARN…). But how can these values be passed to the Kubernetes resources (e.g., Deployments, Helm Chart Values…)?&lt;/p&gt;
&lt;p&gt;The &quot;classic&quot; solution for this is to store these in AWS Secrets Manager or Parameter Store and use External Secrets to create Kubernetes Secrets that will be consumed by your application as environment variables. The reality is that this pattern does not work in all cases, since it&apos;s very common the need to pass the value directly to a Helm Chart custom values, to manifests that do not support reading the value from a secret (e.g. ALB Controller needs VPC ID that is passed as &lt;code class=&quot;language-text&quot;&gt;args&lt;/code&gt; to the controller pod) or even in annotations/labels of resources.&lt;/p&gt;
&lt;p&gt;The solution proposed here allows us to perform substitution on placeholder values using our GitOps Tool (in this case, FluxCD), and it works by:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Creating a ConfigMap that will substitute Kustomize values using the &lt;a href=&quot;https://fluxcd.io/flux/components/kustomize/kustomizations/#post-build-variable-substitution&quot;&gt;postBuild&lt;/a&gt; feature of FluxCD.&lt;/li&gt;
&lt;li&gt;Create a specific ConfigMap per service that will be applied as &lt;a href=&quot;https://fluxcd.io/flux/components/helm/helmreleases/#values-references&quot;&gt;custom values&lt;/a&gt; for the Helm Chart.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This way, we are able to bridge the gap between IaC and GitOps by replacing the values in Kubernetes YAML manifests (created with Kustomize) and custom values for Helm Charts.&lt;/p&gt;
&lt;p&gt;I bet you are still very confused with the summary of the explanation above, so let&apos;s deep dive with real examples and explain this step by step.&lt;/p&gt;
&lt;h2&gt;Scenario&lt;/h2&gt;
&lt;p&gt;Our infrastructure goal is to have a new Kubernetes cluster deployed from scratch and bootstrap it with some good-to-have addons:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;ALB Controller:&lt;/strong&gt; To provision and manage Network Load Balance to expose Kubernetes apps.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Authentik&lt;/strong&gt;: A complete IdP platform to set up SSO. I&apos;ve already talked about SSO and Authentik in the &lt;a href=&quot;https://www.felipetrindade.com/sso/&quot;&gt;Hands-on SSO with Terraform and Authentik&lt;/a&gt; blog post!&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;External-DNS&lt;/strong&gt;: To automatically create records in Route53 to expose our apps (domain level).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;External-Secrets&lt;/strong&gt;: To create Kubernetes secrets based on secrets in AWS Secrets Manager.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Headlamp&lt;/strong&gt;: A dashboard for monitoring our cluster.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Istio&lt;/strong&gt;: Implements the Gateway API controller.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Karpenter&lt;/strong&gt;: Handles cluster node scalability.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Metrics-Server&lt;/strong&gt;: Collects metrics of the pods and allows us to scale them based on these metrics (e.g. CPU and Memory).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Reloader&lt;/strong&gt;: A controller to watch changes in ConfigMap and Secrets and do rolling updates on pods.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Deploying the Infrastructure&lt;/h2&gt;
&lt;p&gt;The infrastructure needs to be deployed in a two-phase process when using Terraform. This is a limitation of the Kubernetes provider for Terraform, since &lt;a href=&quot;https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs#stacking-with-managed-kubernetes-cluster-resources&quot;&gt;it&apos;s required to have an EKS cluster provisioned before using the provider&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Based on that, first we need to deploy the &lt;strong&gt;core&lt;/strong&gt; infrastructure (usually network related and EKS cluster) before moving to the Kubernetes cluster bootstrap.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/678af66bb774a710484bf900347a04a8/a8417/Infrastructure-Deployment.drawio.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 20.253164556962027%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAECAYAAACOXx+WAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA2UlEQVR42nWN3W6CQBBGef/XalOqF7UqUMAUdqsJrLQgiOyuCKeF/tz1Syb5cjJzxtHpKzqOyE8Zz8kKcRTUusZ/8xGFoPnqq/iJTbKmtWfSIiHY+zSm5lDu2amY97bAxDFGpDjnKKTZbpBK4AYu0SEkrzIeX1x86aFOOffrO1zvgbL9wBNbluFi3gmkzzJeII+S1vO47CIcfmKtpSoruks396u9zqO15tbfZlYUBbrT9H0/84lZYzHG/Gq+hcMwUFUVUkqUUvPBlHEc/55NMiEEWZbRdR3/5RMwMSznZ4MkuwAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Infrastructure Installation Flow&quot;
        title=&quot;&quot;
        src=&quot;/static/678af66bb774a710484bf900347a04a8/f058b/Infrastructure-Deployment.drawio.png&quot;
        srcset=&quot;/static/678af66bb774a710484bf900347a04a8/c26ae/Infrastructure-Deployment.drawio.png 158w,
/static/678af66bb774a710484bf900347a04a8/6bdcf/Infrastructure-Deployment.drawio.png 315w,
/static/678af66bb774a710484bf900347a04a8/f058b/Infrastructure-Deployment.drawio.png 630w,
/static/678af66bb774a710484bf900347a04a8/a8417/Infrastructure-Deployment.drawio.png 941w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;You might be surprised that, based on the flow above, the creation of Kubernetes Namespaces is being managed by Terraform, but we will get there. For now, you just need to understand that the Terraform deployment should be performed in a two-phase approach:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Deploy only core infrastructure (this can be easily achieved by using the &lt;code class=&quot;language-text&quot;&gt;--target&lt;/code&gt; flag in &lt;code class=&quot;language-text&quot;&gt;terraform apply&lt;/code&gt; CLI)&lt;/li&gt;
&lt;li&gt;Deploy the rest of the infrastructure&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;FluxCD Bootstrap&lt;/h2&gt;
&lt;p&gt;FluxCD Documentation suggests several ways for &lt;a href=&quot;https://v2-7.docs.fluxcd.io/flux/installation/&quot;&gt;bootstrapping&lt;/a&gt; FluxCD. I personally like the Terraform approach, and I&apos;ll stick with it. The &lt;a href=&quot;https://registry.terraform.io/providers/fluxcd/flux/latest/docs/resources/bootstrap_git&quot;&gt;flux_bootstrap_git&lt;/a&gt; resource of the FluxCD provider is responsible for installing FluxCD in the Kubernetes cluster and push the manifests related to FluxCD installation to your git repository, which is aligned with a GitOps approach.&lt;/p&gt;
&lt;p&gt;Our GitOps Controller (FluxCD) will be responsible for installing all the tools in the Kubernetes cluster using a GitOps approach. Based on our scenario, FluxCD should install the resources in the following order:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/83202528b423a6328aa963f31ce6f74e/a8417/FluxCD-Install-Flow.drawio.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 10.759493670886075%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAACCAYAAABYBvyLAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAgUlEQVR42h3Myw6DIABEUf//76oiJgIGsUl9gVrQdHVLXEwyi5tTJNURG8ESZypbMgbHfofnm9lw3DtybGjfku/vRE8K4eqnGTZLmbs1LlxSchlFkbQiSsF8TtRDxegd/rtR9i/MpAnRI2yVV3NcO/rT0ThBSJ5htQ++ZDC1Gew1f205krn/vjE3AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;FluxCD Installation Flow&quot;
        title=&quot;&quot;
        src=&quot;/static/83202528b423a6328aa963f31ce6f74e/f058b/FluxCD-Install-Flow.drawio.png&quot;
        srcset=&quot;/static/83202528b423a6328aa963f31ce6f74e/c26ae/FluxCD-Install-Flow.drawio.png 158w,
/static/83202528b423a6328aa963f31ce6f74e/6bdcf/FluxCD-Install-Flow.drawio.png 315w,
/static/83202528b423a6328aa963f31ce6f74e/f058b/FluxCD-Install-Flow.drawio.png 630w,
/static/83202528b423a6328aa963f31ce6f74e/a8417/FluxCD-Install-Flow.drawio.png 941w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Some important comments:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I think it&apos;s a best practice to manage CRDs separately, since this avoids some racing conditions (e.g. application tries to use &lt;code class=&quot;language-text&quot;&gt;ExternalSecret&lt;/code&gt; CR but the CRDs were not installed yet).&lt;/li&gt;
&lt;li&gt;Karpenter should be the first addon to be installed in a Kubernetes cluster, since it will guarantee cluster scalability. If you try to deploy Karpenter and several others addons at the same time, it is not guaranteed that the initial nodes of the cluster will have enough memory to install all addons and Karpenter, so it&apos;s better to be safe and make sure Karpenter is installed first and can guarantee the cluster scalability.&lt;/li&gt;
&lt;li&gt;Based on my experience, if you try to deploy ALB Controller and several other services at the same time, the deployments might be stuck because of the following error:&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Internal error occurred: failed calling webhook &quot;mservice.elbv2.k8s.aws&quot;:
failed to call webhook:
Post &quot;https://aws-load-balancer-webhook-service.kube-system.svc:443/mutate-v1-service?timeout=10s&quot;:
no endpoints available for service &quot;aws-load-balancer-webhook-service&quot;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;When installing it first, I saw no errors.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I&apos;m considering &lt;code class=&quot;language-text&quot;&gt;addons&lt;/code&gt; all services that are &quot;internal&quot; to Kubernetes, i.e. tools that handle monitoring, scalability, automation... and &lt;code class=&quot;language-text&quot;&gt;apps&lt;/code&gt; as real apps that will be used by your clients (e.g. APIs, Webservers...).&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Addons/Apps installation&lt;/h2&gt;
&lt;p&gt;I have a preference for installing external tools (i.e. tools that I don&apos;t manage, such as open-source projects) using Helm Chart instead of managing each Kubernetes YAML Manifest individually (Deployment, PDB, Service, ConfigMap...). Because of that, I&apos;ll use the &lt;a href=&quot;https://fluxcd.io/flux/components/helm/helmreleases/&quot;&gt;HelmRelease&lt;/a&gt; Flux&apos;s Custom Resource to install these resources. But, there is always a need to manage some pure YAML manifests (e.g. &lt;code class=&quot;language-text&quot;&gt;ExternalSecret&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;HttpRoute&lt;/code&gt; - some Helm Charts only support Ingress...), for these, I will use the &lt;a href=&quot;https://fluxcd.io/flux/components/kustomize/kustomizations/&quot;&gt;Kustomization&lt;/a&gt; Flux&apos;s Custom Resource. Also, some of these addons need to install CRDs and the best practice is to install and manage these separately. The flow chart below explains the order of deployments I&apos;d like FluxCD to run when creating a new cluster from scratch.&lt;/p&gt;
&lt;p&gt;The following flow summarizes the main idea of how to Bridge the Gap between IaC and GitOps:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/b181b8e838a3eeab0dbb1839de7d0fa3/a8417/GitOps-Installation-of-Kubernetes-Resources.drawio.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 46.835443037974684%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAYAAAAywQxIAAAACXBIWXMAAAsTAAALEwEAmpwYAAABk0lEQVR42pVSXU/bMBTN//8nm+BlmpAQ42EbEDrY+gkthZKmJW0aWpLabWzHsQ+3LkQtD0g7khXf+Prc43uuB2NgaW1gjIW1Fruw7v/2vB22cHh6gIubMwiVQypVnbl7tDz8BxZsjsfxPWYvMRWy0EWBsiz3cjyVJBCTyFWazjkyLp3iwiVr8LXEeMagCo1SG0QJJ2Wly5k8c7ywHMYaSOIo5gk81mogu6oRgcYPv4ubQeyIsuUSUgo8hDG+ndYp5kgWKQ6P/2Awiilf4aI1Qi94JqUKac3HqtOmJ5MSIwRdlkgzBsY5hMj3erjK5VtgkdP+vc1CKidkA8kZNPFUPdRa47LxgH4wrchKo9EZtnHe/IVg9ogwGaJ266Mf3SHOJvhd/4mrXg15sXbEJbWhIty41OxFCKPFHmF3fIvmoOEIo/QJf++vEc6HiNkUlx0f9f4/rASnZ2vH4Vls9bN8ibugi1E8hNIbY2w1ErtFFY3KZ/De5y5IAnw9+YIj/zvSdUqOluSscm4rVbx9t/FugY9z+woAF7HpPBpBogAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;GitOps Installation of Kubernetes Resources&quot;
        title=&quot;&quot;
        src=&quot;/static/b181b8e838a3eeab0dbb1839de7d0fa3/f058b/GitOps-Installation-of-Kubernetes-Resources.drawio.png&quot;
        srcset=&quot;/static/b181b8e838a3eeab0dbb1839de7d0fa3/c26ae/GitOps-Installation-of-Kubernetes-Resources.drawio.png 158w,
/static/b181b8e838a3eeab0dbb1839de7d0fa3/6bdcf/GitOps-Installation-of-Kubernetes-Resources.drawio.png 315w,
/static/b181b8e838a3eeab0dbb1839de7d0fa3/f058b/GitOps-Installation-of-Kubernetes-Resources.drawio.png 630w,
/static/b181b8e838a3eeab0dbb1839de7d0fa3/a8417/GitOps-Installation-of-Kubernetes-Resources.drawio.png 941w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Notice that we can create the namespace with FluxCD, but there is a race condition problem. From the moment you FluxCD bootstrap fisishes, Terraform will try to create a ConfigMap in a namespace that does not exist yet (it takes time for FluxCD to deploy stuff). So if we move the namespace creation to Terraform we won&apos;t have this problem! Sure, we still add some hacky stuff (e.g. null_resource with &quot;sleep&quot;) or retry the Terraform execution after FluxCD has correctly provisioned the namespace, but I personally don&apos;t like this idea much.&lt;/p&gt;
&lt;p&gt;Let&apos;s take real examples to explain this.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/65465b9a4b7e9af245eaad76017536b2/d44c9/Install-Examples.drawio.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 41.77215189873418%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAYAAAD5nd/tAAAACXBIWXMAAAsTAAALEwEAmpwYAAABc0lEQVR42m1R2W7CQAzk//+stFCiIAo8kARyNQnJ3puQoqm9oFaq+rCSd2yPx+PFMAw4Ho/Y7/coyxJpmmK1XuNluQxYFEV4W63wSi+OY2RZhvfNBvF2izzPYYzB4XBAURRwzmHBQN/3qOsaWms0bYv8dMIp2qAijF/2sUOy26GkeL7fUZCA7nyGnyYopUIvc/wQdl0XJmhK+nHETUrYNMFtnkOTr0r4uoKjeLzdYOlvywLGeRgSUVUVhBCw1mKhn0CSJAHU2lCRQt9WoUBRXvUtxLUhNToMkEUOxQIoz6rYBrYuKPxLaIyFkQMGIvR+DKqllVBOQUn1WPNygSZSTQTcw4QtWfWvQkOgHXqIMofznghJoZGPFywhwuYTsqlDrSBlFxrQNM0vIbOzh9zAnirVQ8rrU6EmhQLSiMfK5Jt0MqjmnKehLIh7w8oM8P4M8oSJD0HT/POiI5Ha0dCqLsTz/AXriWi0GOmAE1nAV2ZCjr8BZG9cAnobKgsAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Install Examples&quot;
        title=&quot;&quot;
        src=&quot;/static/65465b9a4b7e9af245eaad76017536b2/f058b/Install-Examples.drawio.png&quot;
        srcset=&quot;/static/65465b9a4b7e9af245eaad76017536b2/c26ae/Install-Examples.drawio.png 158w,
/static/65465b9a4b7e9af245eaad76017536b2/6bdcf/Install-Examples.drawio.png 315w,
/static/65465b9a4b7e9af245eaad76017536b2/f058b/Install-Examples.drawio.png 630w,
/static/65465b9a4b7e9af245eaad76017536b2/d44c9/Install-Examples.drawio.png 722w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;If you REALLY want to understand how this was done, take your time to check these 6 examples:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/felipelaptrin/kubernetes-gitops-flux/tree/main/k8s/addons/base/storage-class&quot;&gt;Storage Class&lt;/a&gt;: Single manifest.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/felipelaptrin/kubernetes-gitops-flux/tree/main/k8s/addons/base/reloader&quot;&gt;Reloader&lt;/a&gt;: Installation via &lt;code class=&quot;language-text&quot;&gt;HelmRelease&lt;/code&gt; without custom values.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/felipelaptrin/kubernetes-gitops-flux/tree/main/k8s/addons/base/karpenter&quot;&gt;Karpenter&lt;/a&gt;: Installation via &lt;code class=&quot;language-text&quot;&gt;HelmRelease&lt;/code&gt;. It needs non-sensitive custom values &lt;a href=&quot;https://github.com/felipelaptrin/kubernetes-gitops-flux/blob/main/k8s/addons/base/karpenter/values.yaml&quot;&gt;non-related to infra&lt;/a&gt; and &lt;a href=&quot;https://github.com/felipelaptrin/kubernetes-gitops-flux/blob/main/k8s/addons/base/karpenter/ec2-node-class.yaml#L11&quot;&gt;related to infra&lt;/a&gt; (that are created by &lt;a href=&quot;https://github.com/felipelaptrin/kubernetes-gitops-flux/blob/main/src/k8s.tf#L159&quot;&gt;Terraform General ConfigMap&lt;/a&gt; and &lt;a href=&quot;https://github.com/felipelaptrin/kubernetes-gitops-flux/blob/main/src/k8s.tf#L381&quot;&gt;Terraform specific ConfigMap&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/felipelaptrin/kubernetes-gitops-flux/tree/main/k8s/addons/base/metrics-server&quot;&gt;Metrics Server&lt;/a&gt;: Installation via &lt;code class=&quot;language-text&quot;&gt;HelmRelease&lt;/code&gt;. It needs non-sensitive custom values &lt;a href=&quot;https://github.com/felipelaptrin/kubernetes-gitops-flux/blob/main/k8s/addons/base/metrics-server/values.yaml&quot;&gt;non-related to infra&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/felipelaptrin/kubernetes-gitops-flux/tree/main/k8s/addons/base/authentik&quot;&gt;Authentik&lt;/a&gt;: Installation via &lt;code class=&quot;language-text&quot;&gt;HelmRelease&lt;/code&gt;. It needs non-sensitive custom values &lt;a href=&quot;https://github.com/felipelaptrin/kubernetes-gitops-flux/blob/main/k8s/addons/base/authentik/values.yaml&quot;&gt;non-related to infra&lt;/a&gt; and &lt;a href=&quot;https://github.com/felipelaptrin/kubernetes-gitops-flux/blob/main/src/k8s.tf#L447&quot;&gt;related to infra&lt;/a&gt;. It also needs &lt;a href=&quot;https://github.com/felipelaptrin/kubernetes-gitops-flux/blob/main/k8s/addons/base/authentik/external-secrets.yaml&quot;&gt;sensitive data&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/felipelaptrin/kubernetes-gitops-flux/tree/main/k8s/addons/base/headlamp&quot;&gt;Headlamp&lt;/a&gt;: Installation via &lt;code class=&quot;language-text&quot;&gt;HelmRelease&lt;/code&gt;. It needs non-sensitive custom values &lt;a href=&quot;https://github.com/felipelaptrin/kubernetes-gitops-flux/blob/main/k8s/addons/base/headlamp/values.yaml&quot;&gt;non-related to infra&lt;/a&gt; and &lt;a href=&quot;https://github.com/felipelaptrin/kubernetes-gitops-flux/blob/main/k8s/addons/base/headlamp/external-secrets.yaml&quot;&gt;sensitive data&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Cya&lt;/h2&gt;
&lt;p&gt;Hope you enjoyed this blog post! Happy New Year, and see you in the next post! 👋&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Ephemeral Environment for serverless backend]]></title><description><![CDATA[Today, I will cover a strategy for ephemeral environments for backend applications hosted in AWS Lambda behind an API Gateway. So today's…]]></description><link>https://felipetrindade.com/ephemeral-environment-backend-serverless/</link><guid isPermaLink="false">https://felipetrindade.com/ephemeral-environment-backend-serverless/</guid><pubDate>Tue, 09 Dec 2025 17:20:32 GMT</pubDate><content:encoded>&lt;p&gt;Today, I will cover a strategy for ephemeral environments for backend applications hosted in AWS Lambda behind an API Gateway. So today&apos;s goal is to deploy a short-lived environment that exists during the pull request lifecycle, helping the developers and testers make sure the code change implements what is expected, without having to merge to the dev/qa environment to test the changes on a lower environment.&lt;/p&gt;
&lt;p&gt;For this demo, I will use AWS CDK (with Typescript) and GitHub Actions.&lt;/p&gt;
&lt;p&gt;All the code used for this demo is available in my &lt;a href=&quot;https://github.com/felipelaptrin/ephemeral-environment-backend-serverless&quot;&gt;GitHub Repository&lt;/a&gt;!&lt;/p&gt;
&lt;h2&gt;Architecture&lt;/h2&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 527px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/787543f3a23d053c4fa508161dc67c82/44385/Architecture.drawio.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 109.49367088607596%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAWCAYAAADAQbwGAAAACXBIWXMAAAsTAAALEwEAmpwYAAADRElEQVR42p1Uy3LURhT1J2TJN/AH2WSTDR/ALj+Q4guySOADWPCoLNixgEoWqQpQwBQLcBHKsIAyGKfsAXtsjWdkaaTRY/QadUutPtzbmhkG20MVXFXXVavV5z7PXcNJabRR2euPCP7ZgHQjSDuAdMJ20XvlTdAIiab8cqlCYO0UoG4Bp7tDAt1b7LWs0dAFvtSICmdJMz0LcOmQRe4dIr57D4L0ssGz78jVgCqbGp3+/QDW+Z+R/HV/dtB8H6CmnJR1DWdzG3u37sJ5s2X2p39s2sWA5QlATeHoWUh8WFHik7LEh8hHlOeoZIWvycJDIYRZrcGGolKLHOpBDO/OBup+CAUNUQpMp1NUVYWaljjYQLX7GFpkVCzVAqZpijiOkZMX/X4fm5ubOLZtpJDw/1xHeO4KwpvrCESKkePCcRwkdCdPMwRXf8Tktx+gRh9BfmAtyzLzw2AwMMC8PM9DkZBFNraxB+vSHTidt6joC4ddkGETIkWTPr+JycPfIROPWktjjQFGoxHG4zGSJPmcD25WpeBHEfaHR/BIz1smnwFOkhTb3QN0D4Y4dj3qsfp0lU1h6GFAzmf/sI+d7R1Yh5bxSBGTGNAUj1ZTk99Kmr2p8ryyyxWee6hnNCyKDEq1LWP7GcYRR6K/sbELaS7ZXY3125XRhjmUQ863lLKl6H83UHQuo8l8NJzDVYB13gJudYDrF4H3T9rvYRBh6907BGHYDpFrPyH54xyUtw9NQaz2cNp6MOzlePnIh2MVxkAcxbCppUICLIoC1st/0Xt6Gxk1v5734anCzIcDvURJBDe0EZOeCwNxvhW3TVESi0oqsPo8bU4WxCSYRlQ7riRk2mpOuqJLSRCjJiCIGjLLIajBuWXUqvFV1fprU6r1EK0TY9/HcHBElCzNmQFktkwmCbWGMlXsDQPDVW7s4bGLzrMXcDyfM4t+UOK9TZ7VxJqqxrTI4fgRgmQGyFaYx0EQmAHhui52d3cMcxoi56s3b3Hhl1/xf5entyLGZOh80Oh6DQ0HgZI8ExQuR2UAa5pxlmWh1+uhXpp3zAoWNvbs+QsTAUsw9tE7ctEfulBnzMeVRZnvGdA5thc8FzQj2bNKioXRZcBPIbmZymABArEAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Architecture&quot;
        title=&quot;&quot;
        src=&quot;/static/787543f3a23d053c4fa508161dc67c82/44385/Architecture.drawio.png&quot;
        srcset=&quot;/static/787543f3a23d053c4fa508161dc67c82/c26ae/Architecture.drawio.png 158w,
/static/787543f3a23d053c4fa508161dc67c82/6bdcf/Architecture.drawio.png 315w,
/static/787543f3a23d053c4fa508161dc67c82/44385/Architecture.drawio.png 527w&quot;
        sizes=&quot;(max-width: 527px) 100vw, 527px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;The architecture is fairly simple: an API Gateway with a custom domain (e.g., &lt;code class=&quot;language-text&quot;&gt;api.mydomain.com&lt;/code&gt;) that points to a Lambda Function (using proxy integration) that serves as an API.&lt;/p&gt;
&lt;p&gt;The proposed solution in this blog post has some considerations:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The Lambda uses &lt;a href=&quot;https://github.com/awslabs/aws-lambda-web-adapter&quot;&gt;AWS Lambda Web Adapter&lt;/a&gt; in order to run APIs on AWS Lambda (e.g., fastapi, flask, next, express...).&lt;/li&gt;
&lt;li&gt;Although I&apos;ll be using FastAPI (Python), you can use any other API framework that is compatible with the Lambda Web Adapter mentioned.&lt;/li&gt;
&lt;li&gt;The proposed solution is agnostic in terms of the IaC tool. I&apos;ll be using CDK, but the same result could be achieved with Terraform, Pulumi, or even AWS CLI. Here, what matters is the proposed idea.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So, based on the architecture, you should expect to have an endpoint deployed that your web page or other service would call (e.g., HTTP GET &lt;code class=&quot;language-text&quot;&gt;api.mydomain.com/users/123&lt;/code&gt;). The API Gateway has a root &lt;code class=&quot;language-text&quot;&gt;/&lt;/code&gt; resource and a &lt;code class=&quot;language-text&quot;&gt;/{proxy+}&lt;/code&gt; that will be used to &lt;a href=&quot;https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-set-up-simple-proxy.html&quot;&gt;proxy&lt;/a&gt; all routes to the Lambda, and the Lambda will handle the request. The API Gateway will target a Lambda Alias that points to the environment (i.e., &lt;code class=&quot;language-text&quot;&gt;dev&lt;/code&gt;).&lt;/p&gt;
&lt;h2&gt;Flow of the Ephemeral Environment&lt;/h2&gt;
&lt;p&gt;The ephemeral environment deployed will have the same lifespan as the pull request, so we are aiming for the following flow:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Developer opens a Pull Request with code changes&lt;/li&gt;
&lt;li&gt;GitHub Actions runs a workflow that builds and deploys the image to the ECR repository. It creates a lambda alias (e.g., &lt;code class=&quot;language-text&quot;&gt;pr-4&lt;/code&gt;), updates the &lt;code class=&quot;language-text&quot;&gt;$LATEST&lt;/code&gt; version of the lambda to the image that was just pushed, adds the &lt;a href=&quot;https://github.com/awslabs/aws-lambda-web-adapter&quot;&gt;REMOVE_BASE_PATH&lt;/a&gt; (more on that on step 3) environment variable, and creates a new Lambda version pointing to the lambda alias created. Then, it creates a new API Gateway with Lambda alias proxy integration (e.g., &lt;code class=&quot;language-text&quot;&gt;pr-4&lt;/code&gt;).&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The biggest reason to not use $LATEST version of the Lambda directly is that new versions are created from the $LATEST version, so it doesn&apos;t make sense to modify the $LATEST version (that would point to dev) when deploying to the ephemeral environment.&lt;/p&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;A comment is added to the pull request with the link to access the ephemeral environment.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/a49bbd206f05fe20f49db4af4318103d/4c42d/pull-request.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 71.51898734177216%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAOCAYAAAAvxDzwAAAACXBIWXMAAAsTAAALEwEAmpwYAAACVklEQVR42nWTyXbbMAxFtU1jzRI1UaMtS7I1WLacOGkGn5NVl931/3/kFaTstGnSxT0ASQkgHwBlPD/j5ecP3L++4fT0hunh9cIZebmFzhIihsZSaP4SmptAtXwsTO9LFP5So/w1wUkysKiAHaSwvERi+wnMsITpFzCjCkY8yLVmB7j9FGxOoug2R8hLxHlFQTg0y4MTUEAW0U08eFEKx+cwbPLDBD7PaR1jYbgfuDUcCsigjM0Kab5BMZwRNyewYg9veYC/muDmI3TeXuhgpYPETPqZuIdB+05+AFseYYQ1lCjkcMMCZX/CcntEsh6JHfJmQlrv4ectgqIDSzdQ3Qwqy9/RLhj+CmZQ0nkK5UZzoZIm1XaH7e6ITX9A0+3RjnfYDBPqdkRHft2NJMuaCrVBUlTgWSnlWeh/P5k0vDUYNIfD8jN80xkdeBIpsvDJClQroO+iGTuULGjvHVmUgAJSBt3lCKKMipFRlWcrflyI9rhi/h9xAYOJS9GTLeF4MQIu2iaHKwgz+kDcJoDuhLJNrqj/+IKF7cOkDhFtpvS7A1ZNj2q4x7o7gBcNwow0WjZI161cW6IfCYMS25TMoaRfIb5RhE4m9djQ7TA9nrE7fpccTi/op0eMd08YyIqz++c3JJToRrWgmlQEUQj9YgkxQYp4v05PW1Utmp6qPBypsnustyMqsnU/0f5Mt3+Q42jxGlbcEpuLJZKWxjKeq6yT42eNnBShg9BUcPUdGkfzorWYIoNGUafe02m2/7CShZwDkvDiB+F/NfAf9mVLsU+jJxBP/g2aTXG7qfSdNAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Screenshot of the Pull Request created by the developer with link to ephemeral environment&quot;
        title=&quot;&quot;
        src=&quot;/static/a49bbd206f05fe20f49db4af4318103d/f058b/pull-request.png&quot;
        srcset=&quot;/static/a49bbd206f05fe20f49db4af4318103d/c26ae/pull-request.png 158w,
/static/a49bbd206f05fe20f49db4af4318103d/6bdcf/pull-request.png 315w,
/static/a49bbd206f05fe20f49db4af4318103d/f058b/pull-request.png 630w,
/static/a49bbd206f05fe20f49db4af4318103d/4c42d/pull-request.png 896w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;It is important to mention that the ephemeral environment here is using a &lt;code class=&quot;language-text&quot;&gt;path-based&lt;/code&gt; model, instead of a &lt;code class=&quot;language-text&quot;&gt;subdomain-based&lt;/code&gt; model (e.g., &lt;code class=&quot;language-text&quot;&gt;pr-4.api.mydomain.com&lt;/code&gt;). This is because during Pull Request, we are creating a new API Gateway proxy resource with &lt;code class=&quot;language-text&quot;&gt;pr-4&lt;/code&gt; as the name. So every request that has &lt;code class=&quot;language-text&quot;&gt;pr-4&lt;/code&gt; in the path will be forwarded to the ephemeral environment lambda alias. This is the main reason for us to use the &lt;code class=&quot;language-text&quot;&gt;REMOVE_BASE_PATH&lt;/code&gt;: the API calls to the ephemeral environment will be routed by the newly created API Gateway resource (i.e., &lt;code class=&quot;language-text&quot;&gt;pr-4&lt;/code&gt; in the example). So we need to remove this base path before sending the payload to the API code, otherwise, we would expect a &lt;code class=&quot;language-text&quot;&gt;not found&lt;/code&gt; response.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/7fd22779ad1a9fec9044e84ab5f67cef/8698d/api-gateway.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 46.202531645569614%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAYAAAAywQxIAAAACXBIWXMAAAsTAAALEwEAmpwYAAABj0lEQVR42o1S2Y7UMBDM/z/yI/zIvgOCFQs7m8lMLt+3U5SdnUW80VLHltVdVV3p4evoMK0GJhQ4HyGExO02Y1s3pJRQa+15HAcTvB8opaLwLeYK7XnGiDB/gVueMWgpsCwzcs69SSmF17cbpkUh5bM5Z5JZC6MNEpsPgvLTRbzuFbtU0E+f4L9/xiCVJuDWlVk2WeuwuQOjrJBkbxFjxrffC35cmFeDi6iYdcFVJEwyU20v6+qHxiolwUI+lTgHoQJcCHhETBm7sigUZlOFjQeECXi53Dhy6VYQDrUUDOuuMN2XD6+8d9Au4oVjFxa0aESakwSSNFvQ/aQdtCnTZ2FLV+xixTDvBsqEd8lkp8LZJPyaJTRNN/TRp0JbVlzGK3zw9FlDac17xP0+Y5EBPyeFjVMM+75jZXFT01i7jxxpvBHQeRCvj2x5F6p5bLByA9rPM8Z074P3kPsGTZJhnheM1+l9LU7AZkmKpSt+jNxWqG1CIngjP9/yP2fbiKH9ECHE35Eb4OnyRzyK/yf/AOr1ulHvK0IgAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Screenshot of the Pull Request&quot;
        title=&quot;&quot;
        src=&quot;/static/7fd22779ad1a9fec9044e84ab5f67cef/f058b/api-gateway.png&quot;
        srcset=&quot;/static/7fd22779ad1a9fec9044e84ab5f67cef/c26ae/api-gateway.png 158w,
/static/7fd22779ad1a9fec9044e84ab5f67cef/6bdcf/api-gateway.png 315w,
/static/7fd22779ad1a9fec9044e84ab5f67cef/f058b/api-gateway.png 630w,
/static/7fd22779ad1a9fec9044e84ab5f67cef/40601/api-gateway.png 945w,
/static/7fd22779ad1a9fec9044e84ab5f67cef/8698d/api-gateway.png 1239w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;ol start=&quot;4&quot;&gt;
&lt;li&gt;Developer/QA/Tester checks the ephemeral environment and verifies the changes&lt;/li&gt;
&lt;li&gt;Pull Request gets approved and merged&lt;/li&gt;
&lt;li&gt;GitHub Action runs a workflow to delete the ephemeral environment (i.e., deletes Lambda alias, version, and API Gateway resource) and the comment in the Pull Request with the URL.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This is a simple flow that is very powerful and can be extended to meet different needs.&lt;/p&gt;
&lt;p&gt;Suppose the following &lt;a href=&quot;https://github.com/felipelaptrin/ephemeral-environment-backend-serverless/pull/4/files&quot;&gt;Pull Request&lt;/a&gt; that creates a new endpoint &lt;code class=&quot;language-text&quot;&gt;/random&lt;/code&gt; that generates a pseudo-random number. During the Pull Request, the endpoint will be available in the ephemeral environment:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/83ec34946c68b16d935f9ca9fd0d5dff/eb390/ephemeral-env-new-route.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 36.708860759493675%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAYAAAAIy204AAAACXBIWXMAAAsTAAALEwEAmpwYAAABJUlEQVR42qXOS0sCURjG8fkC0VxCGZyIwBY5FxsvU8k4qaldUKJEKYi0G5aLFDEoI1r5AdpEi/qs/2amIAhXtvid85x38bxHUCsPRPafiDTeUVsfLJ58snz8xlLzFXVnjFoe+e6JlEYsFIZI3oB5tx8S833krQFa4c6fj5grPSN4nQnF9gSn9Ui2OSZ/+kK2fkuufoV7eIPX6OEd9cgddHFq19i7FxiVDqYvWT0nvXfJdu3Mn3fRa0OEasmjXHDxcg5hLro4GZukqWOZCSzjW/Bes3TspEHqR5Bty8C0TD/rpKxVhIRusL6xievmicdXSGcyyLKCKEpIkvyHhDjF71xGiMViBDRNIxqNhreiKH6pPBMhOERRDAU52DZrWVj4n99MK/wCCQjOMIr8iVoAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Screenshot of the Ephemeral Environment Random endpoint&quot;
        title=&quot;&quot;
        src=&quot;/static/83ec34946c68b16d935f9ca9fd0d5dff/f058b/ephemeral-env-new-route.png&quot;
        srcset=&quot;/static/83ec34946c68b16d935f9ca9fd0d5dff/c26ae/ephemeral-env-new-route.png 158w,
/static/83ec34946c68b16d935f9ca9fd0d5dff/6bdcf/ephemeral-env-new-route.png 315w,
/static/83ec34946c68b16d935f9ca9fd0d5dff/f058b/ephemeral-env-new-route.png 630w,
/static/83ec34946c68b16d935f9ca9fd0d5dff/eb390/ephemeral-env-new-route.png 935w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;But if you try to access the dev environment, the endpoint will not be there:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/38913364bb592a3cb4128c5e81c227ee/eb390/dev-env-new-route.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 36.708860759493675%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAYAAAAIy204AAAACXBIWXMAAAsTAAALEwEAmpwYAAABOElEQVR42pXNXUvCcBTH8b2BcE6ZDo3yImzqZhtOyodNnE+JSFhGQYQYURn0hEEZ0VUvoJvool7rN7eIvAiyi8+f3zl/zjmCUr9Dbj8gb7+h7L6zuP9BYu+Vpf4LSnOCUhtP3RJxx0jONYHSpU+ckuwrX7xyQdAes1B9RLAHz1Sm8v17rP6E8sETVndEsXuM3Rvh7JxT7p1R3DrFbB+h1Qc+vTHAbA0xN4e4nUOM1gnpzg1Co1qmVilSKa3TdB1cp4iZzZBJraImV0irSfRMiqyWxtAz/t83r/ZouoaRTWPqKoKm62wUCqwZJsuJBJaVJ5ezUNUUjuNgmCZRRUEMiASDQcRf/PQlhFgsRjweR5kOybKMV3s5Eon4ORqNEg6HkSRpLoL3iOLX9VlefzbPvTAUCv1r4K+Fn81Tz9UY42uPAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Screenshot of the Dev Environment Random endpoint&quot;
        title=&quot;&quot;
        src=&quot;/static/38913364bb592a3cb4128c5e81c227ee/f058b/dev-env-new-route.png&quot;
        srcset=&quot;/static/38913364bb592a3cb4128c5e81c227ee/c26ae/dev-env-new-route.png 158w,
/static/38913364bb592a3cb4128c5e81c227ee/6bdcf/dev-env-new-route.png 315w,
/static/38913364bb592a3cb4128c5e81c227ee/f058b/dev-env-new-route.png 630w,
/static/38913364bb592a3cb4128c5e81c227ee/eb390/dev-env-new-route.png 935w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;⚠️ Since I was using &lt;a href=&quot;https://fastapi.tiangolo.com/&quot;&gt;fastapi&lt;/a&gt; I could have taken a screenshot of the &lt;code class=&quot;language-text&quot;&gt;/docs&lt;/code&gt; page (Swagger UI) to show that the new endpoint exists on the ephemeral environment, right? Well... the issue is that the Swagger page makes a request to its own API to fetch the &lt;code class=&quot;language-text&quot;&gt;openapi.json&lt;/code&gt; file, and this request does not have the path prefix (i.e. &lt;code class=&quot;language-text&quot;&gt;pr-4&lt;/code&gt;), which uses the &lt;code class=&quot;language-text&quot;&gt;dev&lt;/code&gt; Lambda alias. In other words, the ephemeral environment won’t work for this specific use case. Maybe there’s some custom logic you could implement to fix this, but I couldn’t find a straightforward workaround.&lt;/p&gt;
&lt;h2&gt;Code&lt;/h2&gt;
&lt;p&gt;I don&apos;t think it will be beneficial to copy and paste code from the demo &lt;a href=&quot;https://github.com/felipelaptrin/ephemeral-environment-backend-serverless&quot;&gt;GitHub Repository&lt;/a&gt;. So I recommend that you go over the code there and deploy it yourself!&lt;/p&gt;
&lt;h2&gt;Cya!&lt;/h2&gt;
&lt;p&gt;Hope you liked this blog post!&lt;/p&gt;
&lt;p&gt;See you around! 👋&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Using ALB and Cognito to add authentication to your apps]]></title><description><![CDATA[As a DevOps, the security should always be a concern when deploying applications and for that reason you should add a minimum layer of…]]></description><link>https://felipetrindade.com/alb-auth-cognito/</link><guid isPermaLink="false">https://felipetrindade.com/alb-auth-cognito/</guid><pubDate>Wed, 26 Nov 2025 11:45:32 GMT</pubDate><content:encoded>&lt;p&gt;As a DevOps, the security should always be a concern when deploying applications and for that reason you should add a minimum layer of authentication in front of authless applications before deploying publicly (specially if they are internal tools). A good &quot;hyped&quot; example of application that does not implement authentation and forces you to manage it is &lt;a href=&quot;https://mlflow.org/docs/3.6.0/self-hosting/architecture/tracking-server/&quot;&gt;MLflow Tracking Server&lt;/a&gt; that recommends you to add authentication using a reverse proxy (e.g. NGINX) via proxy authentication headers. In other words, instead of authentication being handled by the application, we should handle it on the infrastructure side.&lt;/p&gt;
&lt;p&gt;The AWS-native way of implementing authentication on the infrastructure side is &lt;a href=&quot;https://docs.aws.amazon.com/elasticloadbalancing/latest/application/listener-authenticate-users.html&quot;&gt;checking if the user is authenticaticate on the Load Balancer (ALB)&lt;/a&gt;. A common strategy is to link a Cognito User Pool on the ALB actions that redirects unauthenticated users (i.e. cookie is not present) to the Cognito hosted UI to the user perform authentication. In this scenario, the Cognito act as the IdP (if these terms are scary to you I recommend reading the &lt;a href=&quot;https://www.felipetrindade.com/sso/&quot;&gt;SSO blog post&lt;/a&gt;) and the ALB act as the OIDC client.&lt;/p&gt;
&lt;p&gt;It&apos;s important to mention that since we are using Cognito as the IdP we can leverage Cognito capabilities and use the &lt;a href=&quot;https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pools-identity-federation.html&quot;&gt;federated sign-in&lt;/a&gt; of Cognito to allow users to perform login using external providers, such as Google, Okta or even AWS &lt;a href=&quot;https://docs.aws.amazon.com/singlesignon/latest/userguide/what-is.html&quot;&gt;IAM Identity Center&lt;/a&gt;. The later is specially interesting for us when the application that you would like to deploy should be internal, i.e. used only by the internal/development team. Since, AWS Identity Center is considered the best practice for managing user access in AWS environment and it&apos;s a &lt;a href=&quot;https://aws.amazon.com/iam/identity-center/faqs/&quot;&gt;free&lt;/a&gt; service, it is very handy to use it as an SSO panel for AWS and internal applications.&lt;/p&gt;
&lt;p&gt;The main goal of this blog post is to set up a public application (I will use Nginx for simplicity, but it could be any other public application) using AWS Fargate (deployed in an ECS cluster) exposed by an ALB and add authentication via Cognito (either via Cognito User Pool or federated SAML with AWS IAM Identity).&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/f33dfb4fff58f08084471efd47279862/49217/Infrastructure.drawio.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 74.0506329113924%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAPCAYAAADkmO9VAAAACXBIWXMAAAsTAAALEwEAmpwYAAACUklEQVR42pWTSW/UQBCF549x5p9wRXDnMBducODGCQQCJMRy40gEiRCaEaCQSAkomUxm8XgZu6e9tXt9VNvZIQQslWyV3V+9elXu4bLLufZmGEdy5x7qjWGXtxZ/u3pXAZtoiemtPtjapyOg+0cgAZxU3d2Hsa0aznMEixCMlGql4Sh3adC53klreY3ly3WomMNkHE5LShrURYkoTpCveFvQeeglYYU8Vei0gSmER2M0sFh/6qBIsM1zRA8eoRhuwp2x4o8uUcFTILVopWmft9cs3t6VaBrKrxgmN/soPny+cihO6vNApwzyOqJIwfIExipoAkyCBRacIxQCkmQ3WsOQ0nPha/0G1BZf9l9gzLaxECGyakZASZUlNpMEj0djHJC1O3mDWVVjXlWYlyXmdY2Y2mmBFSVFI6AbCVULlKrEav8jxOAJwvgnGM8wGY9Rk8fxcobw+Q2wrXdg0oHVElw5xDSwiM62wCiKaDVoJUiFLGtwucJ8+BrRm34L5AVDSYMptcM02EN0/xrK4bPOznQMxLsQZMUJsCTJijzxZnsPhVrhx/g7vu4MsFiOUNU5DKkr6NAerU+YcqRshYqUZa9uo3p4HVVJm0Ad2otTdspCmAxxEGOym4GXC/LQjxqQVHBGS66U7L6nCKjo/Nt75NRBLI48dEd7dTxloVMc7B9iazBByqc06Q7ggVNa8oD8Cgkc5QUmrMAho6H43HHLZxfbAxudIysCLPkMnFbIOtW+V84iIZ/j5jQSmqwPry7T6kLLpMA2qgP7/9i47vk/wp//BemQgcEkYTFEAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Infrastructure of the project&quot;
        title=&quot;&quot;
        src=&quot;/static/f33dfb4fff58f08084471efd47279862/f058b/Infrastructure.drawio.png&quot;
        srcset=&quot;/static/f33dfb4fff58f08084471efd47279862/c26ae/Infrastructure.drawio.png 158w,
/static/f33dfb4fff58f08084471efd47279862/6bdcf/Infrastructure.drawio.png 315w,
/static/f33dfb4fff58f08084471efd47279862/f058b/Infrastructure.drawio.png 630w,
/static/f33dfb4fff58f08084471efd47279862/49217/Infrastructure.drawio.png 701w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;All the code used is available in the &lt;a href=&quot;https://github.com/felipelaptrin/alb-cognito-auth&quot;&gt;GitHub repository&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;CDK code&lt;/h2&gt;
&lt;p&gt;For simplicity a single Stack will be used to deploy the entire infrastructure: VPC, ALB, Cognito, Fargate, ECS Cluster... Here we go:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// main.ts&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; cdk &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;aws-cdk-lib&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; WorkloadStack &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;./stack/workload&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; devWorkloadConfig &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;./config&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; app &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;cdk&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;App&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;WorkloadStack&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;app&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;DevWorkloadStack&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; env&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; devWorkloadConfig&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;env&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; config&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; devWorkloadConfig &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Our workload stack is defined as:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; cdk &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;aws-cdk-lib&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; ec2 &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;aws-cdk-lib/aws-ec2&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; Construct &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;constructs&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; WorkloadConfig &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;../config&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; ecs &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;aws-cdk-lib/aws-ecs&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; alb &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;aws-cdk-lib/aws-elasticloadbalancingv2&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; acm &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;aws-cdk-lib/aws-certificatemanager&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; route53 &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;aws-cdk-lib/aws-route53&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; route53Targets &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;aws-cdk-lib/aws-route53-targets&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; iam &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;aws-cdk-lib/aws-iam&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; cognito &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;aws-cdk-lib/aws-cognito&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; albActions &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;aws-cdk-lib/aws-elasticloadbalancingv2-actions&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NetworkStackProps&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;cdk&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;StackProps &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  config&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; WorkloadConfig&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;IUserPool&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  userPool&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; cognito&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;UserPool&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  userPoolClient&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; cognito&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;UserPoolClient&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  userPoolDomain&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; cognito&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;UserPoolDomain&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;WorkloadStack&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;cdk&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Stack &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  config&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; WorkloadConfig&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  domain&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; route53&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;IPublicHostedZone&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  certificate&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; acm&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Certificate&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;scope&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Construct&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; id&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; props&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; NetworkStackProps&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;scope&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; props&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;config &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;config&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;domain &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; route53&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;PublicHostedZone&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fromLookup&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;PublicHostedZone&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      domainName&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;config&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;domainName&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;certificate &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;acm&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Certificate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;WildcardCertificate&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      domainName&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;config&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;domainName&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      subjectAlternativeNames&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;*.&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;config&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;domainName&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      validation&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; acm&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;CertificateValidation&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fromDns&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;domain&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; vpc &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createVpc&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; cluster &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createEcsCluster&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;vpc&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; publicAlb&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; httpsListener &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createPublicAlb&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;vpc&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; userPool &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createCognitoAuth&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createAuthlessFargateApp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;vpc&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; cluster&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; publicAlb&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; httpsListener&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; userPool&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;createVpc&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ec2&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Vpc &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; vpc &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ec2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Vpc&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Vpc&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      subnetConfiguration&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          cidrMask&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;24&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;public&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          subnetType&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ec2&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;SubnetType&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;PUBLIC&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          cidrMask&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;24&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;private&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          subnetType&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ec2&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;SubnetType&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;PRIVATE_WITH_EGRESS&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          cidrMask&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;28&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;isolated&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          subnetType&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ec2&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;SubnetType&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;PRIVATE_ISOLATED&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      natGateways&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// It reduces costs but also reduces HA&lt;/span&gt;
      maxAzs&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;config&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;maxAzs&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; vpc&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;createEcsCluster&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;vpc&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ec2&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Vpc&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ecs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Cluster &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ecs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Cluster&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;EcsCluster&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      vpc&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      enableFargateCapacityProviders&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;createPublicAlb&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;vpc&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ec2&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Vpc&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    publicAlb&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; alb&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ApplicationLoadBalancer&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    httpsListener&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; alb&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ApplicationListener&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; publicAlb &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;alb&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;ApplicationLoadBalancer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;PublicAlb&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      vpc&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      vpcSubnets&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; vpc&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;selectSubnets&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        subnetType&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ec2&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;SubnetType&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;PUBLIC&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      internetFacing&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    publicAlb&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addRedirect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; httpsListener &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; publicAlb&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;HttpsListener&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      protocol&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; alb&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ApplicationProtocol&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;HTTPS&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      sslPolicy&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; alb&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;SslPolicy&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;RECOMMENDED_TLS&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      port&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;443&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      defaultAction&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; alb&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ListenerAction&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fixedResponse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;404&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        messageBody&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;No services configured&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      certificates&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;certificate&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      publicAlb&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      httpsListener&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;createCognitoAuth&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; IUserPool &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;COGNITO_DOMAIN&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;auth.&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;config&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;domainName&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// Reference: https://repost.aws/knowledge-center/cognito-custom-domain-errors&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; dummyARecordParentDomain &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;route53&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;ARecord&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;CognitoAuthDummyARecord&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      zone&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;domain&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      recordName&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      target&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; route53&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;RecordTarget&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fromIpAddresses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;8.8.8.8&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; userPool &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;cognito&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;UserPool&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;CognitoAuthUserPool&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      selfSignUpEnabled&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      passwordPolicy&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        minLength&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        requireDigits&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        requireLowercase&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        requireUppercase&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        requireSymbols&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      signInAliases&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        email&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      removalPolicy&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; cdk&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;RemovalPolicy&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;DESTROY&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    userPool&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addDependency&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;dummyARecordParentDomain&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; userPoolDomain &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;cognito&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;UserPoolDomain&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;CognitoAuthUserPoolDomain&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      userPool&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      customDomain&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        certificate&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;certificate&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        domainName&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;COGNITO_DOMAIN&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      managedLoginVersion&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; cognito&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ManagedLoginVersion&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;CLASSIC_HOSTED_UI&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    userPoolDomain&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addDependency&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;dummyARecordParentDomain&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; samlProviderName &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;AuthlessFargateApp&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; supportedIdentityProviders&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;config&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;samlMetadataUrl&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      supportedIdentityProviders &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;cognito&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;UserPoolClientIdentityProvider&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;custom&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;samlProviderName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      supportedIdentityProviders &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;cognito&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;UserPoolClientIdentityProvider&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;COGNITO&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; userPoolClient &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;cognito&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;UserPoolClient&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;CognitoAuthClient&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      userPool&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      supportedIdentityProviders&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      oAuth&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        flows&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          authorizationCodeGrant&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          implicitCodeGrant&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        scopes&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;cognito&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;OAuthScope&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;OPENID&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        callbackUrls&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
          &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;https://&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;config&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;appSubdomain&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;config&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;domainName&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;/oauth2/idpresponse&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;https://&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;config&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;appSubdomain&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;config&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;domainName&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        logoutUrls&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;https://&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;config&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;appSubdomain&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;config&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;domainName&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      generateSecret&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;route53&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;ARecord&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;CognitoAuthAliasRecord&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      zone&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;domain&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      recordName&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;COGNITO_DOMAIN&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      target&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; route53&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;RecordTarget&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fromAlias&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;route53Targets&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;UserPoolDomainTarget&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;userPoolDomain&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;config&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;samlMetadataUrl&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;cognito&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;UserPoolIdentityProviderSaml&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;SamlProvider&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        userPool&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; samlProviderName&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        metadata&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; cognito&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;UserPoolIdentityProviderSamlMetadata&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;config&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;samlMetadataUrl&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        attributeMapping&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          email&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; cognito&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ProviderAttribute&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;other&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;email&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      userPool&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      userPoolClient&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      userPoolDomain&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;createAuthlessFargateApp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    vpc&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ec2&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Vpc&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    cluster&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ecs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Cluster&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    publicAlb&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; alb&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ApplicationLoadBalancer&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    httpsListener&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; alb&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ApplicationListener&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    userPool&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; IUserPool&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;FULL_APP_HOST&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;config&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;appSubdomain&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;config&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;domainName&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; taskDefinition &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ecs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;TaskDefinition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;AuthlessAppTaskDefinition&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      cpu&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;512&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      memoryMiB&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;1024&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      compatibility&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ecs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Compatibility&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;FARGATE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    taskDefinition&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addContainer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;nginx&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      portMappings&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; containerPort&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;80&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      image&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ecs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ContainerImage&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fromRegistry&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;nginx:latest&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      logging&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ecs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;LogDriver&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;awsLogs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        streamPrefix&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;nginx&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; fargateService &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ecs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;FargateService&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;AuthlessFargateService&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      cluster&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      taskDefinition&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; taskDefinition&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      desiredCount&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      assignPublicIp&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      vpcSubnets&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; vpc&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;selectSubnets&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        subnetType&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ec2&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;SubnetType&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;PRIVATE_WITH_EGRESS&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    fargateService&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;taskDefinition&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;executionRole&lt;span class=&quot;token operator&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addManagedPolicy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
      iam&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ManagedPolicy&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fromAwsManagedPolicyName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;service-role/AmazonECSTaskExecutionRolePolicy&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; targetGroup &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;alb&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;ApplicationTargetGroup&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;AppTargetGroup&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      vpc&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      targetType&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; alb&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;TargetType&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;IP&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      protocol&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; alb&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ApplicationProtocol&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;HTTP&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      port&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;80&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      targets&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;fargateService&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      deregistrationDelay&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; cdk&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Duration&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;seconds&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; action &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;albActions&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;AuthenticateCognitoAction&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      userPool&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; userPool&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;userPool&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      userPoolClient&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; userPool&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;userPoolClient&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      userPoolDomain&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; userPool&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;userPoolDomain&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      next&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; alb&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ListenerAction&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;forward&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;targetGroup&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    httpsListener&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addAction&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;AppRule&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      priority&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      action&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      conditions&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;alb&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ListenerCondition&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;hostHeaders&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;FULL_APP_HOST&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;route53&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;ARecord&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;AuthlessAppRecord&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      target&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; route53&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;RecordTarget&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fromAlias&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;route53Targets&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;LoadBalancerTarget&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;publicAlb&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      zone&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;domain&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      recordName&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;FULL_APP_HOST&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And &lt;code class=&quot;language-text&quot;&gt;WorkloadConfig&lt;/code&gt; is defined as:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;WorkloadConfig&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  env&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    account&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    region&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  vpcCidrBlock&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  maxAzs&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  domainName&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  appSubdomain&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  samlMetadataUrl&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And real values, should look like:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; WorkloadConfig &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;./types&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; config&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; WorkloadConfig &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  env&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    account&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;937168356724&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    region&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;us-east-1&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  vpcCidrBlock&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;10.5.0.0/16&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  maxAzs&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  domainName&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;demosfelipetrindade.lat&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  appSubdomain&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;app&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  samlMetadataUrl&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;https://portal.sso.us-east-1.amazonaws.com/saml/metadata/OTM3MTY4MzU2NzI0X2lucy03MjIzMGJjNWY5MDQzYzVj&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;Hands-on&lt;/h2&gt;
&lt;p&gt;Ok! Now that you know how the code looks like, let&apos;s deploy this! I&apos;d recommend you to clone my &lt;a href=&quot;https://github.com/felipelaptrin/alb-cognito-auth&quot;&gt;GitHub repository&lt;/a&gt; and follow along.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Install dependencies&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;You can use &lt;a href=&quot;https://mise.jdx.dev/&quot;&gt;mise&lt;/a&gt; to install all developer dependencies.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;mise &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Then, install NodeJS dependencies:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token function&quot;&gt;yarn&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;
&lt;p&gt;Export AWS credentials to connect to the Shared Assets account in our console&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Change the config values to fit your use-case&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Change the &lt;code class=&quot;language-text&quot;&gt;src/config/config.dev.ts&lt;/code&gt; to fit your use case. The parameter &lt;code class=&quot;language-text&quot;&gt;samlMetadataUrl&lt;/code&gt; is optional, and if not provided, the authentication will be via Cognito User Pool; otherwise, if the parameter is passed, the authentication will be via SAML 2.0 using IAM Identity Center as IdP.&lt;/p&gt;
&lt;p&gt;⚠️ The AWS &lt;a href=&quot;https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-sso-application.html&quot;&gt;does not support creating Identity Center applications&lt;/a&gt; programatically, meaning that the application should be created manually. I suggest first deploying the application without the &lt;code class=&quot;language-text&quot;&gt;samlMetadataUrl&lt;/code&gt; parameter, then proceeding to add it. This will be explained in the following steps.&lt;/p&gt;
&lt;ol start=&quot;4&quot;&gt;
&lt;li&gt;Go to your application endpoint and check if it&apos;s redirecting you to log in via Cognito User Pool.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/50d5d64b0dcb12d41a8435b7ab6ece35/dc333/login-cognito.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 55.06329113924051%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAAsTAAALEwEAmpwYAAABtUlEQVR42pWSTWsTQRiA80PEVCviodoiSi3VXFR6EZEKIopI/4J68SZ4SNJs9iuCN+vBJLU0m5SCVMSLBEI0oB5UcrZpAhbWZI3Jrps8zmwohFohGebhnWHmfXjnIzR7+BZzE3c4e+gms+HbnDl2hcjp61yYWWR++hpzU1c5NXGJqfBFTooox9NHLjMzuUDkxALnwjeYP7pE5PgS5yfvEtI0HU3TGMQBiYTCyspz8vkC6+s5LMsakJMxTzqdQVW1gL0cVRvMQ4ZhMEwqlSIej1MsFrF/2uzUd2g0GgH1eh3btqlWq+i6zv5cSUguDGOaJtFolHK5jOd5tNvtAMdxaDabyCbl8lT7cyUHCmOxGKVSiVbLEaJf/PF9ev0+fq8XCGu12nhCWWGl8gG328Fzu0LRF70nuh8I6+IaxqtQCEvvK+w6LrXdFj9aHTqux++uiy/c37e30UcVGoZJcjlG9tU7Huaq3HvxiUfWVx5vfONB5jP3s194tvWRp09M8bIjCQ0URSGdXWNt8y2ZwharG68DsgUZ3/DS2hT79NEq3ENNKujJZUHiH1QlgaaPKTzojw3zv7y/UVxtVKGH0SQAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Cognito User Pool login&quot;
        title=&quot;&quot;
        src=&quot;/static/50d5d64b0dcb12d41a8435b7ab6ece35/f058b/login-cognito.png&quot;
        srcset=&quot;/static/50d5d64b0dcb12d41a8435b7ab6ece35/c26ae/login-cognito.png 158w,
/static/50d5d64b0dcb12d41a8435b7ab6ece35/6bdcf/login-cognito.png 315w,
/static/50d5d64b0dcb12d41a8435b7ab6ece35/f058b/login-cognito.png 630w,
/static/50d5d64b0dcb12d41a8435b7ab6ece35/dc333/login-cognito.png 938w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;You can create a user in the user pool and validate that this user can actually log in to the application.&lt;/p&gt;
&lt;ol start=&quot;5&quot;&gt;
&lt;li&gt;Deploy the &lt;code class=&quot;language-text&quot;&gt;DevWorkloadStack&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token function&quot;&gt;yarn&lt;/span&gt; cdk deploy DevWorkloadStack&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;After the deployment, go to the &lt;code class=&quot;language-text&quot;&gt;AWS Console&lt;/code&gt; &gt; &lt;code class=&quot;language-text&quot;&gt;CloudFormation&lt;/code&gt; &gt; &lt;code class=&quot;language-text&quot;&gt;DevWorkloadStack&lt;/code&gt; &gt; &lt;code class=&quot;language-text&quot;&gt;Resources&lt;/code&gt; &gt; Copy the Physical ID of the &lt;code class=&quot;language-text&quot;&gt;CognitoAuthUserPool&lt;/code&gt; resource. We are going to use it on step 8.&lt;/p&gt;
&lt;ol start=&quot;6&quot;&gt;
&lt;li&gt;Go to the AWS Identity Center&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/989f4db34e80c148c8d6243180f83176/dc333/01-create-app.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 67.72151898734178%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAOCAYAAAAvxDzwAAAACXBIWXMAAAsTAAALEwEAmpwYAAACMklEQVR42m1Ty3LbMAzU1/fUa0899db/6EyOTdMc6tj1M9Zbsh4URckitQWg0Ml4yhkMTIFeLHbJ4JhPKDuHQs3ozYCyvEDrHh/XPM934b+/19tWyf+CMIqQJCnOYQQzDHKgrhs8Pz1S/MJ+t8UwjDDUzGdu3PdGQmvOy36aLILtbo/Xc4hWKenEq2o0Hv9EeN4k+HvMcDi+Upyw2x2QpBnqpsXlUss0ZVmhqhraV7heJwRFUaJp1NsoDgw5tAnSh89IfnyC+f0FdlQy3uzcf8f32VqHoKpruNvBBdD2JfKf33B4+Ir86TsGo1Frh0ZPaCl4NB/WWjhrBUxGTrMc94sL3eCghhnm6pA2Fpt4wjahSAnQihVCYrIzrhODuYVhFMXkUEtG1DR6Q2xn6khg9G29XpNuO6xe1vJ7tXqRHEYJGTSg6wz2UYdtTOYYYuqI4eFwQpplUF0nxvD4HJr27HwYxpLPlF/PkYB1VOu0pqxR1YpkGIipk/GD0+mELGPnGrJ+uX9c0GZCVlQoigJsXJ4XYHlKctMYI01FPyZgWc9pAdwcC2RlS9bX0nEBJPF7h7o16Ig1M2c5fP3+0rPmfEdFQxbY+Rt/OzBJZj0ZjJlzd768PC5n3iuqKdWJnkxIXI7jGAVdzqIZ0Rl7c9m9AfqXwZc5z0uShyW43CbyzDgLIL+SnAErhZGs94DMksNrxaw+PjtNpnDNj83NRUN+LvfLA/CD55E4mM19jON4a7yExT8RIzdOYKlELQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Creating an application in AWS Identity Center&quot;
        title=&quot;&quot;
        src=&quot;/static/989f4db34e80c148c8d6243180f83176/f058b/01-create-app.png&quot;
        srcset=&quot;/static/989f4db34e80c148c8d6243180f83176/c26ae/01-create-app.png 158w,
/static/989f4db34e80c148c8d6243180f83176/6bdcf/01-create-app.png 315w,
/static/989f4db34e80c148c8d6243180f83176/f058b/01-create-app.png 630w,
/static/989f4db34e80c148c8d6243180f83176/dc333/01-create-app.png 938w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;ol start=&quot;7&quot;&gt;
&lt;li&gt;Select Application of type SAML 2.0&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/30bc9318299983209c07b6736f2c35cb/dc333/02-select-application-type.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 89.87341772151898%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAASCAYAAABb0P4QAAAACXBIWXMAAAsTAAALEwEAmpwYAAAC+0lEQVR42nVUa4/aMBDk//+vfuipVLqDAw4RQt5vkpB34mQ6awQ9tXeWLNtJPLs7M5vVj0OL5KYQlzOqbkZ6GxEVE7quR54XuN0qAAvmef5yKqX0N/0w4OfLC1bXa44wjPRMswzLsmCaFOq6ge8HCIIQURzri98NAZaxXv/GKk4SGIaJgIDjOD0/kKiWZXHasG2HwAESfiszjhMdRPb3NdX3Xl9fsZKLSZqh73v9UDKc54WAN5gXG+eLg+PpjLMpexsXTgniM0BRFMiyK6nJWcGMw+EDKznUdf0EW/SGXFYKx7CBnZY4RznXCmZyQ1qT37bVWQrHRVFqUKnqsD9gVZa3Z3Z/OVHI2xlGmOPohjCCBGaYwkoLRNWEuqrwcTxqjlOWG0eRTmYvgBb5kfQzihNFMZqmZYLkkIpLdu+mi+0lxNvZgxnnyFqFpu3g+glsN4LpRDhaCbpBsWQC5kzZp5Ki6jRNmKimAJadglMO+Ig67OMJm2DEMaO9atqknXAKBgYYYCcjz3eVd7s9Vq7rkWhLqyW2EdCFHObNBNoRfqNglSN8Al0HaEB5zwpR9ZOe+vAAjPMOXtrqMqRcUVgyzAl0Y2CzGLClOOe8R0ErJnwuAKNaKBK9WnRP7jWg0m6fcZf3Hmxm2QUvZv2CkKtkFzRUnmedodxh4JrZteTuYezd7iDGpnoUxg2pILPQ2PxgGBXannOYSfiMhvu6o2W4aq7HkYEnKO4Htp0MLYq0nnixoSiPThELiL8Mw4DjOPA8/5mFrP0oJQODzGnh+f5uL8Z+AHweQoFkIeYV0cQBwm/TNKio8Jtd0U4+nLyFRw7F9CLYVkrW3fEfoNKg0gkXtpnLDG3HQ8YWldLdvMG7YWFvedhfPJy8EPkIbN4P9wz/HXdApVtSGj8lkBhfuBJAh844BZnuJCO46gD8A2L7HaBkXFW1/nXJFJ4fz8UVwl/dDuyWGEXVwvEjbZLNZvs1oBDf0peSmQA/aPlMT9PXFKOnC1qqf/+5/Fqv8QfNG2NhSomQCwAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Selecting type of the application to be SAML 2.0&quot;
        title=&quot;&quot;
        src=&quot;/static/30bc9318299983209c07b6736f2c35cb/f058b/02-select-application-type.png&quot;
        srcset=&quot;/static/30bc9318299983209c07b6736f2c35cb/c26ae/02-select-application-type.png 158w,
/static/30bc9318299983209c07b6736f2c35cb/6bdcf/02-select-application-type.png 315w,
/static/30bc9318299983209c07b6736f2c35cb/f058b/02-select-application-type.png 630w,
/static/30bc9318299983209c07b6736f2c35cb/dc333/02-select-application-type.png 938w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;ol start=&quot;8&quot;&gt;
&lt;li&gt;Configure the SAML 2.0 Application&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/13bf086aaafe8b82b8d5b89d473c74ef/dc333/03-configure-saml.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 89.87341772151898%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAASCAYAAABb0P4QAAAACXBIWXMAAAsTAAALEwEAmpwYAAACNUlEQVR42pVT2XKbQBD01+eH8pSHvKbKqejIS8COEYcQiEOIm4Vd6MysJNvlSCjZqq4dIaqZ7ul5aNoO+6PUKBuJtJSIcomWnru7FGGSY5omjATgb0zTiLquMQwDHh+/40GpEQwp324pFSTVTT9BSLyeaZxeMZ7BtaL3+SyXyzfC9+AX67qBbW/guC5c18PGdvSzvu8hhND3pe667ky4uk1YlBUM8wnb7Q5RHCMIQ+R5gYrklfRfVVX6A1yXZTnfIYM9dBwXvr/DLghhOw7SNNV+ak/HUfvHkPLky2KxmCEkGWG412T+bqdlNU2jzWeC92DpdwmF6FGVBeqqhOgEPVMaH8n+iXAkVI3Ar02KJzdFP8hzRHD1sPx5D8+x6SkNjAvRxb+P4M5PHc7EpqpqmKZJcbHxYtn4/WKRhy06sqIjC3RcWqFr9pbPev1zLoc1LCLhDEZRRAMKEe3ppiHxsOKEtoh+B1Tv6ebDDVwlnEaFQyWxsmI8b2P4WQMnKuhukVGGa+o+ThKdy6IokGWZlm1Sbm8MRSGnvTa8A579BN6hRkQfiGuFYzdpuayA5fKE27bVHRqGeTs2nLsgCLS8KIr1NnA3RZ7rztg3nu4lSncJeVO8rU+hDuB6nibmrnjlmJjrE5nC0Iv7hBxs7oyJwv0eCa1ddjxq71gmr5xSkuyRGM6xMcwZwutQ2t+mkwSFpBgQ5jQk6wu6H59gLL7+L+Fpi5isFTS4WoLmhSZYQVifYay/4Q8cp27QXTqfmQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Configure the SAML 2.0 Application&quot;
        title=&quot;&quot;
        src=&quot;/static/13bf086aaafe8b82b8d5b89d473c74ef/f058b/03-configure-saml.png&quot;
        srcset=&quot;/static/13bf086aaafe8b82b8d5b89d473c74ef/c26ae/03-configure-saml.png 158w,
/static/13bf086aaafe8b82b8d5b89d473c74ef/6bdcf/03-configure-saml.png 315w,
/static/13bf086aaafe8b82b8d5b89d473c74ef/f058b/03-configure-saml.png 630w,
/static/13bf086aaafe8b82b8d5b89d473c74ef/dc333/03-configure-saml.png 938w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;code class=&quot;language-text&quot;&gt;Application Start URL&lt;/code&gt; should be set to: &lt;code class=&quot;language-text&quot;&gt;https://{appSubdomain}.{domainName}&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;The &lt;code class=&quot;language-text&quot;&gt;Application ACS URL&lt;/code&gt; should be set to: &lt;code class=&quot;language-text&quot;&gt;https://{appSubdomain}.{domainName}/saml2/idpresponse&lt;/code&gt;. The Cognito &lt;a href=&quot;https://docs.aws.amazon.com/cognito/latest/developerguide/saml2-idpresponse-endpoint.html&quot;&gt;documentation&lt;/a&gt; specificies the &lt;code class=&quot;language-text&quot;&gt;/saml2/idpresponse&lt;/code&gt; path.&lt;/li&gt;
&lt;li&gt;The &lt;code class=&quot;language-text&quot;&gt;Application SAML audience&lt;/code&gt; should be set to: &lt;code class=&quot;language-text&quot;&gt;urn:amazon:cognito:sp:{awsRegion}:{userPoolId}&lt;/code&gt; based on the Cognito &lt;a href=&quot;https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pools-integrating-3rd-party-saml-providers.html&quot;&gt;documentation&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Make sure to substitute the variable in the brackets with the correct value:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;{appSubdomain}&lt;/code&gt;: The value used in your &lt;code class=&quot;language-text&quot;&gt;src/config/config.dev.ts&lt;/code&gt; file&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;{domainName}&lt;/code&gt;: The value used in your &lt;code class=&quot;language-text&quot;&gt;src/config/config.dev.ts&lt;/code&gt; file&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;{awsRegion}&lt;/code&gt;: The value used in your &lt;code class=&quot;language-text&quot;&gt;src/config/config.dev.ts&lt;/code&gt; file (env.region).&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;{userPoolId}&lt;/code&gt;: The value you copied on step 4.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Copy the &lt;code class=&quot;language-text&quot;&gt;IAM Identity Center SAML metadata URL&lt;/code&gt; to use in the following step.&lt;/p&gt;
&lt;ol start=&quot;9&quot;&gt;
&lt;li&gt;Edit attribute mapping&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/e890b39a434f6e68fcd41a7a1a8c578e/dc333/attribute-mappings.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 50.632911392405056%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAAAsTAAALEwEAmpwYAAABjklEQVR42oWSS7KcMAxF2W5WkdVkC9lBBhlmkhnp1/QHDMYfDLiNbW4kU115r5OqUHWwBLJ0JbtS2qC+SNQ3g5swuHQab3eLazuiG1fM8wylLZQyCGFDzjtSyh/gb8uyYpocqlFpnM9v6NoWTdMU5DCgrn/BGIPj2Y/3vhc4wb/JqLhy2wqIfkAnerSdoHWAINi+37sDirneKWZQpFZjHBWhC1IqWOuK2srYCYMcSxDztHmDNhbcgRzHYps5QruAbYsFHsET9lNKqB6PQEp6UiFIaY+BFDg3k9+S31EBScFbSTpZW9o/5pbx+pSWObscJGZKsswL/OJpwAvOzaW0zAW5Oitm9TFuWH2Af0Q4nzGtmeb6PiEH2wVuTZgpwHleA6l08N6XIMb7ByWLZKeyhi1B2IROp0PtTnHcsg8JN5VwEhtO/Ya6Cxhd+nCqB6/+/lfLiU66KvMg9vwH9mNMhedde2/z/0BCGpnR0+2w3z6j/foJy88vR8Ky4WX9HzHmMsOVxhLEDyzX79j0Cb8ByFYDwy4P6+8AAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Editing Attribute mappings&quot;
        title=&quot;&quot;
        src=&quot;/static/e890b39a434f6e68fcd41a7a1a8c578e/f058b/attribute-mappings.png&quot;
        srcset=&quot;/static/e890b39a434f6e68fcd41a7a1a8c578e/c26ae/attribute-mappings.png 158w,
/static/e890b39a434f6e68fcd41a7a1a8c578e/6bdcf/attribute-mappings.png 315w,
/static/e890b39a434f6e68fcd41a7a1a8c578e/f058b/attribute-mappings.png 630w,
/static/e890b39a434f6e68fcd41a7a1a8c578e/dc333/attribute-mappings.png 938w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Map:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;Subject&lt;/code&gt; to &lt;code class=&quot;language-text&quot;&gt;${user:subject}&lt;/code&gt; with &lt;code class=&quot;language-text&quot;&gt;persistent&lt;/code&gt; format.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;email&lt;/code&gt; to &lt;code class=&quot;language-text&quot;&gt;${user:email}&lt;/code&gt; with &lt;code class=&quot;language-text&quot;&gt;basic&lt;/code&gt; format.&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start=&quot;10&quot;&gt;
&lt;li&gt;Deploy the stack with the &lt;code class=&quot;language-text&quot;&gt;samlMetadataUrl&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Add the &lt;code class=&quot;language-text&quot;&gt;samlMetadataUrl&lt;/code&gt; parameter to the &lt;code class=&quot;language-text&quot;&gt;src/config/config.dev.ts&lt;/code&gt; and then deploy the stack again:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token function&quot;&gt;yarn&lt;/span&gt; cdk deploy DevWorkloadStack&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ol start=&quot;11&quot;&gt;
&lt;li&gt;Assign the Application to the identity center user&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/2e9db1833325a679914d138d4dbbaae5/dc333/user-assignment.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 80.37974683544303%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAQCAYAAAAWGF8bAAAACXBIWXMAAAsTAAALEwEAmpwYAAACNElEQVR42q2UW3PSUBSF+Wv+AJ99aKfFEiAkEK4Nl1ou7eDQUcfHjqWVi6Udp9WCdtR3n/wZ6mhrsOUSUgqYLPcJpFAH0TqemcWGnMzH2iv7xCZIUTjFKByCDJdXBu+LgpcS8AbiEP1R+MMrMyWFEqYESTbvt4mBGJZcEngxDDeJ4wPw+JbBe5dHNQKXEPqtGMSCMtkYmW0w2Eoyi+3CLnaKu3haqpi1WN7D84OjCVXHOqwim3t81YkvGB8CGcxBzgrlfTTbKpT6Gc4bzVFtYdoyDMOsR7U3pqFA5N4I6I/B4QlhbtGNzXwZDc3AF0XF13pnWL9r+HjSxKeTFj6ftlBv93CmDlBv9dC+BA6rb68DvZSh4Jdxl3LM7zyDqqrQ9R/MA7NhOpmUtXR9+L32+h2ck0DWv4tanrcL2NwqQlHqOP2moNu9RL/fh6EbU9oFlOYAjS61/OoXh8OHEsacnceT7RL9s46OpuGCgAzKfk8DnncG6PSB6lQgOVxYEumpVq4FPmvNbJldWHD8JyD7YDPk9ARRKO3dADiMokYtc+4g/CFrDskhRzCW4RZleFPgi/0i5m/fgmi/Q8DoaGwoRzvno1NS+QeHx3C6REgBeTyHTIsEzNOx+xsg277K8JhlGIbPatnKkOOD5jn+E/Cip0Ojue98yEJ7H0b15QE4IUIZJsZA600RiaWxdv8RMtmHSK5tYDWzgXgyh3gqhxjVRIpdyyG1/gCZTArr6VXIiTQsBgP+BKI+5gbbfrqKAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;User assignment&quot;
        title=&quot;&quot;
        src=&quot;/static/2e9db1833325a679914d138d4dbbaae5/f058b/user-assignment.png&quot;
        srcset=&quot;/static/2e9db1833325a679914d138d4dbbaae5/c26ae/user-assignment.png 158w,
/static/2e9db1833325a679914d138d4dbbaae5/6bdcf/user-assignment.png 315w,
/static/2e9db1833325a679914d138d4dbbaae5/f058b/user-assignment.png 630w,
/static/2e9db1833325a679914d138d4dbbaae5/dc333/user-assignment.png 938w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;ol start=&quot;12&quot;&gt;
&lt;li&gt;Go to your application endpoint and check if it&apos;s redirecting you to log in via Identity Center.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;⚠️ Even after the deployment is successful, it might take a couple of minutes for Cognito to redirect to Identity Center.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/e0ecfc89e907d8932f30653fbf9c7f15/dc333/identity-center-login.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 78.48101265822784%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAQCAYAAAAWGF8bAAAACXBIWXMAAAsTAAALEwEAmpwYAAABcUlEQVR42s2STUsCQRyH/RxSUkmnQkpExDz3dgnslEHRF+qm9wjqO3StoEOUCYXUsbB9d3Z3dt2X2fk1u1oRaGQINfAwL8w8zH9+kyqkd1Cc3kUhXUMxU0M+u4nyYhWV3DZKC1tYmltPWM5uJMTjvOhzM2tYmV9FcaqKUmYflewByrN7SJldCrProN9TWCaFbbugA+K5ST6xCP1Ys4iN9/MW6ZPChNvfCDnnCf/3hm4vgCPgkxDGpbZur9C6vhATNqGSeQ+IaGz/vdCybBBCoGoadMOEZhB4ngcWhggFo0IaKTQMHZL0CkWRoSoSFFmC13MRMSEMgnGFHI7PYPa+vlk0gH3zjVJDUkjSZOoN2OMxgqdTuO2TBF+M/fYRws7lIHH+M2HcWOcc4d0h2H0DtNmA06yDP9TBWmLt+WwMYVxWxEC9EHYAUIHlA5oIWnYBSeCF0cjERwgjkXAXhq6BdPUk4ReFoqNSyJqZpB/vGSZ8A+M8urGOnVj6AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Identity Center Login&quot;
        title=&quot;&quot;
        src=&quot;/static/e0ecfc89e907d8932f30653fbf9c7f15/f058b/identity-center-login.png&quot;
        srcset=&quot;/static/e0ecfc89e907d8932f30653fbf9c7f15/c26ae/identity-center-login.png 158w,
/static/e0ecfc89e907d8932f30653fbf9c7f15/6bdcf/identity-center-login.png 315w,
/static/e0ecfc89e907d8932f30653fbf9c7f15/f058b/identity-center-login.png 630w,
/static/e0ecfc89e907d8932f30653fbf9c7f15/dc333/identity-center-login.png 938w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;ol start=&quot;13&quot;&gt;
&lt;li&gt;Check if the application is accessible after the login&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/1420157f7cdf639e8d967fcef85822e4/dc333/application.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 50.632911392405056%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAAAsTAAALEwEAmpwYAAABcklEQVR42q2Qy07bQABFvShCUOzMww+C4pQIO8almATHrhO1JFIcUilV+QAW9AOq/v/udOwkC1rUh9TF0V3M3KM7Y13rj1yIT0zdb3w4/U6unxjZXxmpR/qi4vQ4R7/KcA9ufiE8vuXsYEbvcE7/aGHyDqv7Oic8uSU4esuZfU0U3vAuKbgaFmTJjOSNOfeu6MoE/yTCt3c4EbF/Sc8ecy4rIm/GQJZYUjsoLZDKIeh6bD5vWK/XLBYLptOKelXz8PCF4TDGdmyUkkjZ3BeIJnUHYbpCOm1aSmkapFS4rsd4PGYyKaiqivJ9SVEUlGVphEPSy5Q0TcmyjMH5wHRk29U7R4MRKhqawyAImM/nLJdL6rpmtVq1eX+/MWvvSJLEiEf0+xFab3s/80zoeZ5ZNyHP83ZVk3EcE4YRvd6FkZg12t29SP5euMf3/VbcZLO40+ngmL8TwmklW9HLsheF+5IQomW7av9H6o9Yf3PpX/jvwh+o8AeKv+m/lAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Application page&quot;
        title=&quot;&quot;
        src=&quot;/static/1420157f7cdf639e8d967fcef85822e4/f058b/application.png&quot;
        srcset=&quot;/static/1420157f7cdf639e8d967fcef85822e4/c26ae/application.png 158w,
/static/1420157f7cdf639e8d967fcef85822e4/6bdcf/application.png 315w,
/static/1420157f7cdf639e8d967fcef85822e4/f058b/application.png 630w,
/static/1420157f7cdf639e8d967fcef85822e4/dc333/application.png 938w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2&gt;Cya!&lt;/h2&gt;
&lt;p&gt;I hope you liked this new blog post! ArgoCD is an amazing tool that allows us to achieve pure GitOps principles and it&apos;s extremely used in the DevOps world.&lt;/p&gt;
&lt;p&gt;See you around! 👋&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Using end-to-end IAM Auth to connect to RDS using RDS Proxy with AWS CDK]]></title><description><![CDATA[The goal of this blog post is to deploy a Lambda Function inside the VPC and make this Lambda Function connect to an RDS Cluster using…]]></description><link>https://felipetrindade.com/rds-iam-auth/</link><guid isPermaLink="false">https://felipetrindade.com/rds-iam-auth/</guid><pubDate>Thu, 09 Oct 2025 10:40:32 GMT</pubDate><content:encoded>&lt;p&gt;The goal of this blog post is to deploy a Lambda Function inside the VPC and make this Lambda Function connect to an RDS Cluster using Authentication. To increase the resiliency and reduce spikes of connections in the database, we are also going to add an RDS proxy.&lt;/p&gt;
&lt;p&gt;All the code used in this blog post is available in my &lt;a href=&quot;https://github.com/felipelaptrin/rds-iam-auth&quot;&gt;GitHub Repository&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;IAM Authentication&lt;/h2&gt;
&lt;p&gt;In AWS, there are two ways of authenticating in RDS databases:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Using static credentials of users in the database (i.e., master password or user created within the database)&lt;/li&gt;
&lt;li&gt;Using dynamic credentials, i.e., using IAM Authentication&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There are several benefits of using dynamic credentials, such as: no need to handle password rotation and easy integration with AWS services (AWS Lambda, ECS Task, EC2...), since these services already assume an IAM Role that can be granted access to the database. Your security team will be very happy with you!&lt;/p&gt;
&lt;p&gt;When using IAM Authentication, your application will issue a token (temporary password) by using the &lt;a href=&quot;https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/rds/client/generate_db_auth_token.html&quot;&gt;generate_db_auth_token&lt;/a&gt; action. This token will be used by your application as the password of the DB user that can use IAM Auth (more on that soon).&lt;/p&gt;
&lt;p&gt;This token has a lifetime of 15 minutes and can only be used once, but once this token is used and you are connected to the database, there is no &quot;lifetime&quot; involved. You are in!&lt;/p&gt;
&lt;p&gt;The steps needed to use IAM Authentication are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Allow RDS Cluster/Instance to use IAM Authentication&lt;/li&gt;
&lt;li&gt;Create a &lt;a href=&quot;https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/UsingWithRDS.IAMDBAuth.DBAccounts.html&quot;&gt;user&lt;/a&gt; in the DB that will be used during IAM Authentication. The way to create this user depends on the engine (PostgreSQL, MySQL, MariaDB) of the database.&lt;/li&gt;
&lt;li&gt;Create an IAM Role that will be used by your application. The &lt;a href=&quot;https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/UsingWithRDS.IAMDBAuth.html&quot;&gt;policy&lt;/a&gt; of this role should allow &lt;code class=&quot;language-text&quot;&gt;rds-db:connect&lt;/code&gt; action on the ARN of the DB user.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With that being done, your application can issue the token to get the password to connect to the database using the created DB user.&lt;/p&gt;
&lt;p&gt;There are some &lt;a href=&quot;https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/UsingWithRDS.IAMDBAuth.html#UsingWithRDS.IAMDBAuth.Limitations&quot;&gt;limitations&lt;/a&gt;, or at least, points of attention in case you decide to use IAM Authentication. The most relevant, in my opinion, is the fact that IAM database authentication throttles connections at 200 connections per second, and the fact that CloudWatch and CloudTrail don&apos;t log IAM authentication.&lt;/p&gt;
&lt;p&gt;Two hundred connections per second might look scary and a huge spike, but when we are talking about Lambda Functions (serverless) that create connections on every new lambda context creation, this can easily be a reality. To solve this problem, we can add a pool system that will manage the connections to the database to avoid this problem!&lt;/p&gt;
&lt;p&gt;There are several open source database pool systems that you can use, e.g., &lt;a href=&quot;https://www.pgbouncer.org/&quot;&gt;pgBouncer&lt;/a&gt; (for PostgreSQL). In case you don&apos;t want to self-deploy and manage your DB pool system, you can use the AWS managed solution: RDS Proxy.&lt;/p&gt;
&lt;h2&gt;RDS Proxy&lt;/h2&gt;
&lt;p&gt;RDS Proxy is an AWS solution to manage a database pool of connections. It&apos;s a service that is easily integrated with RDS (unfortunately, it does not work with self-managed databases - e.g., deployed in EC2) and can help you with unpredictable surges in database traffic, as mentioned, this is very common in serverless scenarios.&lt;/p&gt;
&lt;p&gt;RDS Proxy can connect to databases using static credentials to the database or with IAM Authentication. You know what this means? That we can have an end-to-end integration (App -&gt; RDS Proxy -&gt; Database) using IAM Authentication! And that&apos;s exactly what we are going to do in this blog post.&lt;/p&gt;
&lt;h2&gt;Architecture&lt;/h2&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/93fab98c64b393971d167947e7f54440/f1d1f/RdsIamAuthProxy.drawio.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 56.32911392405063%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAAsTAAALEwEAmpwYAAACRElEQVR42lWTyY7TQBRF+1v4A/6FD2KFBBuW7JBYwQqJHRKwArVQxBABcTqDHWdwYjtO4tlV5eHwnKQlWDzJrnKduu/e55smK4nefSVfb6ijFOqGztQgVWtDVqnzWqs1rTHnPaMMujKy3tJp2TPXkvUbOtBFyrf5a9xoxCa2SasQbQ6si4RXjosdl8wygy8H4sawCnM+fvcYznx+LY84USPnOrm05qZr+tsNuT4QFx5xviUpdlJbVF0RiJJ1mrLxXTZpgi9KU9Ux8VssV2ONKsZeg7Nvac7A9kIuBJiVvoADUeiTljtRWRK0ELg/yJ48YDO9ZZlUrNwV01PB8I2P9chmYmsWp45GCbDZ/KHxxhR1TCLqgu2eUxKId7uLQtPieAuGb58yX9ts8hI/PDHd1/z8euDziwnWUoCHK1B9eI7+8pKiKwW0xRocCHahKO2BSlpWrAppqwRXAgyl5UzDTFoeB5qhn3G3q1nct7x05NbFAtUkRPEKZzXBCxxR6AtQ1JiOXehyfP8YX9a9UhNEojAw3M0V40HORDxcRFegUgqVFxTqcA4jU77AgrOHSjz0G9i6Q6JnD1nNBtinDNt2GK1ThlbM6FPCnWuk5X9DkfnKVXQGpkUfyCWUs4cyKs7xxO1ozDSM2Mu3fcqzoOO3WzCwIixPy8h1V2DTXlM+CqQPI7xWgG4UodL44uNWalf170q6aJmHLbb4Zkc9vGUpClt1r1B8aWQWG63/q34+a335Y2oB19f3XomRw7q6VP9cS7WV5i9ZJz3np0N8bAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;End to End IAM Auth using RDS Proxy&quot;
        title=&quot;&quot;
        src=&quot;/static/93fab98c64b393971d167947e7f54440/f058b/RdsIamAuthProxy.drawio.png&quot;
        srcset=&quot;/static/93fab98c64b393971d167947e7f54440/c26ae/RdsIamAuthProxy.drawio.png 158w,
/static/93fab98c64b393971d167947e7f54440/6bdcf/RdsIamAuthProxy.drawio.png 315w,
/static/93fab98c64b393971d167947e7f54440/f058b/RdsIamAuthProxy.drawio.png 630w,
/static/93fab98c64b393971d167947e7f54440/f1d1f/RdsIamAuthProxy.drawio.png 739w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;We are going to deploy all this infrastructure using AWS CDK. Ready?&lt;/p&gt;
&lt;h2&gt;Talk is cheap, show me the code&lt;/h2&gt;
&lt;p&gt;Let&apos;s start with our application code. A simple Lambda Python code that connects to the database and runs a simple query.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;python&quot;&gt;&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; os

&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; boto3
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; psycopg2
&lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; dotenv &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; load_dotenv


&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;get_db_password&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    client &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; boto3&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;client&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;rds&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; region_name&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;os&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;getenv&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;AWS_REGION&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    token &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; client&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;generate_db_auth_token&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        DBHostname&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;os&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;getenv&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;DB_HOST&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        Port&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;os&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;getenv&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;DB_PORT&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        DBUsername&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;os&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;getenv&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;DB_USER&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        Region&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;os&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;getenv&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;AWS_REGION&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-interpolation&quot;&gt;&lt;span class=&quot;token string&quot;&gt;f&quot;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;token &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; token


&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;handler&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; context&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;------ LAMBDA STARTED ------&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; os&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;getenv&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;USE_IAM_AUTH&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;lower&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;true&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
            password &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; get_db_password&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
            password &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; os&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;getenv&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;DB_PASSWORD&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        conn &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; psycopg2&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;connect&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
            host&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;os&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;getenv&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;DB_HOST&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            user&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;os&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;getenv&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;DB_USER&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            password&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;password&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            port&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;os&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;getenv&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;DB_PORT&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            dbname&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;os&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;getenv&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;DB_NAME&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            sslmode&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;require&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

        cursor &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; conn&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;cursor&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        cursor&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;execute&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;SELECT current_database(), current_user;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        record &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; cursor&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;fetchall&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
        db_name&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; db_user &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; record
        &lt;span class=&quot;token keyword&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-interpolation&quot;&gt;&lt;span class=&quot;token string&quot;&gt;f&quot;Connected to database &apos;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;db_name&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos; with user &apos;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;db_user&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;...&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;except&lt;/span&gt; Exception &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;raise&lt;/span&gt; e


&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; __name__ &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;__main__&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; os&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;getenv&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;LOCAL_DEVELOPMENT&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        load_dotenv&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        handler&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The code is simple. You can connect via static credentials or with IAM Auth (if the environment variable USE_IAM_AUTH is set to TRUE).&lt;/p&gt;
&lt;p&gt;Now, let&apos;s take a look at the CDK Code.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; ArnFormat&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Duration&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Fn&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Stack&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; StackProps&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Tags &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;aws-cdk-lib&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; ec2 &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;aws-cdk-lib/aws-ec2&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; rds &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;aws-cdk-lib/aws-rds&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; Construct &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;constructs&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; rdsSql &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;cdk-rds-sql&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; lambda &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;aws-cdk-lib/aws-lambda&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; iam &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;aws-cdk-lib/aws-iam&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; ecrAssets &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;aws-cdk-lib/aws-ecr-assets&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; path &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;path&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;IamAuthStack&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Stack&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  vpc&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ec2&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Vpc&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;scope&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Construct&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; id&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; props&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; StackProps&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;scope&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; props&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    Tags&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Repo&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;github.com/felipelaptrin/iam-authentication-rds&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;vpc &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setVpc&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; dbName &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;app&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; dbUser &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;iamuserapp&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; cluster&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; clusterProxy&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; clusterProxySg&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; clusterProxyId &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createDatabase&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;dbName&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; dbUser&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setRdsIamAuthentication&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;dbName&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; dbUser&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; cluster&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setBackend&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;dbUser&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; cluster&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; clusterProxy&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; clusterProxySg&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; clusterProxyId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;setVpc&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ec2&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Vpc &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; vpc &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ec2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Vpc&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Vpc&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      ipAddresses&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ec2&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;IpAddresses&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;cidr&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;10.220.0.0/16&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      maxAzs&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      natGateways&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      subnetConfiguration&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          cidrMask&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;24&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;public&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          subnetType&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ec2&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;SubnetType&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;PUBLIC&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          cidrMask&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;24&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;private&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          subnetType&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ec2&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;SubnetType&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;PRIVATE_WITH_EGRESS&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          cidrMask&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;24&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;databases&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          subnetType&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ec2&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;SubnetType&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;PRIVATE_ISOLATED&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; vpc&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;createDatabase&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    dbName&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    dbUser&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    cluster&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; rds&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;DatabaseCluster&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    clusterProxy&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; rds&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;CfnDBProxy&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    clusterProxySg&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ec2&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;SecurityGroup&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    clusterProxyId&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; cluster &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;rds&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;DatabaseCluster&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;DatabaseCluster&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      vpc&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;vpc&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      iamAuthentication&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      serverlessV2MaxCapacity&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      serverlessV2MinCapacity&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      autoMinorVersionUpgrade&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      engine&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; rds&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;DatabaseClusterEngine&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;auroraPostgres&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        version&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; rds&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;AuroraPostgresEngineVersion&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;VER_17_4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      writer&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; rds&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ClusterInstance&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;serverlessV2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;writer&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      vpcSubnets&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;vpc&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;selectSubnets&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        subnetType&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ec2&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;SubnetType&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;PRIVATE_ISOLATED&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      defaultDatabaseName&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; dbName&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      deletionProtection&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; proxyRole &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;iam&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Role&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;PostgresClusterRdsProxyRole&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      assumedBy&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;iam&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;ServicePrincipal&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;rds.amazonaws.com&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    cluster&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;secret&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;grantRead&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;proxyRole&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    cluster&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;grantConnect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;proxyRole&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; dbUser&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; clusterProxySg &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ec2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;SecurityGroup&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;PostgresClusterRdsProxySecurityGroup&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      vpc&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;vpc&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    cluster&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;connections&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;allowFrom&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;clusterProxySg&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; ec2&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Port&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;POSTGRES&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// L2 Construct rds.DatabaseProxy does not support modifying the DefaulAuthScheme to IAM. So we should use L1 construct instead&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; clusterProxy &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;rds&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;CfnDBProxy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;PgProxyIamE2E&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      dbProxyName&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;PostgreSqlProxy&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      roleArn&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; proxyRole&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;roleArn&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      engineFamily&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;POSTGRESQL&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      vpcSubnetIds&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;vpc&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isolatedSubnets&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;subnet&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; subnet&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;subnetId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      vpcSecurityGroupIds&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;clusterProxySg&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;securityGroupId&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      requireTls&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      defaultAuthScheme&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;IAM_AUTH&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;rds&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;CfnDBProxyTargetGroup&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;PgProxyTG&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      dbProxyName&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; clusterProxy&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dbProxyName&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      targetGroupName&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;default&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      connectionPoolConfigurationInfo&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      dbClusterIdentifiers&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;cluster&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;clusterIdentifier&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; clusterProxyId &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Fn&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;select&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Fn&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;db-proxy:&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; clusterProxy&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;attrDbProxyArn&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      cluster&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      clusterProxy&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      clusterProxySg&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      clusterProxyId&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;setRdsIamAuthentication&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;dbName&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; dbUser&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; dbCluster&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; rds&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;DatabaseCluster&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; provider &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;rdsSql&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Provider&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Provider&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      vpc&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;vpc&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      cluster&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; dbCluster&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      secret&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; dbCluster&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;secret&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      vpcSubnets&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        subnetType&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ec2&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;SubnetType&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;PRIVATE_WITH_EGRESS&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;rdsSql&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Sql&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;CreateIamAuthUser&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      provider&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; provider&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      statement&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;
        CREATE ROLE &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;dbUser&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; LOGIN;
        GRANT rds_iam TO &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;dbUser&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;;
        GRANT CONNECT ON DATABASE &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;dbName&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; TO &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;dbUser&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;;
        GRANT USAGE ON SCHEMA public TO &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;dbUser&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;;
      &lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;setBackend&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    dbUser&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    dbCluster&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; rds&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;DatabaseCluster&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    dbProxy&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; rds&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;CfnDBProxy&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    dbProxySg&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ec2&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;SecurityGroup&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    dbProxyId&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; lambdaFunction &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;lambda&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;DockerImageFunction&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;BackendFunction&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      vpc&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;vpc&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      vpcSubnets&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        subnetType&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ec2&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;SubnetType&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;PRIVATE_WITH_EGRESS&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      code&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; lambda&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;DockerImageCode&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fromImageAsset&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;path&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;__dirname&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;../backend&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        platform&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ecrAssets&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Platform&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;LINUX_AMD64&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      environment&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token constant&quot;&gt;USE_IAM_AUTH&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;TRUE&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token constant&quot;&gt;DB_HOST&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; dbProxy&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;attrEndpoint&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token constant&quot;&gt;DB_NAME&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; dbCluster&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;secret&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;secretValueFromJson&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;dbname&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;unsafeUnwrap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token constant&quot;&gt;DB_PORT&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; dbCluster&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;secret&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;secretValueFromJson&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;port&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;unsafeUnwrap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token constant&quot;&gt;DB_USER&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; dbUser&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      timeout&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Duration&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;seconds&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      memorySize&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1024&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      functionName&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;TestIamAuth&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; rdsProxyDbUserArn &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Stack&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;formatArn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      service&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;rds-db&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      resource&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;dbuser&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      resourceName&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;dbProxyId&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;/iamuserapp&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      arnFormat&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ArnFormat&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;COLON_RESOURCE_NAME&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    lambdaFunction&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addToRolePolicy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;iam&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;PolicyStatement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        actions&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;rds-db:connect&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        resources&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;rdsProxyDbUserArn&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    lambdaFunction&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;connections&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;allowTo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;dbProxySg&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; ec2&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Port&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;POSTGRES&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;In the CDK code we are creating:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;VPC with public (for the NAT Gateway), private (for CDK-RDS-SQL module), and isolated (for RDS Cluster and RDS Proxy)&lt;/li&gt;
&lt;li&gt;Serverless RDS Cluster (but it didn&apos;t have to be serverless)&lt;/li&gt;
&lt;li&gt;RDS Proxy (here I&apos;ve used a L1 construct since the current L2 construct of RDS Proxy does not support IAM Authentication)&lt;/li&gt;
&lt;li&gt;Docker Lambda Function&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Pay attention to the following security group rules:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;RDS Cluster should allow inbound traffic coming from the RDS Proxy&lt;/li&gt;
&lt;li&gt;RDS Proxy should allow inbound traffic coming from the Lambda Function&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is because we are aiming for the following flow: (Lambda Function -&gt; RDS Proxy -&gt; RDS Cluster).&lt;/p&gt;
&lt;p&gt;The creation of the DB user that will be used in the IAM Auth was created using &lt;a href=&quot;https://constructs.dev/packages/cdk-rds-sql&quot;&gt;cdk-rds-sql&lt;/a&gt; construct. This is needed because the RDS sits in an isolated subnet, meaning that when the code runs, it will run on our machine, and our machine can&apos;t connect to the database without a VPN. The idea behind CDK-rDS-SQL is to deploy a Lambda Function inside the VPC that can reach the database and execute queries.&lt;/p&gt;
&lt;h2&gt;Cya!&lt;/h2&gt;
&lt;p&gt;That&apos;s it! We&apos;ve implemented a full end-to-end IAM Auth to connect to RDS using RDS Proxy! This is the basic idea, and you can adapt this to your needs (e.g., add more perms to the DB user to actually use the database). I hope you&apos;ve liked it!&lt;/p&gt;
&lt;p&gt;See you around! 👋&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Ephemeral Environment for frontend]]></title><description><![CDATA[Today I will cover a strategy for ephemeral environments for frontend (static websites) applications, i.e., a short-lived frontend…]]></description><link>https://felipetrindade.com/ephemeral-environment-frontend/</link><guid isPermaLink="false">https://felipetrindade.com/ephemeral-environment-frontend/</guid><pubDate>Thu, 04 Sep 2025 17:20:32 GMT</pubDate><content:encoded>&lt;p&gt;Today I will cover a strategy for ephemeral environments for frontend (static websites) applications, i.e., a short-lived frontend deployment will exist during the pull request lifecycle, and this will help the developers and testers make sure the code change implements what is expected, without having to merge to dev environment to test.&lt;/p&gt;
&lt;p&gt;For this demo I will use Terraform, GitHub Actions and AWS (CloudFront + S3 for the frontend). In case you do not know how to deploy a static frontend application, I recommend you check two blog posts I&apos;ve posted in the past: &lt;a href=&quot;https://www.felipetrindade.com/static-website-s3-cloudfront/&quot;&gt;Deploying a static website on AWS using S3 and CloudFront
&lt;/a&gt; and &lt;a href=&quot;https://www.felipetrindade.com/serverless-infra/&quot;&gt;Deploying a serverless, cheap, and scalable infrastructure using Terraform in AWS
&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;All the code used for this demo is available in my &lt;a href=&quot;https://github.com/felipelaptrin/ephemeral-environment-frontend&quot;&gt;GitHub Repository&lt;/a&gt;!&lt;/p&gt;
&lt;h2&gt;Architecture&lt;/h2&gt;
&lt;p&gt;The architecture of this demo is fairly simple but has some interesting points to be commented on.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/58c826b160bad208533cf543ee12d244/c7dcc/Ephemeral-Environment-Frontend.drawio.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 115.18987341772151%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAXCAYAAAALHW+jAAAACXBIWXMAAAsTAAALEwEAmpwYAAADXUlEQVR42oVVXW9TRxD1r+An8EP4KTzwyBtCyhsvpX1sJSrUClCR2ipUQEVVFZUHCoEmKFWV1HEbYjt2En9e30/fvd67e3fv6cw6N3UaJ1ln5I3vzPGZmTPjWvDjOsRfHehxDD0Iofo+VC+ASXPAlLBKweYKpSpQ5vpimynUZGcEm1GwLVHqAoaCYTWM0RiOxpjN6BkdYy0uO6XSqEEbpEIgSRIUxiCZxmhtSTTWJPyJjz92n2PvaA1pKmAvAZ0DUirt/TYajb+JzYzAY/zyhcKTFUAQyL2fr+HZ7zdRaEsszeWAnHsQhhgOh46BUjm6ewKNPyNK22Cn9Q6toy3kVIqiKJxPZWVZLge0paWA3Dnwu0pCxIPDee2oMaA/KaXLgJ+z8V1Rw+ZI5TJAiZK+NScLP7mH4fVb7v/qMMhiDZk9f7YUsCgsZtxpepCIDP0Xr9B9uIqM7hcBMsvF1G3OgIT65hvg1y9JcySdOI7R8wN0PA8R3TlVtul0egqQQfhzj/w6+x30B30oMWPZaKw/LfD6cV65UqpkpjxJp2JQHr8cGwJ3qkhTJzmRZShIszWegKlIMQn8uaMDmDNJidVg7GPoJxiPxycM+QvOrWEl7CiayyRJYnR3JPY+UJoih7+7Bu/dI0wjKoX3EdutnxBEI/K15wO22m3U6/VTwl69bSDJL31xC+LOFUCV+K3+Fe7+cBX9yT/gJCQp4wwgdyYkdpPJxDFUOkernmLrfcC7Ad7eJvbffo9cKgJq4v32U2Lo0eQU5wMuSoHFquUUSTByNev2xthpHiGiwnOagjrJ3RVUJq111cfTgOVCoRUL+7P7GN1YoU7P59caGjm6M1CaTpFRR5ctijMM2SmlOg6fv8TR199BHq+uSjYHBwfY2NhAr9c7fzksAnJgGIU4IIk0+32qVYSclgUbl4LT5EUiMnFmMfwHSCz4wsBshopvyYzM3bJlsVamsxmyOHV+HPN/sxRT403NZo1xdVpk6+q3uCBIYjN53NnjuFNGMbXK2Y0PpcSzGQQBJjTPK59+jq3GLkajEQnbR2MgsdkH9AWLu1Y1o03iZjCeGM8bOyYPvn2C/e4hfN+nzZMioh+uzWbIs3AxIKfH20RW6dDhJhxSVxPaONURJJlOu7m0IRXgvx0t6I16JpeSAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Architecture for Ephemeral Environment for frontend apps&quot;
        title=&quot;&quot;
        src=&quot;/static/58c826b160bad208533cf543ee12d244/f058b/Ephemeral-Environment-Frontend.drawio.png&quot;
        srcset=&quot;/static/58c826b160bad208533cf543ee12d244/c26ae/Ephemeral-Environment-Frontend.drawio.png 158w,
/static/58c826b160bad208533cf543ee12d244/6bdcf/Ephemeral-Environment-Frontend.drawio.png 315w,
/static/58c826b160bad208533cf543ee12d244/f058b/Ephemeral-Environment-Frontend.drawio.png 630w,
/static/58c826b160bad208533cf543ee12d244/c7dcc/Ephemeral-Environment-Frontend.drawio.png 641w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;This demo will not deploy a backend application, but usually the static website interacts with a backend application, so that&apos;s why I added it in the architecture diagram. The code for this demo involves the deployment of a dev environment frontend and an ephemeral environment frontend. This is not a must-have, but I wanted to isolate the dev environment from the ephemeral environment, especially because the ephemeral environment will require a Lambda@Edge function to rewrite the path (more on that later), and the dev deployment does not need it (at least in my code it doesn&apos;t). The idea is that the ephemeral frontend environment will use the dev backend.&lt;/p&gt;
&lt;p&gt;Our goal is to have an ephemeral environment (frontend) for every Pull Request opened and access the environment using a dedicated (subdomain-based) URL. So, the idea is actually very simple:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Have a dedicated CloudFront + S3 infrastructure for  the ephemeral environment. This is very cheap!&lt;/li&gt;
&lt;li&gt;Set up an &lt;code class=&quot;language-text&quot;&gt;ephemeral.&amp;lt;DOMAIN&gt;&lt;/code&gt; certificate for the CloudFront and create the domain records pointing to the CloudFront.&lt;/li&gt;
&lt;li&gt;For every Pull Request, build the code and push it to the S3 bucket that serves as the origin of this distribution. Objects in S3 will be prefixed by the pull request number, e.g., &lt;code class=&quot;language-text&quot;&gt;pr-10&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;A &lt;code class=&quot;language-text&quot;&gt;viewer-request&lt;/code&gt; Lambda@Edge function will be used to rewrite the URI to the appropriate folder, i.e., if the user accesses &lt;code class=&quot;language-text&quot;&gt;pr-15.ephemeral.&amp;lt;DOMAIN&gt;&lt;/code&gt;, the Lambda will fetch &lt;code class=&quot;language-text&quot;&gt;/pr-15/index.html&lt;/code&gt; in the bucket (and the same logic applies to all the other static assets).&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Flow&lt;/h2&gt;
&lt;p&gt;The ephemeral environment deployed will have the same lifespan as the pull request, so we are aiming for the following flow:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Developer opens a Pull Request with code changes&lt;/li&gt;
&lt;li&gt;GitHub Actions runs a workflow that builds and deploys the code to the S3 bucket (prefixed by the pull request number). This workflow runs for every new commit in the pull request, i.e. the ephemeral environment will always be up-to-date with the pull request code.&lt;/li&gt;
&lt;li&gt;A comment is added to the pull request with the link to access the ephemeral environment&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/173d64fa32998945210e52081fe164f3/c1c45/pull-request.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 106.9620253164557%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAVCAYAAABG1c6oAAAACXBIWXMAAAsTAAALEwEAmpwYAAADYElEQVR42o2VWXPiVhCFNYuNQUL7hnaBDJYAwZjF4Ik9niQ1lcpLnlKVP5Cq5P//gJNzrwjOTFyuefjqtiTU3bfPuULZ/PYJx9+/YHl7QLs5YvHhgNu7R6x3H+W97cMvqNd38PMGxmgGMywwtENoVvAiSvJUI/ncwA5SOGEGw41gevEZPahgeCnMUQUzaZiwhP5aQs/LkRcNiukCli+S5gjTChYLuCPGyRhOkDHOEMQFAl6bXsIuR884J1hIOSxnSGcbLI6/Ytw+Ip4dkNwckM8/Iqi2MNMVrGz9DaszZtrCr3YIpgeYcQ3F8kbwkgrjmzXSyZyVuE03kYj4+0g63BTKwAxgcKs6Z3eluxiYPlQiZyJm9Z2ozKNa3LJIYlOMMBJzKuDFnJGf8GHQ/cgUww5fRfzWClP5vhImhQzCpITPgYvYpgidygkVjuXgdadDuOAlhEsEynZ/QHk9x2Jzjzn9lk5qlLMlaVE1axkH6eSMG5VwouL/sBHhDKVvePC8EKv1HpvjEzaHztSS/Q/S3IeHn3B8/Bm7+08QIg50B5rpQjVcaERlrFl+ZxsxfIeZJ/RhSYrpHHlFX7LrMbsTq6CqWwTlHHbWkiXsvIWTr+AUaxnrbtwlHGie3LuYn0UxhLnFdRd3nE+NsBNPTUf2vNIuQhyZ0LkuYIQxNJdbsTwq5qFvcnUC+oo/4hYlfK57gvBl/Eg+V8o/1vCmJfK6wXy1Q01W23tkFEeMY+h0lc/rK8iEqk4j2z63mfCslhx6LGdqOBF6qoXLgYlL9ZnewJL3/+VKsyUD+lnkUTRW7ukWfBrTYVKhnEoV+3x5yK3rQr3TKhCq9gYGLvs6ixl4ezGA8u4K73tD9IYWE9pdwiDKkZZTOD4FEF8OJjFZzOTsxLXAYmzYgUx6pVnok7cXKt687+MNk16oOhQxm0vN5IshPB4/4TPLizBkN2K1WUAUcdm9c8LlbjQh3Cnxu57GDrVTh4YPYxqj2C+xaPaYtzuyl4Yu6UlNnmlfGlc1v0bc+wqXH1iNh98qEkTbGZLbGvHmBlEzpUATjLJKfmSFN8Wx6sj+wzd+DWKhsoegrbD5+0d8+PMJ7V8PSL4skJT8a7iukbHLdNpwbZBMW4xudhjVe3KH8JqWy0oyJhPYSY5/AM7MH2niU2qeAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Screenshot of the Pull Request created by the developer with link to ephemeral environment&quot;
        title=&quot;&quot;
        src=&quot;/static/173d64fa32998945210e52081fe164f3/f058b/pull-request.png&quot;
        srcset=&quot;/static/173d64fa32998945210e52081fe164f3/c26ae/pull-request.png 158w,
/static/173d64fa32998945210e52081fe164f3/6bdcf/pull-request.png 315w,
/static/173d64fa32998945210e52081fe164f3/f058b/pull-request.png 630w,
/static/173d64fa32998945210e52081fe164f3/c1c45/pull-request.png 824w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;ol start=&quot;4&quot;&gt;
&lt;li&gt;Developer/QA/Tester checks the ephemeral environment and verifies the changes&lt;/li&gt;
&lt;li&gt;Pull Request gets approved and merged&lt;/li&gt;
&lt;li&gt;GitHub Action runs a workflow to delete the ephemeral environment (i.e., deletes files from S3) and the comment in the Pull Request with the URL.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This is a simple flow that is very powerful and can be extended to meet different needs. In this demo, I&apos;ve used &lt;a href=&quot;https://vite.dev/&quot;&gt;Vite&lt;/a&gt; template and deployed it to the dev environment.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/22c813d07369019adcbe79a03861fd6f/0f529/dev-environment.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 60.12658227848101%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAAsTAAALEwEAmpwYAAABPklEQVR42qWSSU/DMBCF8yO4dUnisZ0mJOkSpRWLSClLF7XiACc2CQkOCFRaOMKdn/0YRxWoKgqiPXyyZ+x5fmPbsk9eUD2eotSbwu49wevdIeo/IDy9hzi4RXnnEpXdq5wy4+xfI8w47j5CDN6ghjPo4RxqNIfbn8FS5EIvMHMpXNACRQJaLqMYMnmzXzi8/wcTW0oprKA1jxq/ri2hV7CKCyQcR0CQhFbEjunPQ6wiMVI+Do8StJsBnLABSlJumdYT1CxYYcHP1w9c7GXYOr9B6fmd71Gu61Dz5bvwyhGETbC3Q1SjBiSt6ZC4MEkSTM7GyLoZxqMRJsMB4no9X/u3oOTWarUa0jRFGIZotlpodzp5Tha0XfjKmr+PEAKe5+Vz2uyVVS5k3BmiKEIcx5s5NMVBEHzj+36eK3L4BTG6Y9kCkrAVAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Screenshot of frontend using Vite template&quot;
        title=&quot;&quot;
        src=&quot;/static/22c813d07369019adcbe79a03861fd6f/f058b/dev-environment.png&quot;
        srcset=&quot;/static/22c813d07369019adcbe79a03861fd6f/c26ae/dev-environment.png 158w,
/static/22c813d07369019adcbe79a03861fd6f/6bdcf/dev-environment.png 315w,
/static/22c813d07369019adcbe79a03861fd6f/f058b/dev-environment.png 630w,
/static/22c813d07369019adcbe79a03861fd6f/40601/dev-environment.png 945w,
/static/22c813d07369019adcbe79a03861fd6f/0f529/dev-environment.png 1259w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;When the Pull Request was opened to modify the message, the following ephemeral environment was deployed:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/d1173b5dc30504b6a21d6247a0b016ba/0f529/ephemeral-environment.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 60.12658227848101%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAAsTAAALEwEAmpwYAAABc0lEQVR42pWSS0/CQBSF+yPcCXbehdISsE2Dj0QQBVrKgkRXGGOiLgwJAV0Z9/7t40xJkVCpYfHl9s49c+bezlgnNwsYjq8XsPtzqN4TvMELGrfP8IevkDqvnM1QPZ+hoiGXD/CudN59Axt9QMZLqHgFmaxAh0tYilPkSI1gFHwLYWqCbZAaztlaz4jW/2JyS0qJAkqtkSbm66qok6rA34YbBAhhYFxASa4n4CjXyzJDAS7r6PUDRC0XxGuBh1G2fpCh0qMqx0FNj2o7Lr7fv3B/0cXR3SOO559wtMbRdYNS6n9DIQQopRmEVOFUfZAqhe02YPstUGJv6ka719CcxhhDFEVIkgRxHGM6nSJJY6RpijSJkQwGSMZjjEYjTCYTtNvtbM92p9Zud67rIggChGGITqez+T7VMdAxzw31er3QZenI5vS8A4OpcX3Tea105N2LMbFWq8HzvAzf99FsNrO1bc0Bz2ZtaH5BjhkxN9z3bH4AkaBnlKQoViMAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Screenshot of ephemeral environment frontend&quot;
        title=&quot;&quot;
        src=&quot;/static/d1173b5dc30504b6a21d6247a0b016ba/f058b/ephemeral-environment.png&quot;
        srcset=&quot;/static/d1173b5dc30504b6a21d6247a0b016ba/c26ae/ephemeral-environment.png 158w,
/static/d1173b5dc30504b6a21d6247a0b016ba/6bdcf/ephemeral-environment.png 315w,
/static/d1173b5dc30504b6a21d6247a0b016ba/f058b/ephemeral-environment.png 630w,
/static/d1173b5dc30504b6a21d6247a0b016ba/40601/ephemeral-environment.png 945w,
/static/d1173b5dc30504b6a21d6247a0b016ba/0f529/ephemeral-environment.png 1259w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Check the URL of each screenshot! As you can see, I&apos;m deploying an ephemeral environment for the frontend. You can even check my Pull Request &lt;a href=&quot;https://github.com/felipelaptrin/ephemeral-environment-frontend/pull/3&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Code&lt;/h2&gt;
&lt;p&gt;I don&apos;t think it will be beneficial to copy and paste code from the demo &lt;a href=&quot;https://github.com/felipelaptrin/ephemeral-environment-frontend&quot;&gt;GitHub Repository&lt;/a&gt;. So I recommend that you go over the code there and deploy it yourself!&lt;/p&gt;
&lt;h2&gt;Considerations&lt;/h2&gt;
&lt;p&gt;I think some DevOps Engineers might disagree that what we have here is an ephemeral environment, since there is already an environment in place (S3 + CloudFront + Lambda@Edge deployed beforehand). I don&apos;t agree with this point of view since, in the end, we have an isolated environment per pull request. Also, CloudFront is a slow service, in the sense that it takes a long time to be provisioned, so I don&apos;t think it&apos;s smart to create it on the fly. Also, by having dedicated ephemeral environment resources already deployed, we speed up the deployment and avoid some quota limitations. These services are billed based on usage, so if no pull request is made, then you &lt;em&gt;basically&lt;/em&gt; won&apos;t get billed.&lt;/p&gt;
&lt;p&gt;The frontend code used in this project was a Vite template. In a real-world application, things are way more complex, so you should expect changes in the CI/CD, such as adding environment variables before building the frontend, or end-to-end tests, integration with SSO...&lt;/p&gt;
&lt;p&gt;There are several other ways of achieving the same thing I did here. This solution is not a silver bullet and has pros/cons over other solutions. Be critical and adapt to the situation.&lt;/p&gt;
&lt;h2&gt;Cya!&lt;/h2&gt;
&lt;p&gt;Hope you liked this blog post! Ephemeral environment is a really interesting topic and the strategy to deploy it varies a lot based on the compute/data stack used (e.g. Kubernetes, ECS, Lambda, EC2...). Here I presented an ephemeral environment strategy for S3 + CloudFront (frontend) deployments.&lt;/p&gt;
&lt;p&gt;See you around! 👋&lt;/p&gt;</content:encoded></item><item><title><![CDATA[AWS CDK for Terraform users]]></title><description><![CDATA[This one is going to be a very short blog post since it's a high-level guide on how to understand CDK from a Terraform user perspective…]]></description><link>https://felipetrindade.com/cdk-for-terraform-users/</link><guid isPermaLink="false">https://felipetrindade.com/cdk-for-terraform-users/</guid><pubDate>Tue, 01 Jul 2025 10:40:32 GMT</pubDate><content:encoded>&lt;p&gt;This one is going to be a very short blog post since it&apos;s a high-level guide on how to understand CDK from a Terraform user perspective. This is the guide I would like to have read the first time I worked with CDK.&lt;/p&gt;
&lt;p&gt;I think a good thing when learning a new tool is to bring concepts, ideas, and mindset of similar tools you&apos;ve worked with before. This helps you understand the pros and cons of each tool and how they handle the same problem. So for this guide, I&apos;m expecting you to have experience with Terraform. I&apos;m assuming you have no experience with Pulumi, CDK for Terraform, or tools that use programming languages to create infrastructure.&lt;/p&gt;
&lt;h2&gt;AWS CDK&lt;/h2&gt;
&lt;p&gt;AWS CDK (AWS Cloud Developer Kit) is a framework that allows you to define code in your favorite programming language (e.g. Typescript, Python - check the full list of supported languages in the &lt;a href=&quot;https://docs.aws.amazon.com/cdk/v2/guide/languages.html&quot;&gt;documentation&lt;/a&gt;). Behind the scenes, CDK uses an AWS service called CloudFormation, that allows you to provision infrastructure using templates written in JSON/YAML.&lt;/p&gt;
&lt;p&gt;Because CDK uses programming languages instead of a DSL (domain-specific language) like Terraform, you have way more flexibility to define your infrastructure: create functions, classes, methods, custom logic, apply DRY concepts, code patterns... This is great, especially if the DevOps has a developer background.&lt;/p&gt;
&lt;p&gt;It&apos;s not required to know CloudFormation in order to use CDK. If you know it it&apos;s amazing since the learning curve will be smoother, but I wrote this post supposing you have no idea what CloudFormation is. For now, what you need to know is that when using CDK you write code in your favorite (and supported) programming language and then this code is transformed into a YAML/JSON that will be used by CloudFormation to deploy your infrastructure.&lt;/p&gt;
&lt;h3&gt;Bootstrapping&lt;/h3&gt;
&lt;p&gt;We refer to &quot;bootstrap&quot; as the process of preparing the environment before actual work begins. The idea is that certain setup steps are needed in advance to ensure everything is ready for productive work.&lt;/p&gt;
&lt;p&gt;You might be thinking that in Terraform there is no such thing as &quot;bootstrapping&quot; and you are right. Well, partially. If you want to have a professional Terraform code you need to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Store state remotely (when using AWS we use S3 Bucket)&lt;/li&gt;
&lt;li&gt;Support state locking (when using AWS we used to use DynamoDB Table but this is now deprecated and you should use &lt;a href=&quot;https://developer.hashicorp.com/terraform/language/backend/s3#state-locking&quot;&gt;S3 State Locking&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Terraform calls this &lt;a href=&quot;https://developer.hashicorp.com/terraform/language/backend&quot;&gt;backend&lt;/a&gt;, but it practice it&apos;s a bootstrap proccess: need to have something in place (S3 Bucket with State Locking) before actually starting your Terraform code. This is bootstrapping. I know that some of you might say &quot;hey, but I can start to work locally and only then migrate my state to S3 bucket&quot;... Yes, that&apos;s true but if you start your Terraform code from day one following best practices you can treat the S3 Bucket creation as a &quot;bootstrap&quot; proccess.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 314px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/6fd357942980cf1fe4d45b625a11f54f/5b158/terraform-bootstrap.drawio.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 51.26582278481012%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAAAsTAAALEwEAmpwYAAABjklEQVR42q1Ry07CQBTlP/wAN36BC/0E48aVv+LGjVtN3GlcaTTGRA3xETQCxgcNRI1StLXQQmmkFCxDW1pCy3E6KmDE6MKTnLk3d+aezD03gn9GJDzMVx+JEweCQOD7wZ8au90uZdBjT5DWGfa2HOzuVFlOSBO2bbM8CIKP5j5//aGi5JHj81DkMlzXYSL1ev3bjwaj1dIhlE8hameQ9euvgqL4jNubNNJpDrqus4tarYZ2uw1ZlqGqKjRNQ6lUglKU0fWBOymKhe0xLO5PYOV4CkFY/BQchkqlwkQzmQxSqRQ4jmNMJOMwKg3ErlYxtz6C+c1RrMVm6FSdviCdEGrhHLr20BvLNE0WW60WHMdhnrquC9JssDfSSxxL0XEsH0xiIzkbdiF0gwl6nofU4TQeH2IIHbKaBIZh/LjdENWaAV7gIckSciKPjj8wcuhPMnGEi8trWJbFluJ/PBgGuajiSSyAS98jm5PAU2ZzAswGeRckhNCtNphnHl3Eb7CpBa7rUTtcFj2vTa2wWXwDCpPz0I7Po2cAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Terraform Bootstrap&quot;
        title=&quot;&quot;
        src=&quot;/static/6fd357942980cf1fe4d45b625a11f54f/5b158/terraform-bootstrap.drawio.png&quot;
        srcset=&quot;/static/6fd357942980cf1fe4d45b625a11f54f/c26ae/terraform-bootstrap.drawio.png 158w,
/static/6fd357942980cf1fe4d45b625a11f54f/5b158/terraform-bootstrap.drawio.png 314w&quot;
        sizes=&quot;(max-width: 314px) 100vw, 314px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Differently from Terraform, bootstrapping in CDK is a pre requisite: you MUST bootstrap your AWS environment in order to use CDK. When I say environment I&apos;m talking about an AWS Region in a specific AWS Account. So the bootstrapping process must be performed in EVERY AWS environment that you plan to use CDK. This process is usually a manual process (or in a pipeline) and it&apos;s easily done using &lt;a href=&quot;https://docs.aws.amazon.com/cdk/v2/guide/bootstrapping-env.html#bootstrapping-howto&quot;&gt;aws cli&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The bootstrap process will create a CloudFormation stack (named &lt;code class=&quot;language-text&quot;&gt;CDKToolkit&lt;/code&gt;) to deploy resources that are needed by CDK: S3 bucket (to store files assets - such as inline lambda functions, website files, CloudFormation stacks), Parameter Store (tracks bootstrap environment metadata - used internally by CDK), ECR repository (to build and deploy images locally), IAM Role/policy (Role to perform deployments, upload files/assets to S3, push image to ECR)... In case you are confused, don&apos;t worry. You probably won&apos;t need to use these resources. Just think these are needed resources in order to have CDK working in your AWS environment. In reality, some of these resources might not even be used by CDK, it will depend on how your code will be written.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 221px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/198bf46b62cdf59ab8a6f4ee4dc04ee4/cccdc/cdk-bootstrap.drawio.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 118.35443037974682%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAYCAYAAAD6S912AAAACXBIWXMAAAsTAAALEwEAmpwYAAAEaUlEQVR42q2Va2hbZRjHo/hFEPwgY1+UVWSKIJNt4ESdG4Lih4kiAy/4xU+ibOj8InNfturQOS9zomNzaBl2ZV1r7OzUNU3a2jRp2qRJTnM9uTbN7SQn55Y0l5PL3/eck2TN1gqCBx7Oe97L732f8/6f59Hhf350rVYLaGkflWoZTC4Lhskgl2fAsnlwHKdasVhEUZIgFDhIgkDeBQikvyHLXZjC0imNZrOpdhRYDua/bbBZHaoZJiZhmDRgamoKBoMBTsqNyTkz5pxLsLhdMFotYHkO6xk6nnTEYwmE4xSm7ZdgW9bD5tHD7BxEIh1SN8nncmDyeWQjUcSG9IgNX0N8eAzRwVEkXctgCixSySRyZJ6O4wuQKwAVH8XJK4/i1MhOfDGyC8cH+0CnprTd65pbFZcP9IPPIP7cG4jufw2BrbshDo+rY41aDdlsVjlhATUC9K/+QUC7cfbafpz/8wDOjD2LSMasARt1DUj5sXLgbay+eRiZI/2Iv/AWpLGJXiDXBnoT4+gfehgfD2zFN789rYJD6ekeYNUTRGzvQay+fgj50+eRePUdSPobtwJZ1KuAIzyIowNb8PnVHfjs6mP48OLdxGVTGyh3Txi8fw/C2/chTsDRJ1+BcOX3W1wWeJSKZUiVNFzRUVDR66Bi43DH9ChV2K4c1EVSEaXZBYhECZxpDpLRDDmb08bqdQ0oEE2JotjVUpBOIBZPbyjaDthKZKO/8RfkdRpsNBq3AymvDw6nG5YFO2IrK11IB7RMUZghmpwyGjFtNMFqngMdDG4OzDI5rCSSBLZKIoW9DcjzPPx+P+LxOPJEl6FQCNFodHNgq6WovaWZ0m6Dev3u2M2Q/Rdg6z8ng+6F3QTyEEtrQCmHmnccddoIWTHfdTTLXGeV9ipWUTXRqM1GUJsJo2IIoJEU2rfcAXIsJKLbuuMXsId14Pu3gT/xAAof3Ak5aNRAdU3YsiuFzJZjyO08DWbHKaTuOoLS97NtHcrrgOT2G9QIuGP3gfvoHojf7YX41S7IgYleIJVG7omvke07gcJLF5B//geULlg2AZIMUzh6L/hPH4L47VPgP+mDHDL1Aj0Z9WTsi+cgHBpB4eDPWPtpfgOg4rL9Mth3dZDO7IH05eNg39MRl3uBNXcSyTveR2bbcRRe/hHZR05COjujAasdIK9cCskOwgoJq3MQ5y9BtAygbL2IppjRgCR5NohGmnwZ5WEn1oYcKP3qQuWyAw16g9BTTHlYXoI3koKbXoVUKneloYSYfYnCgpOCLegnaS2FRZ8Xi3QANjeFOasdgihpCVaBSaRWhCIxLDldcJMJbvcyPB4fmZBXgXWyu53A5heccBLw4qILi3Y3fCSdmaYtsC4skZpT0oBKOClhxJDFTsoLjz8IOhSBL0CrYaicrlyuEHcYBIJhMhZVxwLBENIZRp1Lh6OkkPGkuDHQ1UgeUxpKbZFIxIjtXyCKgtqn7KpsqFQ+7ffw6phAjFMqn/JN+pU51WoV/wCd7bhfZFGCQQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;CDK Bootstrap&quot;
        title=&quot;&quot;
        src=&quot;/static/198bf46b62cdf59ab8a6f4ee4dc04ee4/cccdc/cdk-bootstrap.drawio.png&quot;
        srcset=&quot;/static/198bf46b62cdf59ab8a6f4ee4dc04ee4/c26ae/cdk-bootstrap.drawio.png 158w,
/static/198bf46b62cdf59ab8a6f4ee4dc04ee4/cccdc/cdk-bootstrap.drawio.png 221w&quot;
        sizes=&quot;(max-width: 221px) 100vw, 221px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;The process of bootstrapping, as mentioned, is as easy as running a single AWS CLI command:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;cdk bootstrap aws://&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;AWS_ACCOUNT_NUMBER&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;/&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;AWS_REGION&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Well, this is actually the simplest way to bootstrap an AWS Environment but you can use several other flags.&lt;/p&gt;
&lt;h3&gt;State&lt;/h3&gt;
&lt;p&gt;When using Infrastructure as Code (IaC), our goal is &lt;strong&gt;idempotency&lt;/strong&gt;, i.e. running the same code multiple times should consistently produce the same infrastructure. In other words, applying the same code should always result in the same outcome. Behind the scenes, the IaC tool should compare the desired state (defined in code) with the current state (what&apos;s deployed), detect any differences (drift), and make the necessary changes to align them.&lt;/p&gt;
&lt;p&gt;In Terraform, this process is managed through state files stored in S3 Bucket. A state file is a JSON-formatted file that stores metadata about the resources Terraform has deployed, including a one-to-one mapping between each deployed resource and the corresponding resource instance defined in the code.&lt;/p&gt;
&lt;p&gt;This file is crucial because it enables Terraform to track what it created, how it was configured, and how it relates to the desired configuration. In short, Terraform uses three things to deploy infrastructure correctly:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The state file (current known state)&lt;/li&gt;
&lt;li&gt;The desired configuration (your code)&lt;/li&gt;
&lt;li&gt;The actual deployed state (what&apos;s in your cloud provider).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;By comparing these, Terraform can determine what needs to change and apply only the necessary updates.&lt;/p&gt;
&lt;p&gt;As already mentioned, CDK uses CloudFormation, which is an AWS-managed service, meaning that we are not responsible for managing the state (e.g. storing in an S3 bucket) because this is completely managed by CloudFormation. All you need to worry about is your CDK code and CloudFormation will be responsible for managing the resources.&lt;/p&gt;
&lt;h2&gt;CDK Concepts&lt;/h2&gt;
&lt;p&gt;We are getting somewhere! Now, let&apos;s understand CDK core concepts and how it&apos;s related to the code. I will use Typescript for this demo.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 441px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/4cc02bab589940c09a48245f82920e2e/efc6e/Concepts.drawio.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 84.17721518987341%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAARCAYAAADdRIy+AAAACXBIWXMAAAsTAAALEwEAmpwYAAACgElEQVR42p2UW2/aQBCF+f//p0of0iatKhIC2BDwDV8AX9e3GK+BKpDkdHYdICFpH2rp067PnjliZyw6+Mfz/PyMqqpQliWKopCr0P71dPb7PR53jx/Y7XfY/t7CtExYMwuGaUDTNKw3a3n2WY3I6qxWK/Cao+HNp+wf98jTHGVeyv3ffCJDZHXW6zWKOgNbxUhXyTtYJTQGc67DWhpIa/aqnfmotqhzNE2DTtNwmKmOUaTgPh6doWJMWKUBs9AxjlSpnftErcjglCUDnWyGSaJhSuKUTVtSDROmE1Nc9C5wqVweNbG+92ky4xhoZxZC1kOSqkiye0mcKsizO0RMxSLoYxEOELOh1MRZ65vIGlFrpxY4p0BO93ZSA6uih6lb4GoY4HoYwZhneKoVqHaJn2qIH2oC20/xUt9B91JcKxH5AmheKWtFBueyh6dAPwqhuz4RIIiXMtALSHOWUoviBQX2yUfvHoWRV9TUMtAEPwzFoStb8QBu0ofLBhInuSPadZ4pxBB2LLQhcfK5dG6RT7RNXvkwlDENZcwMWvUW2t+zdn/jDHDrKXIvtVe9fRe+16G8DTQSFV52wqVmz9gIDlPwS/mC7ugrbPp1QhNnRx8hau1D4GEoVdaFH9iwXMJzEIYWdlUPc9+F68/hLheIIwtP1Q35qEWuQz4bQeigyrunHh6nXPahzR9oyjGulASmX+CJ00frVvjWD/F9mMAJc7zwAYxFThNOpFdfVKhL6m1mnIYyo8CIdYkRAjaBT4R0jTK/hR+/1RQ85DcI6OykjWStyGg/bLp3UPr0Yc6oJyb1p8WjqTlnmkt754Ov1USG7OF2s8WG/iBaNv9JWy+y/gD02OH1zlG7lgAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;CDK Concepts&quot;
        title=&quot;&quot;
        src=&quot;/static/4cc02bab589940c09a48245f82920e2e/efc6e/Concepts.drawio.png&quot;
        srcset=&quot;/static/4cc02bab589940c09a48245f82920e2e/c26ae/Concepts.drawio.png 158w,
/static/4cc02bab589940c09a48245f82920e2e/6bdcf/Concepts.drawio.png 315w,
/static/4cc02bab589940c09a48245f82920e2e/efc6e/Concepts.drawio.png 441w&quot;
        sizes=&quot;(max-width: 441px) 100vw, 441px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;The core idea of AWS CDK is to define an application (&lt;code class=&quot;language-text&quot;&gt;App&lt;/code&gt;) that contains one or more stacks, and each stack is composed of constructs (reusable building blocks that define AWS resources). Since CDK translates your code into a CloudFormation template, so many terms used in CDK, like stack, originate from CloudFormation itself.&lt;/p&gt;
&lt;h3&gt;App&lt;/h3&gt;
&lt;p&gt;The entry point of CDK is the &lt;code class=&quot;language-text&quot;&gt;App&lt;/code&gt;, which provides a context for all the resources that will be instantiated by the app.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; app &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;cdk&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;App&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;app&lt;/code&gt; will be used to create a stack.&lt;/p&gt;
&lt;h3&gt;Constructs&lt;/h3&gt;
&lt;p&gt;A construct is the building block of CDK, i.e. what is really gonna be used to create resources inside stacks.&lt;/p&gt;
&lt;p&gt;A construct in Typescript is simply a class that expects three main arguments:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;scope&lt;/strong&gt;: The construct&apos;s parent or owner, either a stack or another construct. It decides where this construct lives in the construct tree. For example, you can create a custom construct to set up a common API using API Gateway and Lambda, and inside it, you’d use the API GW and Lambda constructs. Most of the time, you’ll just pass &lt;code class=&quot;language-text&quot;&gt;this&lt;/code&gt; (or &lt;code class=&quot;language-text&quot;&gt;self&lt;/code&gt; in Python), which refers to the current construct.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;id&lt;/strong&gt;: A unique ID for the construct within its scope. This helps CDK and CloudFormation keep track of resources. Be careful with this. If you change the ID, CloudFormation will think it’s a new resource and replace the old one.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;props&lt;/strong&gt;: These are the settings or options you pass to configure the construct. For example, in an L2 (keep reading and I will explain this!) S3 construct, you could set a versioned flag to true if you want the bucket to keep versions of files.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A construct represents one - or more - AWS resources, depending on the level of abstraction of the construct. There are three levels of abstraction for the constructs:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;L1&lt;/strong&gt; (&lt;code class=&quot;language-text&quot;&gt;Cfn&lt;/code&gt;): The lower level of abstraction is using the CloudFormation resource directly (e.g. &lt;a href=&quot;https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-s3-bucket.html&quot;&gt;S3 bucket&lt;/a&gt;). The L1 constructs are also known as &lt;code class=&quot;language-text&quot;&gt;Cfn&lt;/code&gt; and are usually avoided when a higher level construct is available because they are lower level components with no validation, default values (beyond what is default what CloudFront already has), no best practices, hard to maintain.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; cdk &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;aws-cdk-lib&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HelloCdkStack&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Stack&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;scope&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; App&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; id&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; props&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; cdk&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;StackProps&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;scope&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; props&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; bucket &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;s3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;CfnBucket&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;MyBucket&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        bucketName&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;MyBucket&quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;L2&lt;/strong&gt;: Most times you are going to use L2 constructs because they are L1 constructs with default values, best practices, and methods for easily using the resource. Let&apos;s take for example the &lt;a href=&quot;https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_s3.Bucket.html&quot;&gt;S3 Bucket&lt;/a&gt; L2 construct. It applies best practice by default and allows you to use several methods for managing lifecycle, and access (read/write access to Roles)... You can think of them as Terraform community modules for simple resources (e.g. &lt;a href=&quot;https://registry.terraform.io/modules/terraform-aws-modules/s3-bucket/aws/latest&quot;&gt;S3&lt;/a&gt;, &lt;a href=&quot;https://registry.terraform.io/modules/terraform-aws-modules/kms/aws/latest&quot;&gt;KMS&lt;/a&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; cdk &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;aws-cdk-lib&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; s3 &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;aws-cdk-lib/aws-s3&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HelloCdkStack&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Stack&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;scope&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; App&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; id&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; props&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; cdk&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;StackProps&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;scope&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; props&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;s3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Bucket&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;MyBucket&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        versioned&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;L3&lt;/strong&gt;: The highest level of abstraction. L3 constructs implements common AWS patterns (API Gateway with Lambda, ECS with Fargate Service and Load Balancer, S3 with CloudFront...). Usually creates several resources and are also equivalent to Terraform complex modules (e.g. &lt;a href=&quot;https://registry.terraform.io/modules/cloudposse/cloudfront-s3-cdn/aws/latest&quot;&gt;CloudFront S3 CDN&lt;/a&gt;). But nothing stops you from building your own construct (e.g. an S3 bucket with compliance that should be used as a &quot;default&quot; bucket in your CDK code). You can find several L3 constructs in the &lt;a href=&quot;https://constructs.dev/&quot;&gt;Construct Hub&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;CDK Deployment&lt;/h2&gt;
&lt;p&gt;There are several methods to deploy your infrastructure, such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Manual Deployment&lt;/strong&gt;: Easiest and simplest to start working with CDK. You simply run &lt;code class=&quot;language-text&quot;&gt;cdk deploy&lt;/code&gt; and that&apos;s it. Well, you shouldn&apos;t use this approach when working in a team. Ideally, you want to make sure everyone has visibility of the changes so usually a CI/CD system is the way to go.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;CI/CD Deployment&lt;/strong&gt;: You can use your favorite CI/CD tools (e.g. GitHub Actions, CircleCI, GitLab) to set up pipelines to deploy your CID code.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;CDK Pipelines Deployment&lt;/strong&gt;: I&apos;ve made an entire &lt;a href=&quot;https://www.felipetrindade.com/cdk-multi-account/&quot;&gt;blog post&lt;/a&gt; to talk only about this method. We can use CDK to define our CI/CD using AWS Codepipeline. This allows us to have CDK as the truly single source of truth in our infrastructure!&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Cya&lt;/h2&gt;
&lt;p&gt;Hope you enjoyed this blog post! It was very short but this would definitely save me hours from reading the HUGE CDK documentation. From now on I expect you to have the essential CDK knowledge to get started with CDK. See you in the next post! 👋&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Set up Headscale with own DERP server in AWS]]></title><description><![CDATA[Some blog posts ago I talked about bastion host and private resources. I always mentioned VPN but never wrote a blog post about how to…]]></description><link>https://felipetrindade.com/vpn/README/</link><guid isPermaLink="false">https://felipetrindade.com/vpn/README/</guid><pubDate>Mon, 02 Jun 2025 10:40:32 GMT</pubDate><content:encoded>&lt;p&gt;Some blog posts ago I talked about &lt;a href=&quot;https://felipetrindade.com/bastion/&quot;&gt;bastion host&lt;/a&gt; and &lt;a href=&quot;https://felipetrindade.com/public-private-apps/&quot;&gt;private resources&lt;/a&gt;. I always mentioned VPN but never wrote a blog post about how to deploy one. Today I&apos;m finally doing that. The goal of this blog post is to deploy a VPN (Headscale) and access private resources deployed in AWS.&lt;/p&gt;
&lt;h2&gt;Hub-Spoke and Mesh VPN&lt;/h2&gt;
&lt;p&gt;A VPN (Virtual Private Network) server is a mechanism to allow secure connection to public or private resources (such as corporate network, internal systems) using a public network.&lt;/p&gt;
&lt;p&gt;Traditional VPNs follow the &quot;hub-and-spoke&quot; model, i.e. all clients connect to a central gateway and this gateway forwards the traffic to the target destination. This model works well and it&apos;s still heavily used today but it has some downsides:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Multiple locations&lt;/strong&gt;: Modern and big companies have way more than a single place/location of interest. They usually have multiple offices, cloud providers (AWS, Azure, GCP...), and deployment in several regions/AZ. This means that several tunnels will need to be created among these locations which can be a pain to maintain and configure.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Latency&lt;/strong&gt;: If all the traffic needs to go through a central gateway server it will increase latency and affect the user experience when trying to connect to the resources. Maybe the user is physically located close to the resource it wants to access but the traffic will still need to go to the VPN gateway (that might be far from the client).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Single point of failure&lt;/strong&gt;: If the VPN gateway goes down, the traffic also goes down and this means no connection to the resources.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 521px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/91c01852b41c56ecba6b0c43c003197a/bb9c5/hub-and-spoke.drawio.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 61.39240506329114%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAAsTAAALEwEAmpwYAAACkUlEQVR42nWSSU9TURiG+z9cuzIxuiBx64KNSxMTdStuXBhdOCwkgWgwBlG0FCwimujOGEASZZLRIi2DQ2kr0OLU0lo7c+fp8fT2hsjCL3nynXNz8t7vPe/xYdtgm7j9n4rH48RiMbeHw2EikWWi6zE+LC0Rja67Z2xnP47Ax3+qWCyys7PjkslkSP/8QT6XpVwqUqtWvVNOXdbrjfIZtRxmPo5ZzfzzeX/V/6xZDqpANmwk3aaiWNQMh11hrir2ZdlC0R18hambJNoPkBm6hKToyLLkIkkeuzVKVYn742lujWzTMZLkwWyZ/lXH7V2TeQIhCf+SzWxSwyd/j1AJP0FKLXgT7p/TtkzKNZmBFZPeZegNWzx8r9A5WeDE2cscbTpOy83nBKMwvSUEizN32Lp9kOzo1X0TyrLsoiqyKxhctgiuwqOIRU9IIbgCbS8TXBv8QOdYhoDYz6YMfNVUiOx8gPLGu70JHaexkoRgKpXk5680r9YqQsShTwgHFlX6xbpjOEnri1W6JnINwWRdcKGL9IPDFMZvIGsGqqqIqRQ0TXMTTiS+8m07xcfYFn3vK3TP1eieKdH1rkDzqQscOnyMc23P6F/3LFc250hP3aOw/hZNN9F1zcUwDGFZISeeSvb3HwbCCn0rdcsmPYsKjz/S4BMMiO4Pe5ZroYfkepsoTbWjGtaeoK7rLqahu3c4uGaJZCEo7rCeaiAk09L2lJPnW7n+eI7+zzRSrmzO8n38LoXoG0zLxjQNgelhoYkrKJRrbhj1KXrC4rksGnROVzhzpYfm05e46J/AvwZTm0KwNH+fH91HyI+1ogrLmqZ6aB6qm/5oTGMoZjBcJ27yOmExuunwegtGNyxefjFYS2v8BXYHUg4lfRNMAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Hub and Spoke Diagram&quot;
        title=&quot;&quot;
        src=&quot;/static/91c01852b41c56ecba6b0c43c003197a/bb9c5/hub-and-spoke.drawio.png&quot;
        srcset=&quot;/static/91c01852b41c56ecba6b0c43c003197a/c26ae/hub-and-spoke.drawio.png 158w,
/static/91c01852b41c56ecba6b0c43c003197a/6bdcf/hub-and-spoke.drawio.png 315w,
/static/91c01852b41c56ecba6b0c43c003197a/bb9c5/hub-and-spoke.drawio.png 521w&quot;
        sizes=&quot;(max-width: 521px) 100vw, 521px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;What if we implement a peer-to-peer design? Where every node could directly reach each other? That&apos;s the Mesh VPN! This concept is not new but lately, it&apos;s becoming more popular. It was not a preferred approach of the IT team due to the difficulty to deploy/configure/maintain, resource-intensive (especially for devices with limited CPU/RAM) but lately Mesh VPNs (such as Tailscale, Nebula, Zerotier) started to grow and become popular. In a mesh network, every node would need to create several tunnels and know how to reach other nodes (i.e. their IP in a world where devices rarely have static IP addresses). The first problem can be solved by using a fast, modern, and lightweight VPN tunnel, such as &lt;a href=&quot;https://www.wireguard.com/&quot;&gt;WireGuard&lt;/a&gt; (the big brain behind Tailscale/Headscale). The second is solvable by adding a new component: &lt;strong&gt;control plane&lt;/strong&gt;. The control plane (also known as the coordinator server) is the main contact point of every node in the mesh VPN. The control plane is responsible for gathering all public keys of every node in the mesh network and sharing them with all the other nodes. Please notice that the control plane does not forward traffic or act as a main gateway. The control plane only provides information/metadata about the network!&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 521px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/e88cc53c739446a53fa24edc7627744f/bb9c5/mesh.drawio.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 62.0253164556962%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAAsTAAALEwEAmpwYAAABoUlEQVR42oWSz0rDQBDGfRXxiRQ9iQ/iyYMg3gSvIuJBT556EUQQRMFarQdBSrWiJtVIu9lkN3+2ySafO9GmrUZdGEJ29/t25jczhYqV53nx1Vrj/PwMjfoFrq8aqNfr+G9N/XWY5xnee330vAAiVPClLB/705AyGQwGSJKkDPpPNaBcC72jlSJi9ow0A5KKu+RRGkZRBMZYEZzzIlyXwZMRXu4u8LCzgPvteXRbl2YvBHdH94Y68igNVRRCSh+ZTn+UkGUZhJAIwqiyRNKQVo0bxnFsNoNC/NNQI5ACKv7F0GhIq5QaGb42a7BPt8CfmlBpjoE5JC4U9BiVJIQoeRVh7iQGm+zewjJa56b2ZWg6ae8vobM2je7JJtxAg7P+BCPbtuE4DjzPK/e52weTCazjDTyuz8DaWxxl6HIPzOVIUl1ZcsHol5JJwxg94o81xaQvzYxVMdTfGFUzlJMMY9MhacATryEjukAzx+w2WgfLsA5X4b91DLdxxslnQwU1LZ7MkKCXfIZzKEK8tRvo7s6amIPTvixmc3wOKXzfLzP8AB/wlmJxf9gCAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Mesh VPN Diagram&quot;
        title=&quot;&quot;
        src=&quot;/static/e88cc53c739446a53fa24edc7627744f/bb9c5/mesh.drawio.png&quot;
        srcset=&quot;/static/e88cc53c739446a53fa24edc7627744f/c26ae/mesh.drawio.png 158w,
/static/e88cc53c739446a53fa24edc7627744f/6bdcf/mesh.drawio.png 315w,
/static/e88cc53c739446a53fa24edc7627744f/bb9c5/mesh.drawio.png 521w&quot;
        sizes=&quot;(max-width: 521px) 100vw, 521px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;The client in the mesh VPN can connect with its peers by using several advanced network techniques such as &lt;a href=&quot;https://tailscale.com/blog/how-nat-traversal-works&quot;&gt;NAT Traversal&lt;/a&gt; (basically the VPN client will perform a &lt;a href=&quot;https://github.com/felipelaptrin/hole-punching&quot;&gt;Hole Punch&lt;/a&gt;)  and &lt;a href=&quot;https://tailscale.com/kb/1232/derp-servers&quot;&gt;DERP servers&lt;/a&gt; (when NAT traversal is not possible to be done the traffic goes through a public server called DERP - the connection is slower than doing a NAT Traversal but it&apos;s better than not having the connection!).&lt;/p&gt;
&lt;h2&gt;Headscale&lt;/h2&gt;
&lt;p&gt;In this demo we are going to deploy the following infrastructure:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/6ef43ace1d7b506b7d0e3f285217fb25/e35ec/Headscale.drawio.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 95.56962025316456%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAATCAYAAACQjC21AAAACXBIWXMAAAsTAAALEwEAmpwYAAAC+klEQVR42pVU22oUQRDNp/kN/oZPvom/IAii4IsSUFC8IKggyZMXFIMaYtDEGNe4u+7s7Oxcdmb63j3dx5rZJK6rEW0oamen5nRVnVO1Uj3/CDeZoakEvHEIfzPrupg/WbANvLJYcUJjmuZwFIwQ5rZw2idP/4Xud1h689N8+5aAV4L1EJphZ7iOUdlHWo/BZIaajOscXBVQpqSYgu6yWI/G2JmmiJhErjQyrlFa213qO8A2ZbqjyPeQrp6G6L+G9Q20nIFXMxRZgt63LxgnAzgnMNEWB85jKBQiTeY1JlLNq2gBu/oJkPMY355cAov2wRsgYwaGSwwPBtjd/YxoPIBuNOpsiPzROSQixnBLoHd5jKnUCyV3gJ5KTCl9ARDYrf13uLr3uuuUD77zVZ1DOYVZNsLB7bOIWYTe2xLbF78SuFoGDNS3KbTm3ccFzzBKDjBIDW5vJLj7JkM/yYi4GplpsFNIjAhkqDh6qvy15KMMGWXIjIJ2GoZ6U80y7H9P8HAzwuOtGP14DGsZatcgJTJSTYQYQ96gIL8ECDBiOLt/Bqz/FtJZGCugVE1ZMVhSQZZHkOTnhwo8ktKCpPwiKaweoX/vPGzSw4tXG7h54wYklRLHCTy1MaeMlasgS2DjTgDdNVdjOFLlgmyOSIlnrOUEz54+xerqKpXokKZZp92qKuACx2zqsHatRJXbvwESKSKhoAGEmFE2xHg+gZAVOFlVF8jLGM4ragMxzTIIJZcm6hjQzkvO95FeOQU92gQNIX3ICZx13jmDmpg3JOyTzm89rNkYX14+QDGJuoD1TxIX1mI83jUU7cFFeQwYlub9jz2sxJSkI2HaZxq974XGhxHHIFMdIOMFAfJ/y7BVuXQ1MZ1CW8rEl1RqBWsqaPJtdm3Jjub2pOOPM2w8fKXg9pLunkkv4P3aPKRp3/lWc4fmw8nW6pFUsdKKTOU10mefEGj4v24brF9nh3vu/04HGGjLWmJa0whZSWy2K6tIoGjfUY3dJv5Xazf2D9UhtNDirIinAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Infrastructure to be deployed&quot;
        title=&quot;&quot;
        src=&quot;/static/6ef43ace1d7b506b7d0e3f285217fb25/f058b/Headscale.drawio.png&quot;
        srcset=&quot;/static/6ef43ace1d7b506b7d0e3f285217fb25/c26ae/Headscale.drawio.png 158w,
/static/6ef43ace1d7b506b7d0e3f285217fb25/6bdcf/Headscale.drawio.png 315w,
/static/6ef43ace1d7b506b7d0e3f285217fb25/f058b/Headscale.drawio.png 630w,
/static/6ef43ace1d7b506b7d0e3f285217fb25/e35ec/Headscale.drawio.png 861w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Notice the following components:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Headscale Control Plane&lt;/strong&gt;: Tailscale control plane is not open-source and is a hosted service run by Tailscale Inc. The open-source version of it is called &lt;a href=&quot;https://headscale.net/stable/&quot;&gt;Headscale&lt;/a&gt;. We are going to deploy the Headscale in an EC2 server exposed by a &lt;a href=&quot;https://caddyserver.com/&quot;&gt;Caddy&lt;/a&gt; web server that is going to automatically manage and issue a public certificate using &lt;a href=&quot;https://letsencrypt.org/&quot;&gt;Let&apos;s Encrypt&lt;/a&gt;. Because we don&apos;t want to keep connecting to this EC2 to have access to the terminal to run the Headscale CLI to add/remove clients to the VPN we are also going to install &lt;a href=&quot;https://github.com/GoodiesHQ/headscale-admin&quot;&gt;Headscale Admin&lt;/a&gt; a simple GUI to interact with Headscale via an API Key. Please notice that the Tailscale client is in fact &lt;a href=&quot;https://github.com/tailscale/tailscale&quot;&gt;open-source&lt;/a&gt; and used to connect to a Tailnet (Tailscale Network).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Subnet Router&lt;/strong&gt;: The subnet router is a Tailscale client that will serve as a router to private networks when you can not install a Tailscale client in the private resources. A good example of that is an RDS instance. We do not have access to the operational system of the RDS, so we can not install Tailscale there. So the solution to this is to deploy a Tailscale client that has access to the RDS and route traffic to the RDS. The best practice is to deploy the subnet router in a &lt;a href=&quot;https://tailscale.com/kb/1296/aws-reference-architecture#production-best-practices&quot;&gt;public subnet with a public IP address&lt;/a&gt; to ensure direct connection and optimal performance. If you deploy the subnet router in a private subnet it will need to use the AWS NAT Gateway which is a &lt;a href=&quot;https://tailscale.com/kb/1296/aws-reference-architecture#aws-nat-gateway&quot;&gt;Hard NAT&lt;/a&gt; that will prevent direct connections (forcing it to use the DERP server - which is slow).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;DERP Server&lt;/strong&gt;: Ideally you should deploy one DERP server per region to have multiple DERP servers available and minimize latency to the clients. In reality, the DERP server is not required in this setup but it&apos;s highly recommended to deploy your own DERP server. If you do not use your own DERP server, it will use a Tailscale DERP servers, which means the traffic will go through the Tailscale infrastructure. The traffic is encrypted, and Tailscale won&apos;t be able to read the packages but if you came to the point of deploying your own control plane, then you probably want to keep everything on your side and control all the traffic.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Private Hosted Zone&lt;/strong&gt;: As we already discussed in previous blog posts, it&apos;s not recommended to expose dev tools (e.g. ArgoCD, Grafana...) and ideally your developers should access them via VPN. To be one step extra in the best practices, we can deploy our own internal hosted zone, i.e. a DNS that will only work for the hosts that are within the internal AWS VPC. In other words, a hostname like &lt;code class=&quot;language-text&quot;&gt;grafana.internal&lt;/code&gt; will only be resolved by those that are within the VPC. A common alternative scenario is to have a public hosted zone that points to a private IP, i.e. anyone will be able to resolve the domain (e.g. &lt;code class=&quot;language-text&quot;&gt;grafana.mydomain.tld&lt;/code&gt; but only does that are internal to the VPC will be able to have network access to it). Here we are doing an extra security step by deploying our own private hosted zone.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Private Service&lt;/strong&gt;: To simulate a private service that should only be accessed by those devices inside the VPC, I will deploy an NGINX server in a private EC2 instance.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;RDS Instance&lt;/strong&gt;: A database will also be deployed in a private subnet.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Hands-On&lt;/h2&gt;
&lt;p&gt;There is one thing that you should have in place before the hands-on: make sure you have a hosted zone configured in Route 53.&lt;/p&gt;
&lt;p&gt;Since the code for this demo is very big I won&apos;t be copying and pasting it here, please check it in my &lt;a href=&quot;https://github.com/felipelaptrin/headscale&quot;&gt;GitHub Repository&lt;/a&gt;.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Install dependencies of the repository&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;devbox shell&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;Initialize Terraform&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;cd&lt;/span&gt; infrastructure
terraform init&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;Create a file to define variables&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;You can check the README of the repo to see the required variables (or check variables.tf file directly).&lt;/p&gt;
&lt;p&gt;Here is one example of a valid file (modify it for your use case)&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;hcl&quot;&gt;&lt;pre class=&quot;language-hcl&quot;&gt;&lt;code class=&quot;language-hcl&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# vars.tfvars&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;aws_region&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;us-east-1&quot;&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;domain&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;mydomain.tld&quot;&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;headscale_acme_email&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;me@mydomain.tld&quot;&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;headscale_hostname&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;headscale.mydomain.tld&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Once again: make sure the domain is already deployed as a public hosted zone in Route 53.&lt;/p&gt;
&lt;ol start=&quot;4&quot;&gt;
&lt;li&gt;Apply Terraform and create the infrastructure&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Remember to have valid AWS credentials in your terminal!&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;terraform apply --var-file&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;vars.tfvars&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ol start=&quot;5&quot;&gt;
&lt;li&gt;Wait until Headscale gets deployed&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The user_data script will run in all EC2. You should wait for the user_data of the headscale control plane gets ready. You can check if the admin (headscale community console) is available. It is available in the &lt;code class=&quot;language-text&quot;&gt;/admin&lt;/code&gt; route, i.e. if you defined &lt;code class=&quot;language-text&quot;&gt;headscale_hostname&lt;/code&gt; as &lt;code class=&quot;language-text&quot;&gt;headscale.mydomain.tld&lt;/code&gt;, then you will need to wait &lt;code class=&quot;language-text&quot;&gt;https://headscale.mydomain.tld/admin&lt;/code&gt; to be available.&lt;/p&gt;
&lt;ol start=&quot;6&quot;&gt;
&lt;li&gt;Access Headscale Admin&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Use the API Key generated by the Headscale server. You can get the API Key value in Parameter Store via AWS Console or via AWS CLI running the command &lt;code class=&quot;language-text&quot;&gt;aws ssm get-parameter --with-decryption --name &quot;/headscale/headscale-admin/api-key&quot; --query &quot;Parameter.Value&quot; --output text&lt;/code&gt;. Refresh the page when you save the API Key value.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/2e91a625683ac9ac07232692b64bf5d5/6c837/login-headscale-admin.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 50%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAABYlAAAWJQFJUiTwAAACIklEQVR42m3Re0/aYBTHcV7F/ly41rZPS0uBUi5S7hcRGDgEHUZE3Igx05npsssb2N70d4fNuWTZH5/8epqek+c5jVTWNxTWWyqza/zKGqd4hhMsSftvcITrn5LxT/BENr8gl5+TF74o7DI3oxIsqFVXNAqnROzJHbHRFrd3S6f7je7gM+3+A7o6QdOmmNoESxvhJg/xEj1y8Q5BvEU53iSMtxmaY47UlJk5ZWK+IpK2r3GF426xMxvS3gbdXtKbbjjfPjJf3bK4uONk/YGztw9PPnL+7pHTzT317IiRmjC2phwoGWh5l1jeGsO9QHdXmJkVmjpmOLvi4vqTDHzPYn3Lm6t71jdfnl3efGW5faCSG9IxR3StMU01JKLcM2x3SegtqHtzcu6ctD1FuTWMXAMjW0f36iTsClFV/MsMeBF3cIw6dWtEaA3ZVwMihnPMjuu8FkdYQpMPvPKAcnMqP6ctJ68S1QtEjUD8yYCXho+tGhTtQwJ7gG8dENlLj9mzx2giZY8kD0lZfZKZkPR+D91vEHXKxNwy0XSJhLcvdYl4Rk4s73SrTsY+EH0cq0dEk+aUPOxoVleyS1K1MEtt7EafWD5kr9xCKzdJFEJUrSdZk7pGqhTKKmQ10rOjqw6RlNXhXwmzhVPokAm6mF4DM9sUDZSkIftUuRZecS6O5Vat576kav9/4E7UkCuapV9iT37XAXFVoxp+p1r78bSi9vPAn6qTV5nHi0WWAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Screenshot of Login page of Headscale Admin panel&quot;
        title=&quot;&quot;
        src=&quot;/static/2e91a625683ac9ac07232692b64bf5d5/f058b/login-headscale-admin.png&quot;
        srcset=&quot;/static/2e91a625683ac9ac07232692b64bf5d5/c26ae/login-headscale-admin.png 158w,
/static/2e91a625683ac9ac07232692b64bf5d5/6bdcf/login-headscale-admin.png 315w,
/static/2e91a625683ac9ac07232692b64bf5d5/f058b/login-headscale-admin.png 630w,
/static/2e91a625683ac9ac07232692b64bf5d5/40601/login-headscale-admin.png 945w,
/static/2e91a625683ac9ac07232692b64bf5d5/78612/login-headscale-admin.png 1260w,
/static/2e91a625683ac9ac07232692b64bf5d5/6c837/login-headscale-admin.png 2962w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;You can also get the API Key from the AWS Console.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 580px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/19e531a4687c9e70e619dba9c5741d43/b6272/parameter-store-api-key.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 106.32911392405065%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAVCAYAAABG1c6oAAAACXBIWXMAAAsTAAALEwEAmpwYAAADBElEQVR42qWUaXMaRxCG9xfEchCLEDeCPdgDdtmLa7klQEKyZZFSkqrki///X3j9zqDFqdhOSOXDUz3TPdvT3dO9ypXWhWqFUDsBVNNHwU2Qr2nIVTWodkRbIG2XxRrybVfaC3aIghPL74o8U/FHqPTHKLVsKLrTR3q3RzS7RULi+QbD2RrxeIHBcosoXSGertHrJxjSFnIt9lG6RsK95Q9QqOsoNk0UyjdQ2rqD+XyN8TDFJF1gOJ7DD4YYp0ukMzobTJAMp4iJ0MXJBJPpEiOeS/hNxH0+X4ZaqEC9qkLJcXHB9HJM5ye1KpV2L8RVqYGcWoFmdvH+8hoXuSLekQu5zijifb4EleXIUMq1FnQvgRGmuDF7KFdbsLoB6jcmikzBtH3KprzgqpTJrxSuG3RUP6FUmwZMy4NuONA7PdRbFkqMuMy6CNs1LxDyXJRyXcN6+4Td4ws2Dx/JM3757U+4fsIDJpqag0bbPhul0tDRZmQto4s269UyXMqelBlCfy5KQ7MwWbEdRlO4/RjdYAA3SCgTKV22i8VH6rCu/05Ih+0OZustVrtHLDd7yeLuHvPbnWS5eZCXGI7PD/ow3SPCQbb+q0469NlLuuMx9a6kZbrQrB4sL2J0EWw/huH60FgaoReIc0fZg257Xx3WWyZmjGTEJh5Oj6SckH4yZv+pyJF3P1/C8UNmsqPtDilLlC43mK4EW4Rs+mMGbw43+2c8fDyQFww4DV48gs3oyrWmpFRtsNkDBJwaPx7zssmJYJDKOp8cipQdKkTYtncsvkhFHBCpZoi90ItzgmPq3kl3SrnGidh9OGD/6Vd5uzjwvYKfw5tDA3f7T3j94zMiDvz/dihSDsczmNwYb8q/898ctjrypVbsvSlfMKtVRlafH/XeNw7rjNALEzi3Txg8HI7/vsmc7bNEMlnAi0ay57KHOCNlC2tOyer5FfeH37Fn+9yTp5dXPB1esX18fuu5LcaL238cQ2FTKk39uOEkCDKDxb4TbeT4EYnheJFctzuunKQf8QVqeooeIftKGAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;API Key from AWS Console&quot;
        title=&quot;&quot;
        src=&quot;/static/19e531a4687c9e70e619dba9c5741d43/b6272/parameter-store-api-key.png&quot;
        srcset=&quot;/static/19e531a4687c9e70e619dba9c5741d43/c26ae/parameter-store-api-key.png 158w,
/static/19e531a4687c9e70e619dba9c5741d43/6bdcf/parameter-store-api-key.png 315w,
/static/19e531a4687c9e70e619dba9c5741d43/b6272/parameter-store-api-key.png 580w&quot;
        sizes=&quot;(max-width: 580px) 100vw, 580px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;ol start=&quot;7&quot;&gt;
&lt;li&gt;Authorize Subnet Router route&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Go to the &lt;code class=&quot;language-text&quot;&gt;Routes&lt;/code&gt; session and authorize the route created by the Subnet Router.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/a561f8662a5f5e37dd2bc362ba6d091b/73dae/routes.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 43.67088607594937%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAYAAAAywQxIAAAACXBIWXMAABYlAAAWJQFJUiTwAAAB7UlEQVR42nXR7U/aUBTHcf6NZdlMfECo7e1tbWlLqSAPOsGqIIIYEeZDjBvI3Mw0bkv2n393ZL5ZzF58cs/NSX45957MbvcLjb3PNDszirVzDO+IVedwfpqF/oveK9Z/ZDaqjzj+TExJD35T235gJ30iSibk1Qilx9j6DEef4jmn+PqEQA+I7GNi1SNRXcrqkKpqUxeZUvkR5d5iORN29n/RbP+k1flBeesrln+BE1ziFC5w10e41jEFe0A4D+uTWF0qVodNS8LMfbZFplp7QOtPKPsaN5rhxjJtNMUtTvCTGV5pSlSZ4Uc3hP4VvjPEV0eEMllRwuK1fRIjpWLsUjP3yOTvbnkzueDt6COF+DtR6Yk4eSKI7vGCb3jRHUn5niCcUa9IvyChuivPPiTSbcrrXWqecNsyqQQGB1OylRHZZExh45LS5o2EnOMVX0Rj3HCI9yw4IQwHxNGAJOqzERyRNsZ0W1ek1TMqWp5cjK5R1pA1c4jye9hhyopqsGTVX9RYNGvk3TpZqZesKsvWJiumkHPZrLC8Vp7fizolkzV75Ow+hmwv76QsqoQFo8T7fPwPs9DAkNB3ueKr3oIRk5dwT7fkD+0uOVn7M8PpSPgeOf0slXpXtP7STfGBVbUttuZyUhvCFLa9g1ZN/gCR1ToNTAx1bQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Screenshot of Routes allowed by Headscale&quot;
        title=&quot;&quot;
        src=&quot;/static/a561f8662a5f5e37dd2bc362ba6d091b/f058b/routes.png&quot;
        srcset=&quot;/static/a561f8662a5f5e37dd2bc362ba6d091b/c26ae/routes.png 158w,
/static/a561f8662a5f5e37dd2bc362ba6d091b/6bdcf/routes.png 315w,
/static/a561f8662a5f5e37dd2bc362ba6d091b/f058b/routes.png 630w,
/static/a561f8662a5f5e37dd2bc362ba6d091b/40601/routes.png 945w,
/static/a561f8662a5f5e37dd2bc362ba6d091b/78612/routes.png 1260w,
/static/a561f8662a5f5e37dd2bc362ba6d091b/73dae/routes.png 2122w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;ol start=&quot;8&quot;&gt;
&lt;li&gt;Create a new user for yourself&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Give it a name and issue a PreAuth Key, we will use it in the next step.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/9760a37363af5f4013a822dc41f91568/e8950/create-user.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 82.27848101265823%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAQCAYAAAAWGF8bAAAACXBIWXMAABYlAAAWJQFJUiTwAAADD0lEQVR42oWUaW/aWBSG+RujNhnSJBDb2Aa84AXMZrZAgZCF0Eyapc1SRWmWRtN+6LeRRppf/cyxQzujapYPj64x3Oe+55wrMnHrI4PuI/XaDe32LZXmGZq9QCkvKDhv0J0FxhLTOaTozCnac0r2gbCPZSXsYQuOkNE/PLH1/p6fji9Z3bliNPyD8eR3RuOvmOULDOOUknGCbRxT0Y8I9AU1fU5d36elz+gUpgwKE4bCRNshY58/YZ19Irt/RW5yzdvf2owvd/EbTzj1S6zwPXZC8A4nPMcVPOeIWmFXpDOa+lSkE/qpeEqmGf5K5H2iqFxjalfE8yPCwQWO/1k2P2D79yK6pxo94gcfaUQP1CqX+NpUpFMidURDGdJWX9MTcaYePxI279DK52ilcxz7C17whTB6ohLcYXu3uP4NzcYdofeBbnRLK7ggLM2oFSWdu8coWLDt7tM1JWGj80AQ3aAWT1HNE7zonHp8Tdk7xfJOsPy3wi/Y7oKKd4gnG4PKHnX/gLq7y6h1zHz7gr34jE55RsYNL0jQisco5hEV/x2h9M4OTr/LLH8hHErauUj38Su7hJUZkaw1Z4fI3mEQvaFpyVDy+gEJijmXhHJVrJlcmT7rerykLbS+s6E32VySW6KYbakwxjWHZBRzj4QtYzddlfIQ1e2wqlZZUcL/5UXeZ02vkyu3KZr9JOGEZ8YinYhwgB2NWTearCpVflZr/45WI1+OUewO5eoI/Vk4lNgJ2yTPibAYDP9b9I18mMqSFm0UuihGj8xmocdf9NMvrdrr9ORVKSkpPUn6XGKQPqdoVdb9LpvFDmpYwRqWyJmpUF4uyek9eSk/cLpCj2whIitlZbWIV8lQjJg1rU5W0iXCrCMDM2QggU+5b5Mz/kGYlxPNYJtSkJQfsyG9zBVb6O6Ukj8nV+qIpMHKlqTNB+n6MlflxWZNwnR/FEov5MQVNUxTbKoRL7eSTaGsnkzUSwWpLGlHynM71pIB/Vhy0kfV6sufwoSCO5ApRqwV6imvCo2Ub5//TtIaxYzlLg74E43KGtvu6/5hAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Screenshot creating user in Headscale Admin panel&quot;
        title=&quot;&quot;
        src=&quot;/static/9760a37363af5f4013a822dc41f91568/f058b/create-user.png&quot;
        srcset=&quot;/static/9760a37363af5f4013a822dc41f91568/c26ae/create-user.png 158w,
/static/9760a37363af5f4013a822dc41f91568/6bdcf/create-user.png 315w,
/static/9760a37363af5f4013a822dc41f91568/f058b/create-user.png 630w,
/static/9760a37363af5f4013a822dc41f91568/40601/create-user.png 945w,
/static/9760a37363af5f4013a822dc41f91568/78612/create-user.png 1260w,
/static/9760a37363af5f4013a822dc41f91568/e8950/create-user.png 2000w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;ol start=&quot;9&quot;&gt;
&lt;li&gt;Connect to Tailnet (Tailscale network)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now that you have the PreAuth Key for your user, let&apos;s connect using the CLI. Substitute the &lt;code class=&quot;language-text&quot;&gt;&amp;lt;PRE_AUTH_KEY&gt;&lt;/code&gt; with the one you generated in the previous step and &lt;code class=&quot;language-text&quot;&gt;&amp;lt;HEADSCALE_HOSTNAME&gt;&lt;/code&gt; with what you defined on the Terraform vars.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;tailscale up --accept-dns --accept-routes &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
 --login-server&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;https://&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;HEADSCALE_HOSTNAME&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  --auth-key&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;PRE_AUTH_KEY&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;PS: You need to have Tailscale client installed!&lt;/p&gt;
&lt;p&gt;Run &lt;code class=&quot;language-text&quot;&gt;tailscale status&lt;/code&gt;. You will see the &lt;code class=&quot;language-text&quot;&gt;subnet-router&lt;/code&gt; there!&lt;/p&gt;
&lt;p&gt;If you check the Headscale Admin dashboard it should look like this:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/e65bbf569070e5c5d9a8d4ecc717d82b/8c5dc/dashboard.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 53.16455696202532%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAABYlAAAWJQFJUiTwAAACeUlEQVR42h1SWW/aYBDkV/S9hxLfxsbGN4cpggAlODbG3AnkINAQRW2lPrS/oD96OvHDaHet/WZ3dlyJj68IDyfEyzMs/xZibQbFnkO1ixKaPSuhE1U7L2FYU5hEjTDMDFG0RugvEcePqAh3Z3xY7KCNXrCa/8Ng/AeKvoem38HQNrC1FVxtgUAtEAkpYvkGXXmCK2mMoThGIk6QChPkYoJcSFBp+r9hCN+hRS8weweY0R6Su4bmb1ANNtDcBaqEWS8QDnbw/AK+myMi4nCOtjtD7BBugSFVVPz4Dar6gM/JARddyjVTiHYKt7NF1L/HpZVC4SPZmSI4/4DsFRw4KxFfHyGxFkl4yUFBMEfFiU5QpFt8Ht5D6C55NzazoTF4RHt0gFzPoXK66k7hP56ge7xvfQa7scYofYXT3EBnv+7PEXrc0AmfIPJOZmMPI95CCRZQQ0psb2B13msaQ2l6kMNpvcEI1jCDJaxwjVbvCfVoSzNXqIVbBDSzohgLXChTeO0H1BorSLW0hElip7WBwBPIdFatZ3C8v5CYS/a0RIsq3hVIdgaBsWYWdFnPcCEnsCM62rqF6hSwGhv0Jmf0k5cy11xuSDl1/ycMyq86OTdbYpye4XGowROY3gJWLSehdo0vUh9ueILbeIZGQt1ZcuMdgq/3zOckWZTf8us25SV8fMOYotmhdPcGtpdyWAbPGaFyqQ3xUWyjGf+C2zpCsCYQrQxhd4dm/wEC5SvcSOZ9Dqsm8xn/goLOLyj5mfW8rGXGmkvCC7WHTySs+3vUG0+UTMfYHF89ozM4lvk7NG6YjVuw/RFMPnQbCb4lVNHOYHAzOxjDdPr4D5y/iVuMe0pKAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Headscale Admin Dashboard&quot;
        title=&quot;&quot;
        src=&quot;/static/e65bbf569070e5c5d9a8d4ecc717d82b/f058b/dashboard.png&quot;
        srcset=&quot;/static/e65bbf569070e5c5d9a8d4ecc717d82b/c26ae/dashboard.png 158w,
/static/e65bbf569070e5c5d9a8d4ecc717d82b/6bdcf/dashboard.png 315w,
/static/e65bbf569070e5c5d9a8d4ecc717d82b/f058b/dashboard.png 630w,
/static/e65bbf569070e5c5d9a8d4ecc717d82b/40601/dashboard.png 945w,
/static/e65bbf569070e5c5d9a8d4ecc717d82b/78612/dashboard.png 1260w,
/static/e65bbf569070e5c5d9a8d4ecc717d82b/8c5dc/dashboard.png 2384w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;ol start=&quot;10&quot;&gt;
&lt;li&gt;Reach private server&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;We deployed an EC2 with an NGINX installed (pretend it&apos;s a real application). The internal Route53 Hosted zone has a DNS record called &lt;code class=&quot;language-text&quot;&gt;service.internal&lt;/code&gt; that points to this service. Open your browser and try to reach &lt;code class=&quot;language-text&quot;&gt;http://service.internal/&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The Subnet Router is being configured as the DNS server of the Tailnet in the Headscale configuration. Because of that, we are also installing &lt;code class=&quot;language-text&quot;&gt;dnsmasq&lt;/code&gt; to resolve all &quot;.internal&quot; domains, that&apos;s why you are able to resolve &lt;code class=&quot;language-text&quot;&gt;service.internal&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/78d662bdfaaffbf6148059bd5cbec8df/f4b77/service.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 39.24050632911392%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAYAAAD5nd/tAAAACXBIWXMAAAsTAAALEwEAmpwYAAABZUlEQVR42o2QSy8DURTH58tYiJDubNAtWyLaWKGx8hEspPFaSCMWNra+AxJsUGmbUGU6b8yjnT5nTKYzFY/5u3OT0kQbTvLPOefec37n3sOMLh8gurSDaCKFscUUJlf2qSYSuxiZ2yTaQCS2hUh8G8Oz6xicXvvRTBLj80lMxVcxFNvDwMIhGNt9heV4cP03sLyCi3QOBVZE/oHHHStAUFTcFjgq+dmA7bbRJPWW41N9xyGHiAGxIAhCh6dHBednp8hlM0hfXSKfv4EoCjg5PkI2cw3D0NFd38uY7sR1XZimiUqlgnK5DKNUgqqqqFZr0HQdlmWRuEruSqjVTOi6hkaj8RvYmViv18mLRCiKAkmSwPM8ZFmmOcdxdIjj2ATsEeAHPO+d9H72B4bTQ0gILRaLFMSyDxAEiXgbhfsmbPuFQNsECPh+0PvLHWCr1SJ7MuhLw69omkbP/tpb3x12Gv8noNeML3D+Puu3MIurAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Screenshot of NGINX private service&quot;
        title=&quot;&quot;
        src=&quot;/static/78d662bdfaaffbf6148059bd5cbec8df/f058b/service.png&quot;
        srcset=&quot;/static/78d662bdfaaffbf6148059bd5cbec8df/c26ae/service.png 158w,
/static/78d662bdfaaffbf6148059bd5cbec8df/6bdcf/service.png 315w,
/static/78d662bdfaaffbf6148059bd5cbec8df/f058b/service.png 630w,
/static/78d662bdfaaffbf6148059bd5cbec8df/f4b77/service.png 770w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;ol start=&quot;11&quot;&gt;
&lt;li&gt;Access Database&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I&apos;m using DBeaver to connect to the MySQL database. Check AWS Secrets Manager for the secret that contains the root credential.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/90eabd4360d3ea9a1de82629a3c3b7d2/b4098/db-connect.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 70.88607594936708%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAOCAYAAAAvxDzwAAAACXBIWXMAAAsTAAALEwEAmpwYAAACN0lEQVR42o1T2XLTQBDUXwU7CQ5UUY6DZFn3sdYt+YxDMCGBV3686VnZxkCq4KFrtavVTHdPyyi7Jbr1Fs1qjXnVIEjncMIYbpT8H3j3LlbwkhAq9WDcWVNMHQ+2F8ANIni8YHFv2u4/8dF24PgBbBYMVYIsnMFQeYmi7pByTeYFYpXrrpbjY+ryMjHV8E+wT+c+WcaIEhZMqCxKYWQsVnUrZFWLsl2gINKiYgOiKBFnBRJCzlRZI2FjnwXO4ZFhwFXsMqRQt76H4gdl06JdrjWaxVKjpbc1fZaiMRVEVBCm2QnRPNfeCxF5NqRzs9ocGFWakUvqXtyjZ5CeznpW8z/w68zImw7bxz1WD5+x3O7QcuJigyAtakRZSQat3qtSUpC9ilD10AwzwnFdWIEPa+bCCUJ6kuoJTg4pmDEFemUSjqx/R++loaoaLif29uYGF6MrDIaXuLweYfTuvcbw6hoXg2EPvjPZMKSPfzPszwxFWR61z7IWi41I3mhPxUuHwfXOpilBVtrnUhc4RyQDk6HYfoSxOWWgI0an0RNrOOU5IyLZlMJSIOHQZkGs77qvSD3uDTvOMHFD7Y3th5pBwUFJNykm+zxXKArFALO5lyDlkB6fvp7w6QvBVaJjyEdT+e1E3iEe8iyyF5stwqzGh+I7xt0PjNMtbk1TN3vYP2H//IKaWV3e77B/+abtMsSnW8oYm9YJE8tmgDNKzmGS/Rtvh0H0jKtZhQnfS8OQf4XE5Oiv7GX9CTS8O1fRtw69AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Screenshot of Connecting to the RDS&quot;
        title=&quot;&quot;
        src=&quot;/static/90eabd4360d3ea9a1de82629a3c3b7d2/f058b/db-connect.png&quot;
        srcset=&quot;/static/90eabd4360d3ea9a1de82629a3c3b7d2/c26ae/db-connect.png 158w,
/static/90eabd4360d3ea9a1de82629a3c3b7d2/6bdcf/db-connect.png 315w,
/static/90eabd4360d3ea9a1de82629a3c3b7d2/f058b/db-connect.png 630w,
/static/90eabd4360d3ea9a1de82629a3c3b7d2/b4098/db-connect.png 816w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;ol start=&quot;12&quot;&gt;
&lt;li&gt;Disconnect from the Tailnet&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;tailscale down&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Try to reach the private server or the database! You won&apos;t be able to reach, us as we expected!&lt;/p&gt;
&lt;h2&gt;Cya!&lt;/h2&gt;
&lt;p&gt;I hope you&apos;ve like it and that this demo and explanation was helpful!&lt;/p&gt;
&lt;p&gt;See you around! 👋&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Hands-on SSO with Terraform and Authentik]]></title><description><![CDATA[It's been a while since I wanted to write this blog post! I do not intend to follow best practices to deploy Authentik or be extremely…]]></description><link>https://felipetrindade.com/sso/</link><guid isPermaLink="false">https://felipetrindade.com/sso/</guid><pubDate>Sat, 03 May 2025 10:40:32 GMT</pubDate><content:encoded>&lt;p&gt;It&apos;s been a while since I wanted to write this blog post! I do not intend to follow best practices to deploy Authentik or be extremely detailed about how SAML/OIDC works in depth but I will give you a good overview and flows to explain how it works. The idea of this post is to explain how SAML/OAuth/OIDC works, and how to configure a SAML and OIDC application using Authentik. I highly recommend you check the &lt;a href=&quot;https://github.com/felipelaptrin/sso&quot;&gt;GitHub repository&lt;/a&gt; that contains the code used in the blog post!&lt;/p&gt;
&lt;p&gt;At the end of this blog post you should be able to create the following infrastructure:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 471px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/ebeda82a5a1476b841facfd98de4de96/1f09d/Architecture.drawio.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 96.20253164556962%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAATCAYAAACQjC21AAAACXBIWXMAAAsTAAALEwEAmpwYAAAC9klEQVR42o1Uy2oUQRSdj3LjJ7gQFFzqTgRxoRsJuNCNIK5dSRDBiEoSRBHFRYTRKEbzMJNMRp33s6enq9/v7uru4+2amcSQ0aSGM9VVde+pc++tqgJ7sQq2uArOLMQDHdFARSRpiGVDIPUjpGF8PIIIiReiYBV34XyvIbQNuLIkiLnhgFuu6JGloL/jkWVIc8LE85G3zfozvFq/hbZZQ035gSrbhh0pcPyTgSchMoqmwN0xoen00GdbMAMVeurCiHUylOEEo6Pwh3C84f7YpjHnAaUnJoV+KAiTJICXODC6X6Gt3IO+vQRFaxHa+xhN4UgYefJ4jrWg233yD0W+C3ki8xaGJqzMgfptAeq1AoyHZ8lwAM3qCwfTGcB0JRhEZG19gFV8KeYUvUNoI0mjw4RRbMPmJrTaCuRHF2C8uQk3kODHMvUjGOQssQZ6rRL4/F0kcxfhaU1o7hBsJiF3YQcymNlDtVuFRmHpDkNvpMCLVKhmV6ixPAlacwt6+TMc2kQj9YoxU6FLCZaFk271EBDJ0toOrj95i61WA6rWADO6IuxKu4xye0+ozlMym5AfEDKzAx7ZeF5bxrlPl1CWy1BYmwg78DlVX6nD7pfhRGys/DhCjRR6oYLdyhpWiq+h6V0KfwBZbaEv/YTx7j7cL4/F8VH/p1DkkFTk1c3PV7O7h2angqHaFGPD6dN6H9rvBZiNp1QsdpIctgXyXFmkynLGR8aYHBuTqlqqbBM2xbewn63QJ0KFnMlIozxSmIy+mTeCYpIyfTyn5oXxenQee2MbgkU2PJkSTm5KfsGnv2mL63UkG5uAZe3bhB4wrAFUo/w9OPDMHwfiKqT7hFOfbNJl6F2+iuGp0+DFj5NtEnR3gPkrwPIdIE0O+80mnLQkTVG/MYf6mfPw1zcmhCmkKrB4O8X7B+k/CCc5/CtSuugJ6o0GquUyaqUSmhQ6U1WxFgQxflX6aLdGRyKbvIfhzBc4izgyngogTsRYrEUx7chB79XMF/sPu3iYBpru9kIAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Architecture Diagram&quot;
        title=&quot;&quot;
        src=&quot;/static/ebeda82a5a1476b841facfd98de4de96/1f09d/Architecture.drawio.png&quot;
        srcset=&quot;/static/ebeda82a5a1476b841facfd98de4de96/c26ae/Architecture.drawio.png 158w,
/static/ebeda82a5a1476b841facfd98de4de96/6bdcf/Architecture.drawio.png 315w,
/static/ebeda82a5a1476b841facfd98de4de96/1f09d/Architecture.drawio.png 471w&quot;
        sizes=&quot;(max-width: 471px) 100vw, 471px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2&gt;SSO and common scenario&lt;/h2&gt;
&lt;p&gt;As I have mentioned in previous blog posts, my preferred way to understand a topic is by answering generic questions about it. Once the basics are clear, it&apos;s easier to deep into the topic. This time, I will stick to this method, but before getting into the questions, I will propose a scenario. So, without further ado:&lt;/p&gt;
&lt;p&gt;Imagine working for a company that uses several apps/services. It doesn&apos;t really matter what services you use, but if you are a more visual person, you can think of apps such as SalesForce, PowerBI, ArgoCD, Gmail, etc. The most dummy way to manage credentials to all these apps is to create an account (probably IT would do this for you) in every service and then you would have to set up a password for each application. This is tedious because of several reasons:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Now you have to memorize a bunch of credentials and manage the rotating (change of password) of each one when needed. Sure, you can use a vault to store your credentials, but still, very tedious.&lt;/li&gt;
&lt;li&gt;You don&apos;t have an easy way to check which apps you have access to. You would need to save all the URLs in your browser bookmark.&lt;/li&gt;
&lt;li&gt;For each new service that you want to have access to, you will have to create a new password, manage the rotation, and memorize/save the URL to access the application.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is tedious, kinda-insecure (do all apps implement good password policies and 2FA?), and time-consuming. Ideally what you want is to have SSO (Single Sign-On), which is a single place that will manage your access to the apps. To allow SSO you will need a component (technically it&apos;s called an Identity Provider) responsible for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Centralized user identities, i.e. the users of your organization will be registered in a central place (a &quot;centralized database&quot;). This way your users only need to worry about these single credentials.&lt;/li&gt;
&lt;li&gt;Handling all the communication/security between itself and the users and itself and the applications. Here is exactly where SAML and OIDC come into play.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;SAML&lt;/h3&gt;
&lt;p&gt;SAML (Security Assertion Markup Language) is a standard to exchange authentication and authorization between two parts using XML. In our previous scenario, SAML would be used by the Identity Provider and the applications.&lt;/p&gt;
&lt;p&gt;To use SAML, both parts must &quot;speak&quot; SAML, i.e. they should implement SAML in their code. There are three SAML versions (&lt;code class=&quot;language-text&quot;&gt;1.0&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;1.1&lt;/code&gt;, and &lt;code class=&quot;language-text&quot;&gt;2.0&lt;/code&gt;), where SAML 2.0 is the latest and by far the most widely used version. When you hear someone saying &quot;SAML&quot; they are probably talking about the 2.0 version.&lt;/p&gt;
&lt;h4&gt;SAML Concepts&lt;/h4&gt;
&lt;p&gt;SAML uses several technical terms that can intimidate you when trying to learn about it. Let&apos;s define them using simple words, then I will show you the classic sequence diagram.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Identity Provider (IdP)&lt;/strong&gt;: It&apos;s the heart of the SSO! It&apos;s responsible for storing the identities of the users and contains the &quot;logic&quot; to speak with the applications and users.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Service Provider (SP)&lt;/strong&gt;: The SP is the application that users will access! As the name suggests, a service provider will provide a service for the user (e.g. SalesForce, Gmail, PowerBI...). This is exactly what the user wants to use. The user is not interested in the IdP part, he wants to access the applications that will be accessible by using the IdP (instead of a specific single user/password created for this service). The service provider must be able to communicate using SAML protocol with the IdP, i.e. you need to confirm in the documentation of the application if it supports SAML.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SAML Request&lt;/strong&gt;: Corresponds to the XML message that the SP will send to the IdP. In plain English, this would be something such as &quot;Hey IdP, there is a user that is trying to log in. Can you validate his credentials? I will wait for you to validate his credentials (authentication) and log him in if you say the credentials are valid, and I will only allow him to do things he is allowed to (authorization)&quot;. You might be wondering, &quot;Why would the SP trust another entity to validate if the user can log in into it?&quot;. This is because the SP and the IdP were previously configured to allow this using SAML protocol (more on that later).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SAML Response&lt;/strong&gt;: Corresponds to the XML message that the IdP will send to the SP. In plain English, this would be something such as &quot;Hey SP, I validated that these user credentials are correct, but make them valid only for the next X minutes/hours. This user is actually John Doe, and should have the role &apos;admin&apos;. You can validate that this message was issued by me and didn&apos;t suffer a man-in-the-middle attack by checking my digital signature&quot;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Binding&lt;/strong&gt;: The IdP and SP communicate via the browser exchanging HTTPS messages. We call &quot;binding&quot; the mechanism to transport these messages. The most common ways of exchanging these messages are via HTTP POST (SAML message is sent via HTML form with base64 encoding) or HTTP redirect (SAML message is sent via HTTP GET with URL query string). You might be thinking... which one should I choose? This should be mentioned in the SP documentation!&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Assertion&lt;/strong&gt;: It is the main part of the SAML response. The SAML response contains assertions (usually it only contains a single one) and more things (such as issuer, and status...), but the most important information is the assertion, which contains important pieces of information: subject (who the user is - an important field here is the &lt;code class=&quot;language-text&quot;&gt;NameID&lt;/code&gt; that identifies the user usually via email or id), conditions (time window of validity of the assertion), issuer (the IdP), attributes (email, roles, department)... The assertion is signed with IdP digital certificate keys, so this can be validated to make sure the message was not tampered.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Assertion Consumer Service (ACS)&lt;/strong&gt;: The ACS is simply the SP endpoint (e.g. &lt;code class=&quot;language-text&quot;&gt;https:/myservice.com/saml&lt;/code&gt;) that the IdP will send the XML data. Remember that SAML is a protocol that uses XML messages and these messages are transported using the HTTP protocol. You should check this endpoint in the documentation of your SP. In other words, the SP is the entire application, while the ACS is the endpoint of the SP.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SAML Flow&lt;/strong&gt;: SAML allows two flows: SP-initiated and IdP-initiated. As the name suggests, in the SP-initiated flow the service provider (application) is the one that starts the communication with the Identity Provider (IdP). This flow happens if the user accesses the SP URL directly (and clicks to log in using the IdP). The IdP-initiated flow is the opposite, the IdP starts the SAML flow and basically redirects the user to the ACS. This flow usually happens when the user is already logged in and from the IdP Portal clicks to access an application. In practice, the only technical difference between these two flows is that the IdP-initiated flow won&apos;t have a Relay State (more on that below).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Relay State&lt;/strong&gt;: Imagine the user bookmark a specific URL page of the SP (e.g. a specific Dashboard in PowerBI, a specific App in ArgoCD...). If he accesses this link directly the SP-initiated flow will be used. And if RelayState didn&apos;t exist, you would expect, at the end of the flow, the user to be redirected to the homepage of the service provided, not the specific URL that he wanted to access at first. The RelayState is basically the information of the page that the user wants to access when the SAML login is completed. This information can&apos;t be lost during the entire flow, so the RelayState information is passed from the start of the SAML request until the SP validates the SAML assertion and redirects the user to the page.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Metadata&lt;/strong&gt;: When configuring SAML it&apos;s common to see the SP or IdP exposing its metadata, i.e. information about itself that should be known by the other part (URL, certificates, binding...), allowing a more automatic configuration. This usually can be exported as an XML file or exposed via HTTP endpoint.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now that you know all these technical words let&apos;s jump to the diagrams!&lt;/p&gt;
&lt;p&gt;Here is the SP-initiated flow.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/884e7abf5e1de24f6dae1a27c54a21c0/084e2/SP-SAML.drawio.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 63.291139240506325%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAYAAACpUE5eAAAACXBIWXMAAAsTAAALEwEAmpwYAAAB/UlEQVR42n1SSW7UQBRtIRYICS6UM3AJ9iyQIsSWPTdgwRoJiQVixXQARIRIiEKn6cmz3e25XB77Ua8cGyeKKOm7yn94//1hhqtzOBz0nWUZ1us1NpsNVqsVTNPEYrHAcrmEYRiwLGv0ve3MbiqKooDruj3QnxUsx8PWdLDZGgh8X4P+F7ATAerERpNaaCuBqWtTxKgzH03uo63FUIv+VnmIIjQhIwu1iP8B7l8d4fvxfRgvHqI4e9tru1aHma8f4efzB/h1fAfB15da19WFdrHfPMbJ07s4fXYP7rsnfSoVNyuNb4gvPsP78R6BcYFcSMRxjCiK4Z1/we70A+TlR7Txtud26Hr2/jmE0ovLT2h2v0f2uoetQs7LBmGcYquGwYG4joMwk0iLEpXCMOy+r9P+tV2Htm2vDVUBdqgqiSSOlLHBbrfDfr9Hnueq8gZSCm1jAg6kruthLdA2jZJ67Os4ZWapqko7803hm9MmCJmlaapZNApkECGE9ouiSK8T3xqQRvZNSjkKnW3b1jtJUM/zNGvqB+F/GIZ6X+fzudaNgINDEAS6ZJbOJAwgA9o71bMpwyFukJEhSyzLEkmSwFfLSyEgl5yl0pFvBk1PNxnKtR4yE8tyVONZGktlT1guk1Boo46+45Sv+n1jyhiHwJsBgyMZDPrbDu2UKcO/FrjekIypT54AAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;SP-initiated SAML flow&quot;
        title=&quot;&quot;
        src=&quot;/static/884e7abf5e1de24f6dae1a27c54a21c0/f058b/SP-SAML.drawio.png&quot;
        srcset=&quot;/static/884e7abf5e1de24f6dae1a27c54a21c0/c26ae/SP-SAML.drawio.png 158w,
/static/884e7abf5e1de24f6dae1a27c54a21c0/6bdcf/SP-SAML.drawio.png 315w,
/static/884e7abf5e1de24f6dae1a27c54a21c0/f058b/SP-SAML.drawio.png 630w,
/static/884e7abf5e1de24f6dae1a27c54a21c0/084e2/SP-SAML.drawio.png 632w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Notice that the SP initiated the SAML flow, that&apos;s why the flow is called &lt;code class=&quot;language-text&quot;&gt;SP-initiated&lt;/code&gt;. It&apos;s important to understand that in both flows, the SP and IdP do not communicate directly, this is all done by the user browser (front channel), with redirects and HTTP requests to the endpoints (e.g. ACS). Now let&apos;s check the IdP-initiated SAML flow.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 627px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/6ed8f50c5c2f24b6552fce2c34b7afad/e9c9b/IdP-SAML.drawio.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 63.92405063291139%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAYAAACpUE5eAAAACXBIWXMAAAsTAAALEwEAmpwYAAAB8klEQVR42oVSy24TQRD0AS6B/+Ij4CsQIgiJD+HIBU6IUwRCfAEiBIFMcrGzXu/D+9619znj3WKqk3ViK8BIpd7Z6q6p7pkJDlZd15jNZlgul5jP5xK5JzzPw2KxwHa7xd/W5C5B13Xh+z5mlxZsx4fjB7i0bMRxDMuy/i3Y1zFU4UEXLvqu3CN1U0BtAsGOGwYJqspRJw6azEVXpjeC6ZtHOHt2BOvVEaqvr6/+9lqC+/Yxfr14iPPn9xF9fAlKDboRLjw5xo+n9/Dz+AG8d0+uD+sxad3vyM6/IPv9CU1iQfcDtFLQ2wGl/Q3+6QesTt+j9KaGM65VJ7ENLpBPT7C++AwdTvdnuO17dAbKzKZrW3RdB2UKw6SA5UZwwgJJXso/csxRJr/qlIFGbDjHXB7nbwR7OTXPEpN8JTYijiOEwQpRGBg+M4Lqhm8bKJOfZymapkZZlma8w7VDOhNXSqC1xmq1QpIkcttEGIZI01S4Ma+qKqlj/XjzO8E8z8Vya9ohmqaRd8dn4jgOgiAQFxQZwX1mnPNw27ZFfCc4JvCt0RkjHREsIs+W6PA2aIL1/O7NXPda3mw24oTtRVEkLtfrtbTHQh54e1Hg8JGLINXZMiOF2CqFRgdFUUhbbJ3i/xUkwbkdtjNeAONdiyNg7W3BPw2q39CfIR1UAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;IdP-initiated SAML flow&quot;
        title=&quot;&quot;
        src=&quot;/static/6ed8f50c5c2f24b6552fce2c34b7afad/e9c9b/IdP-SAML.drawio.png&quot;
        srcset=&quot;/static/6ed8f50c5c2f24b6552fce2c34b7afad/c26ae/IdP-SAML.drawio.png 158w,
/static/6ed8f50c5c2f24b6552fce2c34b7afad/6bdcf/IdP-SAML.drawio.png 315w,
/static/6ed8f50c5c2f24b6552fce2c34b7afad/e9c9b/IdP-SAML.drawio.png 627w&quot;
        sizes=&quot;(max-width: 627px) 100vw, 627px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;The IdP-initiated SAML flow starts with the IdP already generating the SAML response with assertion, so a &quot;SAML request&quot; does not exist in the IdP-initiated flow.&lt;/p&gt;
&lt;h3&gt;OIDC&lt;/h3&gt;
&lt;p&gt;Similarly to SAML, OIDC (OpenID Connect) is a standard for exchanging authentication and authorization using JSON. This standard was built on top of OAuth: a standard that only implemented authorization (that&apos;s why we don&apos;t use OAuth for SSO). OAuth&apos;s first version (1.0) was born in 2009 but nowadays it&apos;s rarely used, since OAuth 2.0 (created in 2013) is more flexible and simpler to implement. OIDC implements the authentication layer of OAuth 2.0 and it&apos;s a common standard used with SSO these days. The general rule is: that new apps use OIDC, while legacy/enterprise apps use SAML.&lt;/p&gt;
&lt;h4&gt;OAuth/OIDC Concepts&lt;/h4&gt;
&lt;p&gt;Let&apos;s check some concepts that are present in both OAuth and OIDC.&lt;/p&gt;
&lt;p&gt;Basically, there are four actors involved:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Resource Owner&lt;/strong&gt;: The user that owns data/resources and would like to grant access to another part. This is usually a human (me, you, etc) that would like to share information with a system. Have you ever seen the &quot;Log in with Gmail&quot; instead of creating from scratch your user in a platform? That&apos;s it, you are the owner of your information (name, email, phone number) and would like to share these. Resource Owner is also known as simply &quot;User&quot;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Resource Server&lt;/strong&gt;: This corresponds to the server/service that contains the resources (e.g. Google Drive, Grafana...).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Client&lt;/strong&gt; The client is the application that wants to access the resources on behalf of the resource owner (user). Here is a practical example: I usually use DrawIO to create the diagrams for my blog post. I can integrate the diagram created with Google Drive (so I don&apos;t have to store it locally). DrawIO is the client (because he wants to access my Google Drive on my behalf) and Google Drive is the Resource Server.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Authorization Server&lt;/strong&gt;: It&apos;s responsible for issuing access tokens for the client to access the resources. In the DrawIO example I just mentioned, this would be the Google Accounts service. Sometimes the Authorization Server and Resource Server is the same thing (same system). Although this is not technically 100% accurate the Authorization Server is also known as Identity Provider (IdP).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Before the OAuth/OIDC flow starts the client and authorization server should be configured beforehand.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Client type&lt;/strong&gt;: The client can be placed in one of the following categories when adding a new client to the authorization server: confidential client and public client (non-confidential). The concept of public here doesn&apos;t mean publicly accessible, it means an application that cannot safely store secrets. A SPA can not store secrets securely because the only way for the application to store this is in the source code, which is available if you inspect the website. Because of this, confidential clients (i.e. server-side hosted applications) are the only type of clients that store &lt;code class=&quot;language-text&quot;&gt;client secret&lt;/code&gt;. Public clients use PKCE (Proof Key for Code Exchange) a one-time code challenge.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Client ID and Client Secret&lt;/strong&gt;: The authentication server needs to create in its configuration a new application/client. This gives us a client ID (nonsensitive) and a client secret (sensitive - if the client is confidential). These are used later to obtain the access token when using the authorization code flow (keep reading and you will understand this!).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Some important concepts in the flow:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Authorization Code&lt;/strong&gt;: Corresponds to a short-lived (around 10 minutes), one-time-only use code that is used to prove to the client that the user logged in and gave permissions. The authorization code is meaningless to the resource server and it&apos;s obtained with browser communication (front channel), which is less secure (is it secure, don&apos;t get me wrong, but it&apos;s considered less secure than a server to server - i.e. back channel - communication), that&apos;s why the authorization code is not sufficient to access the protected resources.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Scopes&lt;/strong&gt;: We don&apos;t want the client to access the resource server with permissions to execute whatever he wants, we would like to limit the access. Using the DrawIO example, we would like to grant DrawIO (the client) to see/create its own configuration data in the user Google Drive(&lt;a href=&quot;https://developers.google.com/identity/protocols/oauth2/scopes#drive&quot;&gt;https://www.googleapis.com/auth/drive.appdata scope&lt;/a&gt;) but not view the user photos, videos, and albums in Google Photos (&lt;a href=&quot;https://developers.google.com/identity/protocols/oauth2/scopes#drive&quot;&gt;https://www.googleapis.com/auth/drive.photos.readonly scope&lt;/a&gt;). The user needs to be aware of the actions that the client will do on the user&apos;s behalf, so that&apos;s why a consent screen is usually presented after the user logs in to the authorization server.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Redirect URI&lt;/strong&gt;: The Redirect URI (also known as callback URL) is the URL that the user needs to reach back with the Authorization code that was given by the Authorization Server. The flow is stateless and the communication happens using the browser as an intermediate layer (except for the Access Token exchange), so the client needs to specify this URL when the OAuth/OIDC process starts.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Access Token&lt;/strong&gt;: Differently from the Authorization Code, the Access Token contains the &quot;real credential&quot; that proves authorization to access the protected resources. It&apos;s often a signed JWT that can be validated by the resource server. This access token is limited to the scopes authorized (consented) by the user and it&apos;s obtained between the direct communication of the client and authorization server (back channel) when the client passes the client ID, client secret, and authorization code. Notice that this value can not fetched by the user, since this communication happens between the client and the authorization server.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Response Type&lt;/strong&gt;: The response type specifies what flow the client expects the authorization server to use. OAuth/OIDC supports several flows but the most common is the &lt;code class=&quot;language-text&quot;&gt;Authorization Code Flow&lt;/code&gt; (the one I&apos;m commenting on here!). You might have heard about &lt;code class=&quot;language-text&quot;&gt;Implicit flow&lt;/code&gt; but this is deprecated and not recommended anymore (it only uses the front channel to exchange tokens).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you understand these concepts, you are ready to understand the OAuth 2.0 Authorization Code Flow below.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/92b3f8124855aaed05f4e5137d6ff3f3/5b4a1/OAuth-2.0.drawio.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 69.62025316455697%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAOCAYAAAAvxDzwAAAACXBIWXMAAAsTAAALEwEAmpwYAAAB4ElEQVR42pWTTW7bMBCFfawWWXedI/QEPUGOkEVygKDLZtVVlkEQoF22XRRIgdqGrdhKJEuyadGSLImS/MJHhUpi5wehMCBFcj6+mSF7eKFt9Mc2XyzQ7/fhui4cx4FYLtv1zeZZv1672GirsWlqs5HWNI35L4scWZahLEvkeQ5VFtqhNj7d4c29v54zwPT3CYbHH7E8+6JB7cmV/xfh10+Iv+1DCaf1rAu4p5/x/3gP2dX3Dih/HGJw9AHy/KAFVuIacnCJdPoHcSwhkxRx6CIZXmLt/ESeCCsF2fSX2VvJ2w6ooqGeu4AK/rVAtlJVmHoRxuMxxqMRnMkUIskhU63q5qbLGfu6rndyZ+c6YL5eIwwCnXSBIAw1eITJtQPfu4UQAkopk1fmtCxzFEXxANbK60qZvseNXGTS2VdVZWy1WmE2m8HzPEgpu3UWJk1ThPpQjncU8nReCVaSG+hEW2vFvu8bqJ0jlPPs3wzZnk41BAU6fKrj2IKTJDFARrB9F3eANvQ4jg2AjlTNjcwdjWNrbyq0jo8vts3nfD43CtkzCobMPFLEq8Dt50QoVVI1gSwE/1kwjgl9N9A8N62EzrbqNKpd6ndtq097AqTzdrMKbYU5jqLIFMzCeCDzbcO/A0mRN0v5HK3hAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;OAuth 2.0 Authentication Code Flow&quot;
        title=&quot;&quot;
        src=&quot;/static/92b3f8124855aaed05f4e5137d6ff3f3/f058b/OAuth-2.0.drawio.png&quot;
        srcset=&quot;/static/92b3f8124855aaed05f4e5137d6ff3f3/c26ae/OAuth-2.0.drawio.png 158w,
/static/92b3f8124855aaed05f4e5137d6ff3f3/6bdcf/OAuth-2.0.drawio.png 315w,
/static/92b3f8124855aaed05f4e5137d6ff3f3/f058b/OAuth-2.0.drawio.png 630w,
/static/92b3f8124855aaed05f4e5137d6ff3f3/5b4a1/OAuth-2.0.drawio.png 831w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;As mentioned, OIDC is built on top of Auth 2.0, so it introduces new concepts/jargon that do not exist in OAuth 2.0 that are helpful when it comes to authentication:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;ID Token&lt;/strong&gt;: A signed JWT that contains information about the user (name, email, unique ID...) that is issued by the Authorization Server. The key-value pair inside the JWT that represent the user is called &lt;code class=&quot;language-text&quot;&gt;claim&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;/userinfo&lt;/strong&gt;: The OIDC standard specifies an endpoint called &lt;code class=&quot;language-text&quot;&gt;/userinfo&lt;/code&gt; in the authorization server so that the client can get more information about the user if needed. Yes, the ID Token already has information about the user, but sometimes the client needs extra information that is not available in the claim. In these cases, the client will reach the &lt;code class=&quot;language-text&quot;&gt;/userinfo&lt;/code&gt; route (passing the Access Token - since it&apos;s a protected route) to get more information, if needed.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;openid scope&lt;/strong&gt;: To specify that the OIDC flow will be used, the client needs to specify the &lt;code class=&quot;language-text&quot;&gt;openid&lt;/code&gt; scope. Scope, as we just saw, is something present in OAuth 2.0, but the &lt;code class=&quot;language-text&quot;&gt;openid&lt;/code&gt; didn&apos;t exist.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;/.well-known&lt;/strong&gt;: The Authorization Server exposes all the URL capabilities, and metadata that the client needs to interact via OIDC in a route called &lt;code class=&quot;language-text&quot;&gt;/.well-known&lt;/code&gt; (instead of specifying the Auth URL, Token URL, and Userinfo Url).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You will notice that the OIDC flow below will be very similar to the OAuth 2.0.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/bf30751049bcc84c68afbba4f500c6ea/5b4a1/OIDC.drawio.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 69.62025316455697%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAOCAYAAAAvxDzwAAAACXBIWXMAAAsTAAALEwEAmpwYAAAB2UlEQVR42pWUS47TQBCGc6yRWLPlCNyAE3AEFswBECvEjhVLhEYgsWIWLEBCIQ9iK3Hi+NV+xI677X/6L9MW88poLJW7Xe76+q+qtid44Oq6DvP5HMvlEp7noe/7k+snvPV9Z82g74wE0Ajic9dpVFWFpmnE6ENvroH77l+85QiwvHyD6fkZ0o8vLGBYqIMfCN8+RfLuGXSyHCJNDf/9c/w+f4Lq54cRqL6+wvT1GdSnlwPQpCsUsy8ovUtkmYLKS2Shj3J2gWb1DXWROimovO9Q0wtotR6B7f6P9X1Gu/s1ACmqrFv4QSS1Wizm+LvyEasDwiSH5/tjihyNMbdq53yDQqORxBGi/R5JkmC32wl0sZjZhswQxzHatpW6Hpsax2Mt9RzBVrnRrYwTLuRLN2qtxYqiEPB2u4VSykKO8r6ua5RliTAMZX5LIRX5NiV2kgtcNw+HA4IgEKDzEUo/x5Mp83K7Uw1BVLfZbGTuwFRNIDNw9bwX6FLOskwADKRqLmTtaJw7e1ChC/z/YLt6RlEkCjm6lPM8l/lJ4M3PilCqpGoC2Qg+M/X1ei3dfzSQdWU52FlCWVcaYWmajt2nXQMy+K4fg/uOGcj53p5VNoyngz5uyNS5KYFXZho3SlQ3UnAAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;OIDC Authentication Code Flow&quot;
        title=&quot;&quot;
        src=&quot;/static/bf30751049bcc84c68afbba4f500c6ea/f058b/OIDC.drawio.png&quot;
        srcset=&quot;/static/bf30751049bcc84c68afbba4f500c6ea/c26ae/OIDC.drawio.png 158w,
/static/bf30751049bcc84c68afbba4f500c6ea/6bdcf/OIDC.drawio.png 315w,
/static/bf30751049bcc84c68afbba4f500c6ea/f058b/OIDC.drawio.png 630w,
/static/bf30751049bcc84c68afbba4f500c6ea/5b4a1/OIDC.drawio.png 831w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2&gt;Hands-On&lt;/h2&gt;
&lt;p&gt;The goal of this demo is to show how to configure:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Authentik as an Identity Provider&lt;/li&gt;
&lt;li&gt;AWS as a Service Provider using SAML&lt;/li&gt;
&lt;li&gt;Grafana as Client using OIDC&lt;/li&gt;
&lt;li&gt;Configure Caddy as a web proxy&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To achieve that I will use Terraform and deploy resources in AWS. For simplicity and to make this demo cheap I will deploy Grafana and Authentik in the same EC2. This is obviously a bad practice and you should not use this in production! Since the code is kinda big I will only paste the step-by-step guide to run the demo. Please check the repository for the code.&lt;/p&gt;
&lt;p&gt;There are some things we need to know before hands-on starts:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Grafana only supports SAML in the &lt;a href=&quot;https://grafana.com/docs/grafana/latest/setup-grafana/configure-security/configure-authentication/saml/&quot;&gt;Enterprise edition&lt;/a&gt;, so we are going to use OIDC, that is available to everyone.&lt;/li&gt;
&lt;li&gt;AWS Identity Center can only be configured in the Management Account, so if you are planning to run this demo, make sure you are in the management account!&lt;/li&gt;
&lt;li&gt;Make sure you have a hosted zone configured in Route 53&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;Install dependencies of the repository&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;devbox shell&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;Initialize Terraform&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;terraform init&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;Create a file to define variables&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;You can check the README below to see the variables that are required (or check &lt;code class=&quot;language-text&quot;&gt;variables.tf&lt;/code&gt; file directly).&lt;/p&gt;
&lt;p&gt;Here is one example of a valid file (modify it for your use case)&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;hcl&quot;&gt;&lt;pre class=&quot;language-hcl&quot;&gt;&lt;code class=&quot;language-hcl&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# vars.tfvars&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;aws_region&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;us-east-1&quot;&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;domain&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;mydomain.com&quot;&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;use_staging_certificate&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;authentik_email&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;me@mail.com&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ol start=&quot;4&quot;&gt;
&lt;li&gt;Apply Terraform and create the infrastructure&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Remember to have valid AWS credentials in your terminal!&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;terraform apply --var-file&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;vars.tfvars&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This command will provision the VPC, and EC2 with elastic (static) IP and create the record in Route53 to allow Let&apos;s Encrypt to validate we own the domain (Caddy will manage and create this certificate for us). The EC2 will run the &lt;code class=&quot;language-text&quot;&gt;user_data&lt;/code&gt; script to configure Authentik and Grafana.&lt;/p&gt;
&lt;ol start=&quot;5&quot;&gt;
&lt;li&gt;Wait for infrastructure to get ready&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Open your browser and keep checking if the Authentik server is already ready (check the endpoint by running &lt;code class=&quot;language-text&quot;&gt;terraform output authentik_endpoint&lt;/code&gt;).
It will take a couple of minutes (most likely less than 10) to be available because the &lt;code class=&quot;language-text&quot;&gt;user_data&lt;/code&gt; script needs to take a couple of minutes to install Authentik and expose using Caddy. Also, the DNS to reach the DNS needs to propagate to the network.&lt;/p&gt;
&lt;p&gt;You can check how the progress is going by connecting via Session Manager (AWS Console) to the EC2 and running the following command:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;su&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Check Cloud Init Script&quot;&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;cat&lt;/span&gt; /var/log/cloud-init-output.log
&lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Check Caddy Status&quot;&lt;/span&gt;
systemctl status caddy&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ol start=&quot;6&quot;&gt;
&lt;li&gt;Go to AWS Console to get Identity Center URL&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now, open the AWS Console and navigate to the IAM Identity Center.
Go to &lt;code class=&quot;language-text&quot;&gt;Settings&lt;/code&gt; and click &lt;code class=&quot;language-text&quot;&gt;Change Identity source&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/76d762a4e863f47ea515eea1fd9966bc/636d3/identity-source.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 68.35443037974683%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAOCAYAAAAvxDzwAAAACXBIWXMAAAsTAAALEwEAmpwYAAACmklEQVR42m1UaW/bMAw1MKArtnVtk9qJY9nxfV/xVSdprnZDP+7//5o3Ukm7otgHQhRFkU/ko5RstUHV7xFkHVQRwIlXGLa/YLgZHgwfGtmkmOE//aPto510JW+2yFZrzKxIGnU7QdE9wU9b2GGFZVBKMf0Cll9epMAyqqRNePnZh3z5rsIXF6S8ZVs4KdrxiHo8wfBKShRivowRlwNcQs/Iea8RgLDokdbr8/5yXwnKtXyqShteBV1oKRhnnFMiRs7SkK2k0nBQk1Cxbz0+Y9y/ShCcSCJM2gOiYkCQdzDogLOFeU+2HiHVdX4JyKVhlH7WwknPdi8bkFQjrKCQ5WEwSrc+yZpJRBSMs6V0OW938PMBUxFitkyQUPNiet4ybqG5FVSuuRnhgUSzYnrheZVN8dNGFnaxjHBv15glBzjVM+zqBWZ+xJ3d43bZ4cZs8NNqcUf6vTOQraezAdd6hVm0lb4Kd8zwCqhODp0Q3pk5JskLRP+HAp+ghlvM470UNXyCFu2krsc7qNEB99krvrk7fNEKfNVrKKvHI/L+ADN9pKdFmDgrqBnxsPqNWXbCJNjhxlnjp7t5X787G1w7T7gSA64WLa4MKkNyhJ4/Q+GaqRdizswAU7vEPDvCKk+wigNERkh8ShatoYUjtGDEhPa3wQY/rAbXi5KQ5bi1W0y8Hor6ifG6W8BsXrAo9xBUjoDqa3oZHCKyG9cQTgLDjjAXHnU6hEf7kPw88nFtasp7MOYhIWTEzbBHR+R+IzJPBHOPxaBJ0b2aOJoSAwIMYY7H1R4tSUdD8ingeVJ4tnkCuGFMVrZJ8l54OhERpsZ5GAzhQxguhGDx/h+w7HbgT4M/Cv4g5CSRTEnnJDnNPn8mHFxOGHFSihnhLxeukYeG+SrVAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Change IAM Identity Center Source&quot;
        title=&quot;&quot;
        src=&quot;/static/76d762a4e863f47ea515eea1fd9966bc/f058b/identity-source.png&quot;
        srcset=&quot;/static/76d762a4e863f47ea515eea1fd9966bc/c26ae/identity-source.png 158w,
/static/76d762a4e863f47ea515eea1fd9966bc/6bdcf/identity-source.png 315w,
/static/76d762a4e863f47ea515eea1fd9966bc/f058b/identity-source.png 630w,
/static/76d762a4e863f47ea515eea1fd9966bc/40601/identity-source.png 945w,
/static/76d762a4e863f47ea515eea1fd9966bc/636d3/identity-source.png 1222w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;ol start=&quot;7&quot;&gt;
&lt;li&gt;Select External Identity Source&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;We will be using Authentik as the IdP of AWS (our SP).&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/d4768738b78ed287a00f66b91d6b0d5e/669eb/external-identity-source.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 34.810126582278485%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAYAAAAIy204AAAACXBIWXMAAAsTAAALEwEAmpwYAAABQklEQVR42oVRy2rDMBD0ubfSOPFLkRzZsi2/EzuJk6aBUCj9lf7/fbqSMRRK6WEYsVrNzK6cqDzDz4/w1AGrMAFLarTjG5rhZpFWAzbbDB7P4RP7fD57osDGgM4bPrPLFJydHhEXA3i+t2Jx1qE+vEJ3E8r+Yjmrj1DVCFFfCNPclzZQqoFMKiRpBZU1iMjM4VQ0IsHiRilksSexyYrFeQ8mK6TlAN1fwVUHn3oLqmlNxppMiwkh66BFCWdNMY2IYTdKLQvVWjGbKmsR7kooSnm5f2CYHqjHG7TqsYrPCKsHWP3Ac3bHTjSz4E8s+zL7NAYvgbTpI0okaJItjeuTQS5ruPKKsP3AOr3iiZ8htn8IBrG2Yy0wCY3Jcu8SMupJ5EC7PIGFJTgr0cjit+Dy6D+szY9vUwRMYiU6vJ8SfH16+AYBYMjJSvA7LAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Use External Identity Provider&quot;
        title=&quot;&quot;
        src=&quot;/static/d4768738b78ed287a00f66b91d6b0d5e/f058b/external-identity-source.png&quot;
        srcset=&quot;/static/d4768738b78ed287a00f66b91d6b0d5e/c26ae/external-identity-source.png 158w,
/static/d4768738b78ed287a00f66b91d6b0d5e/6bdcf/external-identity-source.png 315w,
/static/d4768738b78ed287a00f66b91d6b0d5e/f058b/external-identity-source.png 630w,
/static/d4768738b78ed287a00f66b91d6b0d5e/40601/external-identity-source.png 945w,
/static/d4768738b78ed287a00f66b91d6b0d5e/669eb/external-identity-source.png 1244w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;ol start=&quot;8&quot;&gt;
&lt;li&gt;Copy the SP metadata provided&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;We are going to use these values in the Terraform Authentik configuration soon so do not close this window yet!&lt;/p&gt;
&lt;ol start=&quot;9&quot;&gt;
&lt;li&gt;Get credentials to Authentik&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Soon we are going to configure Authentik using Terraform. Authentik Terraform provider requires a Token to execute HTTP calls to execute actions and configure it. Also, we will need to configure Grafana&apos;s OAuth provider using the created client ID and client secret. We can get these values from the outputs of the main Terraform code we just executed.&lt;/p&gt;
&lt;p&gt;Run the Terraform command to get these values:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;terraform output authentik_endpoint
terraform output authentik_token
terraform output grafana_oauth_client_id
terraform output grafana_oauth_client_secret&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If you would like to log in as admin you will use the email used to configure Authentik and the password available in the &lt;code class=&quot;language-text&quot;&gt;authentik_password&lt;/code&gt; output (&lt;code class=&quot;language-text&quot;&gt;terraform output authentik_password&lt;/code&gt;).&lt;/p&gt;
&lt;ol start=&quot;10&quot;&gt;
&lt;li&gt;Initialize the Authentik Terraform module&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;cd&lt;/span&gt; authentik
terraform init&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ol start=&quot;11&quot;&gt;
&lt;li&gt;Create a file to define variables&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The Authentik terraform module needs some variables to be defined. Here is an example:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;hcl&quot;&gt;&lt;pre class=&quot;language-hcl&quot;&gt;&lt;code class=&quot;language-hcl&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# authentik/vars.tfvars&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;authentik_url&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https://sso.mydomain.com/&quot;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# Use the Terraform output&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;authentik_token&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;123ABC&quot;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# Use the Terraform output&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;authentik_insecure&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# Set to true if you set use_staging_certificate to true&lt;/span&gt;

&lt;span class=&quot;token property&quot;&gt;grafana_url&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https://monitoring.mydomain.top&quot;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# Use the Terraform output&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;grafana_client_id&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;1a2b3c&quot;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# Use the Terraform output&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;grafana_client_secret&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;123abc&quot;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# Use the Terraform output&lt;/span&gt;

&lt;span class=&quot;token property&quot;&gt;aws_sign_in_url&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https://ABC.awsapps.com/start&quot;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# Use the AWS value&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;aws_acs_url&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;https://us-east-1.signin.aws.amazon.com/platform/saml/acs/DEF&quot;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# Use the AWS value&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;aws_issuer_url&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https://us-east-1.signin.aws.amazon.com/platform/saml/GHI&quot;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# Use the AWS value&lt;/span&gt;

&lt;span class=&quot;token property&quot;&gt;set_scim&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;aws_scim_url&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;aws_scim_token&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We are not setting SCIM yet. We need to first set up the external Identity Provider for the Identity Center before configuring the SCIM.&lt;/p&gt;
&lt;ol start=&quot;12&quot;&gt;
&lt;li&gt;Apply Authentik configuration&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If Authentik is already available you can now configure it using Terraform&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;terraform apply --var-file&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;vars.tfvars&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Here we are going to create:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;SAML provider for AWS&lt;/li&gt;
&lt;li&gt;OIDC provider for Grafana&lt;/li&gt;
&lt;li&gt;AWS and Grafana application&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Some important things to mention:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://grafana.com/docs/grafana/latest/setup-grafana/configure-security/configure-authentication/generic-oauth/&quot;&gt;Grafana documentation&lt;/a&gt; specifies the redirect URI&lt;/li&gt;
&lt;li&gt;AWS Identity Center does not support &lt;a href=&quot;https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_create_saml.html&quot;&gt;encrypted SAML assertion&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;AWS Identity Center supports &lt;a href=&quot;https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_create_saml_assertions.html&quot;&gt;post binding&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start=&quot;13&quot;&gt;
&lt;li&gt;Upload SAML metadata and IdP Cert to AWS External Identity Provider configuration&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;You will notice that when applying the module two files were generated: &lt;code class=&quot;language-text&quot;&gt;authentik/cert.pem&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;authentik/metadata.xml&lt;/code&gt;. These files can be obtained in the Authentik but for simplicity, we extracted them via Terraform.
Upload these to the AWS Console:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;authentik/metadata.xml&lt;/code&gt; should be uploaded in the &quot;IdP SAML metadata&quot; field&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;authentik/cert.pem&lt;/code&gt; should be uploaded in the &quot;IdP certificate&quot; field&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;PS: I noticed a bug that I couldn&apos;t fix that it might happend here. The first Terraform apply execution of the authentik module can generate and empty the &lt;code class=&quot;language-text&quot;&gt;metadata.xml&lt;/code&gt; file. If this happens simply rerun the &lt;code class=&quot;language-text&quot;&gt;terraform apply --var-file=vars.tfvars&lt;/code&gt; command and the file will be created correctly. If the file is empty AWS will complain (of course!).&lt;/p&gt;
&lt;ol start=&quot;14&quot;&gt;
&lt;li&gt;Enable SCIM&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Go to AWS Console on the Identity Center Settings page. You will see a message to enable SCIM. Click to enable. An endpoint and a token will be generated. Copy these values because we are going to need it the next step.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/1a2017a3ce737c5790f62de17aa7d76f/36c33/scim.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 13.924050632911392%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAADCAYAAACTWi8uAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAoUlEQVR42mWNTQuCQBRF3UQ6tggqyHRGrWzj1zBakgSlqbkR+v9/5jaZWNDicO69PHiKETVgvIEjWlBpwk7Q6LG3bmfQ35YQJjf62Ujfv6hWCiuu4WUdFOaXCM4d4ssTh7SFHZYjblSBBQW2cSWpx+xEd1C/6G/24oHVLsdkGUJdcygLL8fGv8IMbpi78rsloNNkQAwkmLHf7T9rJsfU4HgBhN5W5eCSVGsAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Enable SCIM in AWS IAM Identity Center Console&quot;
        title=&quot;&quot;
        src=&quot;/static/1a2017a3ce737c5790f62de17aa7d76f/f058b/scim.png&quot;
        srcset=&quot;/static/1a2017a3ce737c5790f62de17aa7d76f/c26ae/scim.png 158w,
/static/1a2017a3ce737c5790f62de17aa7d76f/6bdcf/scim.png 315w,
/static/1a2017a3ce737c5790f62de17aa7d76f/f058b/scim.png 630w,
/static/1a2017a3ce737c5790f62de17aa7d76f/40601/scim.png 945w,
/static/1a2017a3ce737c5790f62de17aa7d76f/36c33/scim.png 946w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;ol start=&quot;15&quot;&gt;
&lt;li&gt;Deploy SCIM via Terraform&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Modify the &lt;code class=&quot;language-text&quot;&gt;authentik/vars.tfvars&lt;/code&gt; file to enable SCIM. The SCIM variables should look like this:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;hcl&quot;&gt;&lt;pre class=&quot;language-hcl&quot;&gt;&lt;code class=&quot;language-hcl&quot;&gt;&lt;span class=&quot;token property&quot;&gt;set_scim&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# This should have &quot;true&quot; value now!&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;aws_scim_url&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https://scim.AWS_REGION.amazonaws.com/ABCDEF/scim/v2&quot;&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;aws_scim_token&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;GIANT_TOKEN&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Rerun the apply&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;terraform apply --var-file&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;vars.tfvars&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This will create:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;SCIM provider for AWS. AWS says that even when using an external identity provider you still need to &lt;a href=&quot;https://docs.aws.amazon.com/singlesignon/latest/userguide/manage-your-identity-source-idp.html&quot;&gt;provision all applicable users and groups into AWS Identity Center&lt;/a&gt;. This is very tedious, but here SCIM comes to the rescue! SCIM is a standard to simplify the exchange of user identity information between identity centers. Since AWS supports SCIM we can use and take advantage of it! In other words, every time a user (that is from the AWS group) is created, Authentik will do an API call to the AWS SCIM server to also create this user there. The AWS SCIM implementation requires some specification and has some &lt;a href=&quot;https://docs.aws.amazon.com/singlesignon/latest/developerguide/createuser.html&quot;&gt;constraints&lt;/a&gt; so that&apos;s why we carefully used a Python script to specify the fields we need to pass to the AWS SCIM provider. Also, another important thing to mention is that the AWS external Identity Center requires us to use &lt;a href=&quot;https://docs.aws.amazon.com/singlesignon/latest/userguide/other-idps.html&quot;&gt;email&lt;/a&gt; as the NameID to identify the user.&lt;/li&gt;
&lt;li&gt;Two users: &quot;dev&quot; (that should only have ReadOnly permissions in the AWS account and Viewer permission in Grafana) and &quot;admin&quot; (should have AdministratorAccess in the AWS account and Admin permission in Grafana). We are creating passwords for these users using Terraform, this is not ideal in a real-world scenario: once again, this is a demo.&lt;/li&gt;
&lt;li&gt;Groups: AWS groups (all users that need AWS access should be placed here since in a real-world scenario we don&apos;t want to create users that do not need AWS access to be created there), Admin AWS group, Dev AWS group, Grafana Admin group, Granafa Viewer group.&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start=&quot;16&quot;&gt;
&lt;li&gt;Manually sync the SCIM&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;In case SCIM was not synced (i.e. users were not created in AWS IAM Identity Center) we should manually sync before moving on. Log in as admin (the email you set up and password of &lt;code class=&quot;language-text&quot;&gt;authentik_password&lt;/code&gt; in the main terraform code) and go to &lt;code class=&quot;language-text&quot;&gt;Applications&lt;/code&gt; &gt; &lt;code class=&quot;language-text&quot;&gt;Providers&lt;/code&gt; &gt; &lt;code class=&quot;language-text&quot;&gt;aws-scim&lt;/code&gt; &gt; hit the sync button. Now, you can see in the AWS IAM Identity Center console (Users and Groups section) the users and groups created by the Authentik Terraform module.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/a8703c81b85961e8b6d8b8ed1032c285/ae694/sync-scim.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 80.37974683544303%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAQCAYAAAAWGF8bAAAACXBIWXMAAAsTAAALEwEAmpwYAAACl0lEQVR42o1T2W7UQBD0P6Cs7xmPx/f6WnvvTTYkJCEoKIqExAsSL3wOH110z8YQARE8lHoOu6arq9vaJxqfqhQbHeOQMDR2FEOpEIQC/WLA/nCOi+MlhnEJ1/NPcD04hGnPaz8IYfVRhLtUYatPZIwVrQMRYeZ4SLLcENVNi1BIzGwHZzPbRNtxf64ZTGpldYdyWEOkBc4CIgkVHKkRxwmGpkGeJIh1gnndQBBhTWdt1yMvSigVo2k781hRVlCxhqWTDMOwgtIZ/DCiLEiqiKFkhFJK6CAg6RIyUvD8kLLwzZ6zdb2QZAoDPjOSpUqwGNfoiTSULFWZaHsB4qxAR3e8d32BNK9w+fYa5byFQ2RFWWPe9CaZ6Rsi1OgWSzTdAiLS5iIkyR5dxjpF2w/mB/44K1ps91dEXBKhQEXl2h2OJhqFlKk1b3sMyw3iJDeSOUMmlhG/GBqi6ZwjE/ksj9ac8XZ/jvX2gHG1NZ1hZebwSJmMiOgVRcQxGSSI0CeDApWZmgamtupZQQwvkCiqBh8fn3Bzd29UCia8PRzw+ctXfHh4xPXNe7y7vcfd/QP6cQXPdRE6b0w/viRlTITL9RY1qeS6sqGWjE41TMgAfpnlCjIq8Khx55ewn77Dy8kYcvB3UgaXhA1yA3ZZwqrJDK4FH0x1ElECKQVsvYB9/g12VCEgQi4B378ED8CEkylk+3K9M/ZPNeJW4sxlGKLU5G6WkcTJjNfhcR8y0bjaYL3Zk+zSvBrFqXEsKyocr26pNS7A7WVMEq/DSOY+Mw499yBfSJUaAt6nZYusbKgDCvh/qeEfhAuakLpd0MfqFyFlGJFsl+dad5glI5x0gK/yfxPOm86MHtdt6jFecx/y3PL8Mlwaxf/BDz9DxdUOhFLzAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Sync SCIM&quot;
        title=&quot;&quot;
        src=&quot;/static/a8703c81b85961e8b6d8b8ed1032c285/f058b/sync-scim.png&quot;
        srcset=&quot;/static/a8703c81b85961e8b6d8b8ed1032c285/c26ae/sync-scim.png 158w,
/static/a8703c81b85961e8b6d8b8ed1032c285/6bdcf/sync-scim.png 315w,
/static/a8703c81b85961e8b6d8b8ed1032c285/f058b/sync-scim.png 630w,
/static/a8703c81b85961e8b6d8b8ed1032c285/ae694/sync-scim.png 850w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;ol start=&quot;17&quot;&gt;
&lt;li&gt;Initialize the AWS Organizations Terraform module&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;In the previous module, we created the users and groups. Now, from the AWS side, we need to configure this group to access the AWS account and configure their permissions. First, let&apos;s initialize the Terraform module.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;cd&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;/organizations
terraform init&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ol start=&quot;18&quot;&gt;
&lt;li&gt;Create a file to define variables&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;# organizations/vars.tfvars
aws_region = &quot;us-east-1&quot;
instance_arn = &quot;arn:aws:sso:::instance/ssoins-abc123&quot;
identity_store_id = &quot;GHI&quot;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;instance_arn&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;identity_store_id&lt;/code&gt; variables can be obtained in the settings page of the Identity Center.&lt;/p&gt;
&lt;ol start=&quot;19&quot;&gt;
&lt;li&gt;Deploy Organizations module&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;terraform apply --var-file&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;vars.tfvars&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Here we create the permission sets (for administrators and developers) and assign these permission sets to the management account.&lt;/p&gt;
&lt;ol start=&quot;20&quot;&gt;
&lt;li&gt;Log in using admin or dev user&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Log in using the &lt;code class=&quot;language-text&quot;&gt;dev&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;admin&lt;/code&gt; user. The credentials are available in the Authentik Terraform module!&lt;/p&gt;
&lt;p&gt;You should be able to see the following application on the Authentik home screen:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/c4f897394e190a6972889c4e5802baa2/62da8/authentik-apps.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 30.37974683544304%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAGCAYAAADDl76dAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAt0lEQVR42p2PyQrCQAyGM20yXRxPdplu6klcwIMXEVHE93+n3+koUjxI6eFLwpB8mdDORNgkgnXEyCQEEUE5aARxHCNJU0RaIzVzLLolSERgbYW6bTEz5q9Aa3HD8l06xL8pBQpcME7U029kZjeoff4VchBCc4hQ0QflYZ8JQaD6PkKWl7hcb2jaFaqmw/3xhK2aUWcP8T/si7yw2O5PsHWH0jY4HM8obDtdyCzuzAgi+o2vZZLwBWKjf/6bB1qqAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;AWS and Grafana Apps&quot;
        title=&quot;&quot;
        src=&quot;/static/c4f897394e190a6972889c4e5802baa2/f058b/authentik-apps.png&quot;
        srcset=&quot;/static/c4f897394e190a6972889c4e5802baa2/c26ae/authentik-apps.png 158w,
/static/c4f897394e190a6972889c4e5802baa2/6bdcf/authentik-apps.png 315w,
/static/c4f897394e190a6972889c4e5802baa2/f058b/authentik-apps.png 630w,
/static/c4f897394e190a6972889c4e5802baa2/40601/authentik-apps.png 945w,
/static/c4f897394e190a6972889c4e5802baa2/78612/authentik-apps.png 1260w,
/static/c4f897394e190a6972889c4e5802baa2/62da8/authentik-apps.png 1262w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Now, let&apos;s log in the AWS using the Dev and Admin to see the differences. First, let&apos;s use the Dev user:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/ebedc429ef0d27a01eed85fc7ddb287a/c679a/dev-aws.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 30.37974683544304%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAGCAYAAADDl76dAAAACXBIWXMAABYlAAAWJQFJUiTwAAAA0klEQVR42pWR246DIBRF+YS2UaGjCGpFRKvVml5Gk5n//6ndow+TmE6T9mHlBAJrbwKz7RXHYYJtb+DKwpfmI/ZZg9j2kPYMRZO54QemG1GfRyS2gx/lCOLiLVkQW+yr7wXuRujqAvZFi6iZkJQdZN7AI+FrQbGG9rgqIUwPkZ+WM8zUA/rbL6p+ImELTS1jmvPzhS5XPDWncJ7U1PAOYS+LnIWpQ2ocVFYhiFL4YUrJhyfZzL/NSeqFGd07LI3ZnMoVJWuHnSywCSS2Qn/4OcUfD7jVphqKwG7QAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Dev AWS Page&quot;
        title=&quot;&quot;
        src=&quot;/static/ebedc429ef0d27a01eed85fc7ddb287a/f058b/dev-aws.png&quot;
        srcset=&quot;/static/ebedc429ef0d27a01eed85fc7ddb287a/c26ae/dev-aws.png 158w,
/static/ebedc429ef0d27a01eed85fc7ddb287a/6bdcf/dev-aws.png 315w,
/static/ebedc429ef0d27a01eed85fc7ddb287a/f058b/dev-aws.png 630w,
/static/ebedc429ef0d27a01eed85fc7ddb287a/40601/dev-aws.png 945w,
/static/ebedc429ef0d27a01eed85fc7ddb287a/78612/dev-aws.png 1260w,
/static/ebedc429ef0d27a01eed85fc7ddb287a/c679a/dev-aws.png 2460w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Now let&apos;s check with the Admin user:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/c06b7095b1a961c7c3a4f2f3217ca9d1/c679a/admin-aws.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 30.37974683544304%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAGCAYAAADDl76dAAAACXBIWXMAABYlAAAWJQFJUiTwAAAA00lEQVR42pWR0Y6CMBBF+wm6kRYFWspKKQXFCrquNdH//6nrwMMmxjXRh5NJJp0zd1Jmux9shgtsd4JQFlFmPiI1HmnloeoB2vVgbrjC+IC2D9DWI0pLcFm9JePSYtmciQDhApQ7gK2okW4v0LVHVm6xIOFrQfUI9YSqsTQ94nI3vWGmHbA/3dDsAwk75JRSUh3Pj/P6gafktFzolhL+IrbHSc6SwqEwDmrdgCcFIoJn5ZNs5N/kJF0k3zS3nhIzLumUcbt2+ModZkJiHucffk71xx3RfqYzl5Z7RgAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Admin AWS Page&quot;
        title=&quot;&quot;
        src=&quot;/static/c06b7095b1a961c7c3a4f2f3217ca9d1/f058b/admin-aws.png&quot;
        srcset=&quot;/static/c06b7095b1a961c7c3a4f2f3217ca9d1/c26ae/admin-aws.png 158w,
/static/c06b7095b1a961c7c3a4f2f3217ca9d1/6bdcf/admin-aws.png 315w,
/static/c06b7095b1a961c7c3a4f2f3217ca9d1/f058b/admin-aws.png 630w,
/static/c06b7095b1a961c7c3a4f2f3217ca9d1/40601/admin-aws.png 945w,
/static/c06b7095b1a961c7c3a4f2f3217ca9d1/78612/admin-aws.png 1260w,
/static/c06b7095b1a961c7c3a4f2f3217ca9d1/c679a/admin-aws.png 2460w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;We will see the same behavior when using accessing Grafana. First, let&apos;s use Dev user:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/a857ebe924ed91c303803e8e6a72ed43/d43b4/grafana-dev.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 13.29113924050633%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAADCAYAAACTWi8uAAAACXBIWXMAAAsTAAALEwEAmpwYAAAATklEQVR42mPQ0jX8LyEjh4ElZeXBGJscLiwuLfufQUxK9r+UnCIKlpRV+K+kpvlfVUMHzEaXx4VlFJT/M0A0YGIZBaX/ckqqWOVwYZCBANH7UH7BzJ6GAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Dev Grafana Page&quot;
        title=&quot;&quot;
        src=&quot;/static/a857ebe924ed91c303803e8e6a72ed43/f058b/grafana-dev.png&quot;
        srcset=&quot;/static/a857ebe924ed91c303803e8e6a72ed43/c26ae/grafana-dev.png 158w,
/static/a857ebe924ed91c303803e8e6a72ed43/6bdcf/grafana-dev.png 315w,
/static/a857ebe924ed91c303803e8e6a72ed43/f058b/grafana-dev.png 630w,
/static/a857ebe924ed91c303803e8e6a72ed43/40601/grafana-dev.png 945w,
/static/a857ebe924ed91c303803e8e6a72ed43/d43b4/grafana-dev.png 1202w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Then, admin user:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/d0c4e3a1523ddb515fa6f8e292a2d887/d43b4/grafana-admin.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 13.29113924050633%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAADCAYAAACTWi8uAAAACXBIWXMAAAsTAAALEwEAmpwYAAAATklEQVR42mPQ0jX8LyEjh4ElZeXBGJscLiwuLfufQUxK9r+UnCIKlpRV+K+gov5fTUsPzEaXx4VlFJT/M0A0YGJpecX/ckqqWOVwYZCBANC5UHqUaIyuAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Admin Grafana Page&quot;
        title=&quot;&quot;
        src=&quot;/static/d0c4e3a1523ddb515fa6f8e292a2d887/f058b/grafana-admin.png&quot;
        srcset=&quot;/static/d0c4e3a1523ddb515fa6f8e292a2d887/c26ae/grafana-admin.png 158w,
/static/d0c4e3a1523ddb515fa6f8e292a2d887/6bdcf/grafana-admin.png 315w,
/static/d0c4e3a1523ddb515fa6f8e292a2d887/f058b/grafana-admin.png 630w,
/static/d0c4e3a1523ddb515fa6f8e292a2d887/40601/grafana-admin.png 945w,
/static/d0c4e3a1523ddb515fa6f8e292a2d887/d43b4/grafana-admin.png 1202w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Load test your ECS deployment]]></title><description><![CDATA[In this blog post we are going to deploy an API using ECS and enable auto-scaling using AWS CDK. We will load test our application using…]]></description><link>https://felipetrindade.com/ecs-load-testing/</link><guid isPermaLink="false">https://felipetrindade.com/ecs-load-testing/</guid><pubDate>Tue, 01 Apr 2025 12:05:32 GMT</pubDate><content:encoded>&lt;p&gt;In this blog post we are going to deploy an API using ECS and enable auto-scaling using AWS CDK. We will load test our application using Locust and validate in the AWS Console that our application is able to scale. In reality, a bunch of services and components will be created along the way!&lt;/p&gt;
&lt;p&gt;As always, the code used here is available in my &lt;a href=&quot;http://github.com/felipelaptrin/ecs-load-test&quot;&gt;GitHub Repository&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Proposed Architecture&lt;/h2&gt;
&lt;p&gt;For this demo I will use a very similar architecture that I proposed on my &lt;a href=&quot;https://www.felipetrindade.com/cdk-multi-account/&quot;&gt;Multi-account deployment using AWS CDK&lt;/a&gt; blog post. For simplicity I will skip the CodePipeline deployment and deploy everything manually, but you can easily modify the provided code here to deploy using CodePipeline if you followed the Multi-Account with CDK blog post.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/88172422e8d34bb4a86d198d9d25043f/160a3/Architecture.drawio.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 105.69620253164558%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAVCAYAAABG1c6oAAAACXBIWXMAAAsTAAALEwEAmpwYAAADWUlEQVR42pWTy24cRRiF/RY8CWLN+/AAbFizsGTEwogNERZISAgkJDBBySa3sZTEVoCJx5MZzYyne259v1V3V/XtcKp6SCwiJLukM1093fX1X+c/deB88wB1IlCnOdQuQhMLtKpC03aomxZtqdCpGrcdB13HhVMP5cM36AjQoyxLZMPfIWYDSA2r7gDUP/nxU/gfHkH4EdzARxSGEF99jPznTyDJ6u4CbPiyO7axfvQaQuR9hUUB5+oM/vxPKFkR2NwBKCT9atF09KvmQnrXNY3xUEtXpz29vYeEQXtXt8bDjlBdkQZ1FatTzVtvbwVMnl6iSQs0SW6uuqvQld5QD7+F2MCD+OEr1FGG2k9Rh4yM9sxsmzYQ9r7q/5Vec6ABZnT7C2OUiQRx5qKUBYqygJRapZFSCoqLFe0w87101JSUBOrgEtbWHcyEY+dP8WJ6gnU8wyqaIFOMURWhoDo2D+1eNwrRjZWl1DmsMHoMnB7xWdtXWEqBMPQwG3rwkwBi/RLFg0OE2yF71yD48h7ik596XkTf7ZjheAusMXrS4PSLEnx3D8zheFsMn6/gBC6i1Tk2vx3C2VxCVgqb42/h/fCLASaDKfLhCszHHsij4GYRroMtKt0pAhU9E2KDtTNBlNqYW1cYvHyMjTs3kOligeXKNnPL5kdd38wNUFugzi3kJy+g+EdDs+0ownnoQ9CCiPd5VaJsJIRiY7gNxeBLSjENJRuUsSEVPVT9lgHx9TO4Hx0h9gIkcYiJ6+HHawtX8zGG1hLLLMdaCKzzHHaawcoyrIvSyMoE5lGMhKep0l1uOPGWO+yGC7Zemi5KnpRIZNh9+gH8Xz/DjMl6Ze9wYTmYxxlmfkB4gZ1U5rqIk3dAE2ToI9wZ//RRzPminaYY3T/G6O9nWFw+wfKPQyxOP8ds/BzTMIWVpJiHEeZBiOv/As1o+0C1GkhP9Jfn3JLNzs9HA9hn32M1+A6T8QXeeCGWhJwNX+NiMjVbj1lEDyxkH8CmD2tHkwW9WqcCM8fFKo4xdgL8tek1cQMsuOUVn2/zEhtRYEWPY0Ir6r2jZyrkl3ye2w3BvcRe2f6aw2P3/5XLuGUsxFQobQ+VG6FyeqldiPR6i8zaQdjOO1l77e/185vSa4qNj38AdNhEw/ikZOoAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;The proposed architecture for this blog post. Several AWS Services are being used: Route53, CloudFormation, ECR, ECS, Fargate, CloudWatch Alarm, Certificate Manager, Application Load Balancer, NAT.&quot;
        title=&quot;&quot;
        src=&quot;/static/88172422e8d34bb4a86d198d9d25043f/f058b/Architecture.drawio.png&quot;
        srcset=&quot;/static/88172422e8d34bb4a86d198d9d25043f/c26ae/Architecture.drawio.png 158w,
/static/88172422e8d34bb4a86d198d9d25043f/6bdcf/Architecture.drawio.png 315w,
/static/88172422e8d34bb4a86d198d9d25043f/f058b/Architecture.drawio.png 630w,
/static/88172422e8d34bb4a86d198d9d25043f/160a3/Architecture.drawio.png 682w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Some important points to mention about the proposed architecture:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Expose application using ALB&lt;/strong&gt;: The API will be deployed in a private subnet and the service will be exposed via Application Load Balancer (ALB). The Security Group of the API needs to allow inbound traffic from the ALB.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cross Account Image Registry&lt;/strong&gt;: The artifacts (docker images) will be deployed in a centralized account that can be used by other workload accounts (e.g. Dev, Staging, Prod...). This is important to assure artifact promotion and was already discussed in the &lt;a href=&quot;https://www.felipetrindade.com/consistent-deployments/&quot;&gt;Consistent deployments - a real-world case scenario&lt;/a&gt; blog post. Since the API is deployed in a private subnet it required to deploy a &lt;a href=&quot;https://docs.aws.amazon.com/AmazonECR/latest/userguide/vpc-endpoints.html&quot;&gt;VPC Endpoint&lt;/a&gt; or NAT Gateway (I will use this!) to reach ECR service to pull the docker image.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;HTTPs handled by ALB&lt;/strong&gt;: The users of the API will communicate over HTTPs with the load balancer which is responsible for handling the HTTPs (using a certificate issued by AWS Certificate Manager) connection, while HTTP will be used in the communication between ALB and API (SSL termination at ALB).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Route53 should be created in advance&lt;/strong&gt;: I will consider that a Route53 hosted zone is already in place and ready to use. If you do not have a domain I highly recommend you to buy a cheap one for testing and experimenting (you can easily find it for under 2 dollars per year!).&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;API&lt;/h2&gt;
&lt;p&gt;I will develop a super simple API that has two routes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;GET /api/health&lt;/code&gt;: A simple health check. It&apos;s a must-have route to allow the ALB to check if the task is healthy.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;GET /api/count-prime/{limit}&lt;/code&gt;: Calculates the number of prime numbers lower than a given value. This route is enough to consume some CPU and it will work great during our load test.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let&apos;s check our simple API code:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;py&quot;&gt;&lt;pre class=&quot;language-py&quot;&gt;&lt;code class=&quot;language-py&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; time
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; os
&lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; typing &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; Optional

&lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; fastapi &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; FastAPI&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; HTTPException
&lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; fastapi&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;responses &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; JSONResponse

&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; random

app &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; FastAPI&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    title&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Backend&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    description&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;An API with endpoints designed to simulate CPU and memory load.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    version&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;1.0.0&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    root_path&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/api&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;


&lt;span class=&quot;token decorator annotation punctuation&quot;&gt;@app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/health&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; summary&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Healthcheck Endpoint&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;healthcheck&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;dict&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;status&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;ok&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;


&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;is_prime&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;n&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token triple-quoted-string string&quot;&gt;&quot;&quot;&quot;Check if a number is prime.&quot;&quot;&quot;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; n &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;False&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;# Only test up to the square root of n for efficiency&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; i &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;n&lt;span class=&quot;token operator&quot;&gt;**&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; n &lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;False&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;True&lt;/span&gt;


&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;count_primes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;limit&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token triple-quoted-string string&quot;&gt;&quot;&quot;&quot;Count the number of prime numbers below the given limit.&quot;&quot;&quot;&lt;/span&gt;
    count &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; num &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; limit&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; is_prime&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;num&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
            count &lt;span class=&quot;token operator&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; count


&lt;span class=&quot;token decorator annotation punctuation&quot;&gt;@app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/count-prime/{limit}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; summary&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Simulate CPU Load&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;simulate_cpu&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;limit&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;dict&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    start_time &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; time&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;time&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    result &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; count_primes&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;limit&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    duration &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; time&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;time&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; start_time

    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;limit&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; limit&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;result&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; result&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;durationSeconds&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; duration&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;


&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; __name__ &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;__main__&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; uvicorn

    uvicorn&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;run&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&quot;main:app&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; host&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;0.0.0.0&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; port&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;8000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; workers&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;os&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;getenv&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;NUMBER_OF_WORKERS&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The github repository containing this code also contains some other pieces, such as &lt;code class=&quot;language-text&quot;&gt;requirements.txt&lt;/code&gt; (dependencies) and &lt;code class=&quot;language-text&quot;&gt;Dockerfile&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Load Test&lt;/h2&gt;
&lt;p&gt;Since you are already familiar with the API code, we are ready to understand the load testing. We are going to use one of the most widely used frameworks to load test: &lt;a href=&quot;https://locust.io/&quot;&gt;locust&lt;/a&gt;. The documentation is great and it&apos;s simple to use. If you have never used it, don&apos;t worry, you will see how easy it is now:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;py&quot;&gt;&lt;pre class=&quot;language-py&quot;&gt;&lt;code class=&quot;language-py&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; json

&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; logging
&lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; locust &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; HttpUser&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; task&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; between
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; random

logger &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; logging&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;getLogger&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;logger&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
logging&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;basicConfig&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    level&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;getattr&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;logging&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;INFO&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; logging&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;INFO&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token builtin&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;%(asctime)s - %(levelname)s - %(message)s&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;


&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Test&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;HttpUser&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    wait_time &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; between&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token decorator annotation punctuation&quot;&gt;@task&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;prime&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;self&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        limit &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; random&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;randint&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;10_000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;20_000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        logger&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;info&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-interpolation&quot;&gt;&lt;span class=&quot;token string&quot;&gt;f&quot;Calculating the number of prime number below &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;limit&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;...&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        response &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;client&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;get&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-interpolation&quot;&gt;&lt;span class=&quot;token string&quot;&gt;f&quot;/api/count-prime/&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;limit&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; name&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;count-prime&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;raise_for_status&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;When running locust we should specify the max number of users to simulate and the spawn rate (i.e. how fast new users will be created). The load test starts with 0 users and goes up to the maximum number of users defined in the test using the spawn rate defined (e.g. 1 new user per second).&lt;/p&gt;
&lt;p&gt;Each user in our load test waits between 1 and 3 before sending an HTTP request to our API (and this will keep repeating until the end of the load test). The limit number to calculate the number of prime is a random number between 10,000 and 20,000.&lt;/p&gt;
&lt;h2&gt;IaaC with CDK&lt;/h2&gt;
&lt;p&gt;We are going to create two stacks in our CDK code:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;EcrStack&lt;/code&gt;: Created ECR repository that will contain the API image. Should be deployed in the Shared Assets account.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;EcsStack&lt;/code&gt;: Create the entire ECS infrastructure, VPC, Certificates, Route53 record... Should be deployed in the Development account.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// main.ts&lt;/span&gt;
#&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;usr&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;bin&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;env node
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; cdk &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;aws-cdk-lib&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; EcsStack &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;./stack/ecs&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; EcrStack &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;./stack/ecr&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; devConfig&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; sharedAssetsConfig &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;./config&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; app &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;cdk&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;App&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;EcsStack&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;app&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;EcsStack&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; devConfig&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;EcrStack&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;app&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;EcrStack&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; sharedAssetsConfig&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The main code basically instantiate both stacks. Let&apos;s check in depth the &lt;code class=&quot;language-text&quot;&gt;EcrStack&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// stack/ecr.ts&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; cdk &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;aws-cdk-lib&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  Repository&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  RepositoryEncryption&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  TagMutability&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  TagStatus&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;aws-cdk-lib/aws-ecr&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; Construct &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;constructs&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; AwsAccount &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;../config&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; AccountPrincipal&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; CompositePrincipal &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;aws-cdk-lib/aws-iam&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;EcrProps&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;cdk&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;StackProps &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  ecrRepository&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  maxAgeOtherTagsInDays&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;EcrStack&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;cdk&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Stack &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;scope&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Construct&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; id&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; props&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; EcrProps&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;scope&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; props&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; awsAccount &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;AwsAccount&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Development&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; allAwsAccounts &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; awsAccount&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;accountId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AccountPrincipal&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;accountId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; principals &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CompositePrincipal&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;allAwsAccounts&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; repositoryName &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ecrRepository&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; repository &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Repository&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; repositoryName&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        repositoryName&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; repositoryName&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        removalPolicy&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; cdk&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;RemovalPolicy&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;DESTROY&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        imageTagMutability&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; TagMutability&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;MUTABLE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        encryption&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; RepositoryEncryption&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;KMS&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      repository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;grantPull&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;principals&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      repository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addLifecycleRule&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        tagPrefixList&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;dev&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        maxImageCount&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        rulePriority&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      repository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addLifecycleRule&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        description&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;Delete images with other tags after &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;maxAgeOtherTagsInDays&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; days&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        tagStatus&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; TagStatus&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;TAGGED&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        maxImageAge&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; cdk&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Duration&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;days&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;maxAgeOtherTagsInDays&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        tagPatternList&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;*&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        rulePriority&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    cdk&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Tags&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;CreatedBy&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;CDK&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The stack is simple, it basically creates ECR repositories (in our case we will create only a single repository) and allows cross-account pull from the Development account. Some lifecycle rule policies were also created to reduce costs. The tagging strategy used here is tag based on the environment name, i.e. tag named &lt;code class=&quot;language-text&quot;&gt;dev&lt;/code&gt; will be used by the Development environment while a tag named &lt;code class=&quot;language-text&quot;&gt;prod&lt;/code&gt; should be used in the Production environment.&lt;/p&gt;
&lt;p&gt;Now, let&apos;s check how the &lt;code class=&quot;language-text&quot;&gt;EcsStack&lt;/code&gt; looks in depth:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// stack/ecs.ts&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; cdk &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;aws-cdk-lib&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  Certificate&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  CertificateValidation&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  KeyAlgorithm&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;aws-cdk-lib/aws-certificatemanager&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; IpAddresses&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Port&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; SubnetType&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Vpc &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;aws-cdk-lib/aws-ec2&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  Cluster&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  Compatibility&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  ContainerImage&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  ContainerInsights&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  EcrImage&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  FargateService&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  LogDriver&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  TaskDefinition&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;aws-cdk-lib/aws-ecs&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  ApplicationListener&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  ApplicationLoadBalancer&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  ApplicationProtocol&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  ApplicationTargetGroup&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  HealthCheck&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  ListenerAction&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  ListenerCondition&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  SslPolicy&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;aws-cdk-lib/aws-elasticloadbalancingv2&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  ARecord&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  HostedZone&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  IPublicHostedZone&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  RecordTarget&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;aws-cdk-lib/aws-route53&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; Construct &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;constructs&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; AwsAccount&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Environment &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;../config&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; Repository &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;aws-cdk-lib/aws-ecr&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; TaskRole &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;aws-cdk-lib/aws-stepfunctions&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; ManagedPolicy &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;aws-cdk-lib/aws-iam&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; LoadBalancerTarget &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;aws-cdk-lib/aws-route53-targets&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;EcsProps&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;cdk&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;StackProps &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  envName&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Environment&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  vpcCidrBlock&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  maxAzs&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  domain&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  backendConfig&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; EcsFargateServiceDefinition&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;EcsFargateServiceDefinition&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  cpu&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  memoryMiB&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  scalability&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    minCapacity&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    maxCapacity&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    cpuThresholdPercentage&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  port&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  healthCheck&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; HealthCheck&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  listenerRules&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    priority&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    pathPattern&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;EcsStack&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;cdk&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Stack &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  props&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; EcsProps&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  hostedZone&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; IPublicHostedZone&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  vpc&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Vpc&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;scope&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Construct&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; id&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; props&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; EcsProps&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;scope&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; props&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;props &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; props&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;vpc &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setVpc&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;hostedZone &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; HostedZone&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fromLookup&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Zone&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      domainName&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;domain&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; cluster &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Cluster&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;EcsCluster&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      vpc&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;vpc&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      containerInsightsV2&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ContainerInsights&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;ENABLED&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; alb&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; httpsListener &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createAlb&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;vpc&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; backendRepositoryArn &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;arn:aws:ecr:&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;env&lt;span class=&quot;token operator&quot;&gt;?.&lt;/span&gt;region&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;AwsAccount&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;SharedAssets&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;:repository/&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;backendConfig&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; backendImage &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; ContainerImage&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fromEcrRepository&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
      Repository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fromRepositoryArn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;backendConfig&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        backendRepositoryArn&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;envName&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createFargateService&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
      cluster&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      backendImage&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      alb&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      httpsListener&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;backendConfig&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;setVpc&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Vpc &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; vpc &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Vpc&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Vpc&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      ipAddresses&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; IpAddresses&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;cidr&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;vpcCidrBlock&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      maxAzs&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;maxAzs&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      subnetConfiguration&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          cidrMask&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;22&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;PrivateForServices&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          subnetType&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; SubnetType&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;PRIVATE_WITH_EGRESS&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          cidrMask&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;22&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;PublicForServices&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          subnetType&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; SubnetType&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;PUBLIC&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      natGateways&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; vpc&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;createAlb&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;vpc&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Vpc&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; certificate &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Certificate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;EcsCertificate&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      domainName&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;domain&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      keyAlgorithm&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; KeyAlgorithm&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;RSA_2048&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      validation&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; CertificateValidation&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fromDns&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;hostedZone&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; alb &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ApplicationLoadBalancer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token string&quot;&gt;&quot;EcsApplicationLoadBalancer&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        vpc&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        internetFacing&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    alb&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;connections&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;allowToAnyIpv4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Port&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;tcp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;80&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    alb&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;connections&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;allowToAnyIpv4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Port&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;tcp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;443&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    alb&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Http&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      protocol&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ApplicationProtocol&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;HTTP&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      port&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;80&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      open&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      defaultAction&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ListenerAction&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;redirect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        port&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;443&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        protocol&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;HTTPS&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        permanent&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; httpsListener &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; alb&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Https&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      protocol&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ApplicationProtocol&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;HTTPS&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      port&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;443&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      open&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      certificates&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;certificate&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      defaultAction&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ListenerAction&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fixedResponse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;404&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        messageBody&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token string&quot;&gt;&quot;Please configure services to redicted to the ALB correctly&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      sslPolicy&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; SslPolicy&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;RECOMMENDED_TLS&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; alb&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; httpsListener &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;createFargateService&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    cluster&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Cluster&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    image&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; EcrImage&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    loadBalancer&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ApplicationLoadBalancer&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    listener&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ApplicationListener&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    definition&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; EcsFargateServiceDefinition&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; FargateService &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; taskDefinition &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TaskDefinition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;EcsServiceTaskDefinition&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;definition&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        cpu&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; definition&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;cpu&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        compatibility&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Compatibility&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;FARGATE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        memoryMiB&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; definition&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;memoryMiB&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    taskDefinition&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addContainer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;definition&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      image&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; image&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      portMappings&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; containerPort&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; definition&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;port &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      logging&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; LogDriver&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;awsLogs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        streamPrefix&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; definition&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    taskDefinition&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;executionRole&lt;span class=&quot;token operator&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addManagedPolicy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
      ManagedPolicy&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fromAwsManagedPolicyName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&quot;service-role/AmazonECSTaskExecutionRolePolicy&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; service &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;FargateService&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;EcsService&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;definition&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      cluster&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      taskDefinition&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; taskDefinition&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    loadBalancer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;connections&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;allowTo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;service&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Port&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;tcp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;definition&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;port&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; scaling &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; service&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;autoScaleTaskCount&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      maxCapacity&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; definition&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;scalability&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;maxCapacity&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      minCapacity&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; definition&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;scalability&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;minCapacity&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; cpuMaxMetric &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; service&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;metricCpuUtilization&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      statistic&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Maximum&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      period&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; cdk&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Duration&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;minutes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; cpuAverageMetric &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; service&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;metricCpuUtilization&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      statistic&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Average&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      period&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; cdk&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Duration&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;minutes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    scaling&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;scaleOnMetric&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;definition&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;CpuMaxScale&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      metric&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; cpuMaxMetric&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      scalingSteps&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          lower&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          upper&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; definition&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;scalability&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;cpuThresholdPercentage&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          change&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          lower&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; definition&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;scalability&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;cpuThresholdPercentage&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          upper&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;70&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          change&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; lower&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;70&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; change&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      evaluationPeriods&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      cooldown&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; cdk&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Duration&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;seconds&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;60&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    scaling&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;scaleOnMetric&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;definition&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;CpuAverageScale&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      metric&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; cpuAverageMetric&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      scalingSteps&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          lower&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          upper&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; definition&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;scalability&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;cpuThresholdPercentage&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          change&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          lower&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; definition&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;scalability&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;cpuThresholdPercentage&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          upper&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;70&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          change&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; lower&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;70&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; change&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      evaluationPeriods&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      cooldown&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; cdk&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Duration&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;seconds&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;60&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; targetGroup &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ApplicationTargetGroup&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;definition&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;TargetGroup&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        vpc&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;vpc&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        protocol&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ApplicationProtocol&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;HTTP&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        port&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; definition&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;port&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        healthCheck&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; definition&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;healthCheck&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        targets&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;service&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    listener&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addAction&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;definition&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;Action&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      conditions&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
        ListenerCondition&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;hostHeaders&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;domain&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        ListenerCondition&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;pathPatterns&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;definition&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;listenerRules&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;pathPattern&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      priority&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; definition&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;listenerRules&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;priority&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      action&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ListenerAction&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;forward&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;targetGroup&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ARecord&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;domain&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;ARecord&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      target&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; RecordTarget&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fromAlias&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LoadBalancerTarget&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;loadBalancer&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      zone&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;hostedZone&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      recordName&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;domain&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; service&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This stack is way bigger than the ECR stack so let&apos;s explain this step-by-step:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The Stack class contains three methods:
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;setVpc&lt;/code&gt;: Created a VPC with NAT Gateway, Public Subnets, Private Subnets, and Route Tables.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;createAlb&lt;/code&gt;: Create an ALB with an HTTPS listener (with a Certificate attached to it) and an HTTP listener (that redirects to HTTPS). The ALB allows HTTP (port 80) and HTTPS (port 443) traffic from anywhere.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;createFargateService&lt;/code&gt;: Creates the taskDefinition, container definition, and auto scaling policies for the ECS service.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;There are three ways to &lt;a href=&quot;https://docs.aws.amazon.com/AmazonECS/latest/developerguide/service-auto-scaling.html&quot;&gt;auto-scale&lt;/a&gt; your ECS cluster:
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;Target Tracking&lt;/code&gt;: Simply select a metric and a target value to scale and ECS will manage everything - e.g. 70% average CPU - and the alarms will try to keep the metric as close to the specified value.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;Step scaling&lt;/code&gt;: You will manage the creation of CloudWatch Alarms, the rules to scale and steps of scale-in/scale-out, e.g. create an alarm that monitors average memory and reduce 1 task if below 30% / add 1 task if above 60% / add 2 tasks if above 75%.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;Predictive scaling&lt;/code&gt;: You specify a metric and AWS will scale based on historical data of the metric selected. Predictive scaling only scales up, meaning that you will need other scaling methods to scale down. Often used when there is cyclical traffic in our application.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;I chose to not use the &lt;a href=&quot;https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_ecs_patterns.ApplicationLoadBalancedFargateService.html&quot;&gt;ApplicationLoadBalancedFargateService&lt;/a&gt; construct because it creates a single ALB per service created and my idea was to use a single ALB per ECS Cluster, so we could add other services (e.g. frontend) that would reuse this ALB. Also notice that I&apos;ve used a &lt;code class=&quot;language-text&quot;&gt;path-based&lt;/code&gt; routing instead of a &lt;code class=&quot;language-text&quot;&gt;subdomain-based&lt;/code&gt; routing (i.e. &lt;code class=&quot;language-text&quot;&gt;/api&lt;/code&gt; forwards to the backend service, while the &lt;code class=&quot;language-text&quot;&gt;/&lt;/code&gt; could forward to the frontend service for example).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now, let&apos;s just define the configuration for each stack.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// config/dev.ts&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; Protocol &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;aws-cdk-lib/aws-elasticloadbalancingv2&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; AwsAccount &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; EcsProps &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;../stack/ecs&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; devConfig&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; EcsProps &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  env&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    account&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; AwsAccount&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Development&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    region&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;us-east-1&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  domain&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;demosfelipetrindade.top&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  envName&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;dev&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  vpcCidrBlock&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;10.200.0.0/16&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  maxAzs&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  backendConfig&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;backend&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    cpu&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;1024&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    memoryMiB&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;2048&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    port&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;8000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    scalability&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      minCapacity&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      maxCapacity&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      cpuThresholdPercentage&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;40&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    healthCheck&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      path&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/api/health&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      protocol&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Protocol&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;HTTP&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    listenerRules&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      priority&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      pathPattern&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/api/*&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;and&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// config/sharedAssets,ts&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; AwsAccount &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; EcrProps &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;../stack/ecr&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; sharedAssetsConfig&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; EcrProps &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  env&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    account&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; AwsAccount&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;SharedAssets&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    region&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;us-east-1&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  ecrRepository&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;backend&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  maxAgeOtherTagsInDays&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;Running our demo!&lt;/h2&gt;
&lt;p&gt;The commands to reproduce this demo are available in my GitHub Repository. So let&apos;s focus on the results of the demo and suppose that the ECR contains the API image and that the ECS service is already running as expected.&lt;/p&gt;
&lt;p&gt;For this load test, I decided to use a spawn rate of 0.25 users per second and 80 max users. I let the test run for almost an hour. Here are the Locust results.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/3d6737ee0bebb93c73af876292681bec/5c744/locust-statistics.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 16.455696202531648%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAADCAYAAACTWi8uAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAcUlEQVR42lWN2QnAMAxDM0XIHch97b+eiwwp7cdDlpFloZQiYwxJKSmEQFpr8t6Tc46x1v5mZK9PKbFHHrcxRhKtNTrnUCmF1lpUa/2BPRQ5AA/tvfM+58weCgS+7L25bM75FnzBwRiDgUcZ5vsA/hY+zFhT+BJHUpEAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Locust Statistics&quot;
        title=&quot;&quot;
        src=&quot;/static/3d6737ee0bebb93c73af876292681bec/f058b/locust-statistics.png&quot;
        srcset=&quot;/static/3d6737ee0bebb93c73af876292681bec/c26ae/locust-statistics.png 158w,
/static/3d6737ee0bebb93c73af876292681bec/6bdcf/locust-statistics.png 315w,
/static/3d6737ee0bebb93c73af876292681bec/f058b/locust-statistics.png 630w,
/static/3d6737ee0bebb93c73af876292681bec/40601/locust-statistics.png 945w,
/static/3d6737ee0bebb93c73af876292681bec/5c744/locust-statistics.png 1206w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;During our test, 116k requests were made to the API, and only 2 requests failed to return to the user and the 99%ile was 260ms. Let&apos;s now analyze the charts (RPS, Response Time, and Number of Users).&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/72006ff98431f0cf91edb1ac81ad2a16/5c744/locust-chart.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 72.78481012658227%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAPCAYAAADkmO9VAAAACXBIWXMAAAsTAAALEwEAmpwYAAACF0lEQVR42m1T23KbMBTkV1qjC0hCN3DsgPElM31Im7ZJZvr/f7I9EmBIJg87u2f3GOlIctHuDwhxj+g7KGnAvgnwnQT/nkBaK7R1g1royVuy3ZzPEGUFKWoUUihwWWHXO7AhgI8R7BTAzsSXNutd1nH2Fp/41k167hNao6hqQx/zYK4BI4MZjdJMzJqFzQRj7n5599d+LmmH+jHAR49A40ZlEWi8sPAXiGqrlz5i8lVFO+yMxehb9D6idxOGVNuAkw8YY4chdJlzNqN3gfoiHm3MnLKGvlXY0MLGFoqKHZNgvEJJXLKKVpTQNKZSHko7GknlbO1ZNacLEZQXjfGoSFSVQp2hZyRPT9kGH3s+QqRbPoaeVqghpaXV1LxyPesqI+2Cp5pYiCZrxutpd4RpMkUZ7fBlPOP9dsO/px94u97wc+jxezzh7+WCl9OA51yPeKX6z/mE9+sT3ki/Xq6Un/Hc9/g1DlSfEenci5rGsI2lA23grIdPoAN31pEOuXaNy3XKc4/z915HPYEuJPUouukitHtYMjgTK8pVszpdSJPPeOt/6CEtaHSZLkXRw5YE3uzBTTdxRpfBmofVSzmBmf2mb4WoDP1TaOUyXsH8AGYfCMcNDhs+bOrjp/qAkn7P6XkVor3kD1WMoxJyApdfa/HJXzIuIOnZcPrHFFxHGG2gtaVH7DKbxs315KnkLVniTZ/aeMp4/AdWJMv/eRtu5wAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Locust Chart&quot;
        title=&quot;&quot;
        src=&quot;/static/72006ff98431f0cf91edb1ac81ad2a16/f058b/locust-chart.png&quot;
        srcset=&quot;/static/72006ff98431f0cf91edb1ac81ad2a16/c26ae/locust-chart.png 158w,
/static/72006ff98431f0cf91edb1ac81ad2a16/6bdcf/locust-chart.png 315w,
/static/72006ff98431f0cf91edb1ac81ad2a16/f058b/locust-chart.png 630w,
/static/72006ff98431f0cf91edb1ac81ad2a16/40601/locust-chart.png 945w,
/static/72006ff98431f0cf91edb1ac81ad2a16/5c744/locust-chart.png 1206w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;We can observe that there were some peaks in the 95th percentile but for the most time, the 50th percentile was pretty stable.&lt;/p&gt;
&lt;p&gt;Now, let&apos;s analyze the infrastructure side: CPU and memory usage of the ECS Service during the load test.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/dd775fc863ae13eee62a16e0d38d8138/afa16/memory-usage.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 34.177215189873415%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAYAAAAIy204AAAACXBIWXMAABYlAAAWJQFJUiTwAAABVElEQVR42kVQ2XKEMAzjJ0jCcl9JIJzLsld3+tL//yZVgc70QeNYtmTHQWYGZE2HnIiJtOW7tow9Loyxz/kujEOpOxStRV5ZJPqsX2rWtUePqOoQqNqhmGna95D1gHQYUTgadTNkbnAxI8rZIdtuKJ1Bbg1SS7GZoHINVVoU64TUjZAFDWU5IHILRDNC5BainSFofCAzjBO5CaHdiBtz32cQej7VZ4+9IvQ65oGiq+gfJG+QScO4Md8hSwfJBlnReHxBdDvE/vNf86CZ7znq1Mmk5YYZCUfDjtNJCHOl6A5R9ucGRY9w+SCkYTh/jj7pt8u5SHZuGI5vhHr529BPGZ7HZEVCecGfSFGgKgd1/YYaHlDTFxS3kfxeRD7iiSIOVByo9HrkQTXcYZY39PxEP+18v9DMD2JHR652G2JOT82KlDE2CyIaJrx5ytse0a7ItOcG/ALgr8djug7QigAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;ECS Service Memory usage&quot;
        title=&quot;&quot;
        src=&quot;/static/dd775fc863ae13eee62a16e0d38d8138/f058b/memory-usage.png&quot;
        srcset=&quot;/static/dd775fc863ae13eee62a16e0d38d8138/c26ae/memory-usage.png 158w,
/static/dd775fc863ae13eee62a16e0d38d8138/6bdcf/memory-usage.png 315w,
/static/dd775fc863ae13eee62a16e0d38d8138/f058b/memory-usage.png 630w,
/static/dd775fc863ae13eee62a16e0d38d8138/40601/memory-usage.png 945w,
/static/dd775fc863ae13eee62a16e0d38d8138/78612/memory-usage.png 1260w,
/static/dd775fc863ae13eee62a16e0d38d8138/afa16/memory-usage.png 2602w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/9532cf025c8d98dcda135e5e650765be/e28fd/cpu-usage.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 34.810126582278485%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAYAAAAIy204AAAACXBIWXMAABYlAAAWJQFJUiTwAAABNElEQVR42lVR2XKDMBDjH5oEU8xhc9gGcpC2SWc6/f/PUrVLk04fNHsJrdZkpZ+xr0bFzjLa8Vnn9V/+qIVTNEGxs//ngsy0CYc6aFGHGX1aNC9dRNMltMS+Clr7PqEfJu055pWPOtvz+9c26pIs/91kuN1fVlTzGVUbUCt5VGJJVBTM+WFRb/wXQoRr9iWX5TJXh0Koxxl2usAMM3xMsI4EF2Ao7LoJDR0ZTyd9hGFfjAi6njP/uIQn53IyE7+ckHd8z2FFThHj+VYdHfUBthehoILaI8qRtUTChai9QyOCtNnQXXtct4df7jgMF27nonZCThguMvx5xs10vUWF54wOxUjRLZBrszKcsdy/EE8fSMRwvqG7fiK9f8MdbyiGM2y8okpvCpuuzyizMqxbTY4dTvgBRlPDpWMoEKsAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;ECS Service CPU usage&quot;
        title=&quot;&quot;
        src=&quot;/static/9532cf025c8d98dcda135e5e650765be/f058b/cpu-usage.png&quot;
        srcset=&quot;/static/9532cf025c8d98dcda135e5e650765be/c26ae/cpu-usage.png 158w,
/static/9532cf025c8d98dcda135e5e650765be/6bdcf/cpu-usage.png 315w,
/static/9532cf025c8d98dcda135e5e650765be/f058b/cpu-usage.png 630w,
/static/9532cf025c8d98dcda135e5e650765be/40601/cpu-usage.png 945w,
/static/9532cf025c8d98dcda135e5e650765be/78612/cpu-usage.png 1260w,
/static/9532cf025c8d98dcda135e5e650765be/e28fd/cpu-usage.png 2650w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;We can see that the application was sensitive in terms of CPU but the memory didn&apos;t change much (didn&apos;t even reach 10% utilization), that&apos;s why we used scaling based on CPU for this application. The Max CPU Utilization varied and peaked several times around 40% (our target set threshold) but after a while, the value dropped significantly. Why? That&apos;s because more tasks were added to the service to make sure the CPU goes below 40%. This becomes way more visible in the chart of a number of tasks running.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/358d2529a68ffa9c91be5c3b8a2453aa/fd561/tasks-running.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 30.37974683544304%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAGCAYAAADDl76dAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAz0lEQVR42oVQSxbCIAzsJeRTqZYqtFbQ4qLPjfc/VpykPhd+F/NCMpOBoVp3R1Kb/g/id85jvx2efWXaA+nt8Bu7/HkOA90X0uEsZzG0b4YQteMCXgCveIl75p66SNpjtkuk9ifhxZDJ1xir40yrWEi5QIq/pEsLOLroH+C4MJUZUqgmPiLz7X55lQmF7D6T5RgQGVTbjdIbvMSkq8CWG9mMir3aH8jGiTQurVjY5lnEbGBRGxg6gGc1+CYk6Vm7xqIbLkAh10/gWJukGj/SHaFCpk5ggoGPAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Number of ECS tasks running&quot;
        title=&quot;&quot;
        src=&quot;/static/358d2529a68ffa9c91be5c3b8a2453aa/f058b/tasks-running.png&quot;
        srcset=&quot;/static/358d2529a68ffa9c91be5c3b8a2453aa/c26ae/tasks-running.png 158w,
/static/358d2529a68ffa9c91be5c3b8a2453aa/6bdcf/tasks-running.png 315w,
/static/358d2529a68ffa9c91be5c3b8a2453aa/f058b/tasks-running.png 630w,
/static/358d2529a68ffa9c91be5c3b8a2453aa/40601/tasks-running.png 945w,
/static/358d2529a68ffa9c91be5c3b8a2453aa/78612/tasks-running.png 1260w,
/static/358d2529a68ffa9c91be5c3b8a2453aa/fd561/tasks-running.png 2866w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;It&apos;s important to comprehend the correlation between CPU increase and number of tasks running. When the CPU peaks, ECS scales up the service. When the CPU goes down, ECS scales down the cluster. The scaling (up and down) of ECS is managed by CloudWatch Alarms that monitor a metric (in our case CPU Average and CPU Maximum) and perform an action when the alarm is &lt;code class=&quot;language-text&quot;&gt;in alarm&lt;/code&gt; state. The ECS service sends metrics (CPU, Memory...) to CloudWatch in &lt;a href=&quot;https://docs.aws.amazon.com/AmazonECS/latest/developerguide/service-auto-scaling.html&quot;&gt;1-minute intervals&lt;/a&gt;. We created four alarms:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/5115107abffb9c640f26fd4b1682325b/b918a/alarms.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 13.29113924050633%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAADCAYAAACTWi8uAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAnUlEQVR42m3Nyw6CMBCFYZ5CjFaogDDQCxelgGEv5f3f5zgQ4srFl/5N22lwikv8JTcVN+GaKojcQGQGZAdIaiFSjaTscLkrhFGBM98LWXDrPcRrgeg3/nC0W/nsgzAxiHIezB88zICCh4rMIuemeuTVQT9nUDMhiAePyC3M77Z9/Ot1JzQ/IgWZaZTNCN29IYsaqp1QMbIO9jXv/QV02lozZIHAygAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;CloudWatch Alarms created&quot;
        title=&quot;&quot;
        src=&quot;/static/5115107abffb9c640f26fd4b1682325b/f058b/alarms.png&quot;
        srcset=&quot;/static/5115107abffb9c640f26fd4b1682325b/c26ae/alarms.png 158w,
/static/5115107abffb9c640f26fd4b1682325b/6bdcf/alarms.png 315w,
/static/5115107abffb9c640f26fd4b1682325b/f058b/alarms.png 630w,
/static/5115107abffb9c640f26fd4b1682325b/40601/alarms.png 945w,
/static/5115107abffb9c640f26fd4b1682325b/78612/alarms.png 1260w,
/static/5115107abffb9c640f26fd4b1682325b/b918a/alarms.png 2780w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Let&apos;s take a look at how the Average CPU metric (to scale up) behaves:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/f83e8c270819047afd3cdabf9c615deb/43853/cpu-scale-up.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 55.06329113924051%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAABYlAAAWJQFJUiTwAAAB5ElEQVR42pWTSXPTQBCF9RewHW3Wao1k7Ytl2diGBDhQKRsIhApLQaWKMyQn/v/jjRSDq7jA4dNrjbp6Xk+PFLvZId3tkW5eIj9/hfL8NbLdJfKnB+RP9iieXaG4eINseYG4WiNpNkibLePHyKi2KDCeCpxZIc7sCIoot3CLDUZOiolfYewzwcsxmZWY9FpBC1sYYY3pLIMZ5IPOUuhBCSddIUgWCLIWpsihxOUa3fYFRNrCYpIVZDC8mDsKqHb4Ww0WGQqdkvcbmH6KaZhBDxMohsedvASaM4cqcVmMOqH9U8Zs6W9E324fy7Zly25UwaMrK6phhxUcunO4wX9DlybNKDofGm2rtK+JEiqLqoHUmusFND/jujxHvocPyjzdHzrT+V1nnu5EfYeKRYd+0WGWNHCjAq7UdAGn15YD64a457hOzVeE3zgMJ1vCZjylMUU6s+jAdDOMpjwrMjEfMDiUk1jqmKpaMWzeAos3QmqPkLeABTV3jv3tJd7fX+H2ywHfPh9wzfjmO/nxtufD3cDp+6f7a3w8cvcOX3/eoH2+lVOOEdULZOs1L2qHtO6QdCvEiyXCukHYLKhHmj9UDUQ1qCRinjMvoNg8WItWvbiBzWHotG5RDTfFI0Ng9K/o8m+J8AteHkVZ1Z0N7wAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Alarm of CloudWatch Average CPU to scale up&quot;
        title=&quot;&quot;
        src=&quot;/static/f83e8c270819047afd3cdabf9c615deb/f058b/cpu-scale-up.png&quot;
        srcset=&quot;/static/f83e8c270819047afd3cdabf9c615deb/c26ae/cpu-scale-up.png 158w,
/static/f83e8c270819047afd3cdabf9c615deb/6bdcf/cpu-scale-up.png 315w,
/static/f83e8c270819047afd3cdabf9c615deb/f058b/cpu-scale-up.png 630w,
/static/f83e8c270819047afd3cdabf9c615deb/40601/cpu-scale-up.png 945w,
/static/f83e8c270819047afd3cdabf9c615deb/78612/cpu-scale-up.png 1260w,
/static/f83e8c270819047afd3cdabf9c615deb/43853/cpu-scale-up.png 2284w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;This alarm was in alarm state a single time when the average CPU peaked and went beyond the 40% threshold. The alarm to scale down based on the Average CPU will be the opposite of the alarm to scale up! Let&apos;s check:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/995d3540b0b29d625f5adb2a80905264/43853/cpu-scale-down.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 55.06329113924051%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAABYlAAAWJQFJUiTwAAAB2ElEQVR42pWSyXLaUBBF9QeJAWtCI5rQ8CSBIS7LjqAy2BRxnK/IwvsE7/LrN7eFCyfllRenunlqum/f97SouUFxvUdxdYuqv0fdf0d5vUP18Ruqmz3U9gfU5gHFqkemPqBYdANZfYly2SGcLzBxkoFzN4UW1R0CdYWRV2ASNhiHCuOgwmRWYzLEBkZyAStp4cxKTKMKU4nEjBq4+RpRvkRarvhNQcs5ad19Qlxc8A8FnKiEFcxx7sTQ3eQUrecm9okCNpvbbCLnSdrAY9SsoIBJDC+D7qXQfTZjPqH8fxlzpdfEGE/jIR8xSp3ms3NAVU7awk04heq8IH8zfkjFfg7NpCKD0vVZBSOuodMrPZJI77iOwUIjFh9b0hxhnRnKZjkjvYwVTNmOm2kOFYYVjeVt+YmCP28RMBd8+hrwW8AzP38+E3gJXrk+whrBZS4XpulibMbpcYUzJ8WInHHSEf720lM+RPGUtkwpxEpq2IyCxUE2lWoGC39ebvDUfcHj/R0eH3Y4MP/Vf8Xv/vaFzUt+YP603Z04bO/w5/MeW75TzaKHJVdaqjVqtULZrNDyTdVcq8waVFQv8RXp/0hdKArlhmR3r1zA4Qpivqwgl/GOT+H9G5Bn8xeEqUOp8rF/wQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Alarm of CloudWatch Average CPU to scale up&quot;
        title=&quot;&quot;
        src=&quot;/static/995d3540b0b29d625f5adb2a80905264/f058b/cpu-scale-down.png&quot;
        srcset=&quot;/static/995d3540b0b29d625f5adb2a80905264/c26ae/cpu-scale-down.png 158w,
/static/995d3540b0b29d625f5adb2a80905264/6bdcf/cpu-scale-down.png 315w,
/static/995d3540b0b29d625f5adb2a80905264/f058b/cpu-scale-down.png 630w,
/static/995d3540b0b29d625f5adb2a80905264/40601/cpu-scale-down.png 945w,
/static/995d3540b0b29d625f5adb2a80905264/78612/cpu-scale-down.png 1260w,
/static/995d3540b0b29d625f5adb2a80905264/43853/cpu-scale-down.png 2284w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Now let&apos;s check the Alarm related to the scaling up of the Maximum CPU metric:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/90ff45e8a58cf739751565b957bc848e/43853/cpu-max-scale-up.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 55.06329113924051%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAABYlAAAWJQFJUiTwAAACMUlEQVR42mVSaWsTURSdf2CzmMw+k8ns+ySp2WyrUtPYaGOCVhEUpOgHEQwUKv7643mTWCh+ONx371vuOec+yajmiOeXiKcLpKevkZ++QTJfIj1ZIX36CvmzNbKzNZLRGcJ8jGTA89UMUTlBVEygWCHauofHAoYPyc1nsLIpmmaMdq9Aq5ehZadoOznadSzQ9YZQvBKak0AlNCeto+wWsJIx+tEA/WQI1U0hRex6PHsBNx5Cd2Lo/QSKHbKji47h3UeFe/8eukc/hUKoPe55bOBFkBQ7hkx0zRAdM6ghqAsZNQ7rlub+jwd10ZySDS+HKZi5OXS/hE52omYEAxisGSJnvc6Dag+fIDOD/hlkWtd4TvgpCXlyP0OXcmThC70SucxNmQ/KvCxkKXZ0QLiPfET4KnsFlHBQ512qk3Sy6aXHcKISlp/DDhlDmh1wzdwKK5hcW0EBm6jPiDphcBgmo8nzBn+B5maQOmSmc1qqnaBBDxomPdN8+hOgQZYt1tqqjybzJn1uMLbEENhYdzJKL2poVKNSmdS1Aqy+LLH99RY37y5xNzvH+x9X+PpphT/Tc9x8XGFzu8H36xV2Ly+wW1zg98kCd8+X+PxtjevdFh9+bnC7vcJ0PNl76JcDJOMx0mKEPB4hGj1BXAyR+gXCrIRXDRAmJRKyiqMCKaUnhJ9VcMsKXlEhjkvYHhkaNFinbJteial2xKTFMPiVHvE7NFQXTWUfj7SHaB72BI64Fl/sLznxRLgvXMvnAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Alarm of CloudWatch Max CPU to scale up&quot;
        title=&quot;&quot;
        src=&quot;/static/90ff45e8a58cf739751565b957bc848e/f058b/cpu-max-scale-up.png&quot;
        srcset=&quot;/static/90ff45e8a58cf739751565b957bc848e/c26ae/cpu-max-scale-up.png 158w,
/static/90ff45e8a58cf739751565b957bc848e/6bdcf/cpu-max-scale-up.png 315w,
/static/90ff45e8a58cf739751565b957bc848e/f058b/cpu-max-scale-up.png 630w,
/static/90ff45e8a58cf739751565b957bc848e/40601/cpu-max-scale-up.png 945w,
/static/90ff45e8a58cf739751565b957bc848e/78612/cpu-max-scale-up.png 1260w,
/static/90ff45e8a58cf739751565b957bc848e/43853/cpu-max-scale-up.png 2284w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Notice how this metric was triggered several times! As you expect the policy to scale down based on maximum CPU usage will be the opposite.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/cb9f75fbd1b414bd368e6e29b39b4c05/43853/cpu-max-scale-down.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 55.06329113924051%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAABYlAAAWJQFJUiTwAAACLklEQVR42m1T2W7TUBD1JzSrd8eJd/vajrM0SZcQsbVNaau0ICF47SIhFaFKCMHPH851AvSBh6OZOXPvnZkztuKVR0gP10gXbyGW71AsL5AdnkIcn0McnSFfXdVIxseI8n2k1QLJDnExQ9cO0baCGh36ilccwM0XaDop2v0SrX6OlivQHhRo17aEGoyhB0NYgwzmDsYghe6X6GUzeMkIYTaB6eVQElaZHryEn455IYXlZdDdGB3LZ/Xgr9WZMwaCyJ5BkN9yQVjCoVV0N4VGqE6MrhPV6Dwbo137Plrmf8B8bQ0fTdo2Cyt2UPBlduYXsEKOxe4kZ0cj2ORsGZO3o+ofQsITsHvMsSsZOzxnMFbkeBpnV9m6xqRGrWSsManxQY2HdV7W3WSHGHqf+vFhqasWlNDjUR2rnE6x2E1fTDGIh3DCAr2kghtR7Kio4ZJ3JS+5mDrteHnW4TKcYJuzJyuYPpfSZXUjGqLLoGFSLyPgxiM07Yh+SJ0i7LHbOqbf4pmulXDkEgbvmCxoSqm8glsWUFQnxN38BX4sXuFuc4abxys8rd7gy8UpNk8b3L9f49fsNR5u1vh0f4nPt5f48HWDj48bfDtf4+fyBN9XJ3i4vcZ8MaeGrJ6xyjifIi8miCb7KMUEQozgVxViMYTgJ5FkQ4RlhbCoEJAP6Ce8lzEnEecVegFHdii0bNXJqnrTGj9Wg0KrFL6p+Wjwk9gzt7b5B/oWDXObq/OM5Z/yGxpnRJhRiqdgAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Alarm of CloudWatch Max CPU to scale down&quot;
        title=&quot;&quot;
        src=&quot;/static/cb9f75fbd1b414bd368e6e29b39b4c05/f058b/cpu-max-scale-down.png&quot;
        srcset=&quot;/static/cb9f75fbd1b414bd368e6e29b39b4c05/c26ae/cpu-max-scale-down.png 158w,
/static/cb9f75fbd1b414bd368e6e29b39b4c05/6bdcf/cpu-max-scale-down.png 315w,
/static/cb9f75fbd1b414bd368e6e29b39b4c05/f058b/cpu-max-scale-down.png 630w,
/static/cb9f75fbd1b414bd368e6e29b39b4c05/40601/cpu-max-scale-down.png 945w,
/static/cb9f75fbd1b414bd368e6e29b39b4c05/78612/cpu-max-scale-down.png 1260w,
/static/cb9f75fbd1b414bd368e6e29b39b4c05/43853/cpu-max-scale-down.png 2284w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;You might be wondering why the alarms were not triggered when the CPU reached 40% immediately. Well, the reason for that is that it takes time for ECS to send the metrics, CloudWatch to evaluate and EventBridge trigger the scale up.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/6a09c165c8aeef047995719912afb7e4/f238d/investigate-cpu-max-scale-up.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 43.0379746835443%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAYAAAAywQxIAAAACXBIWXMAABYlAAAWJQFJUiTwAAACDklEQVR42m1SW2sTURDeXyCaZi/NpjGXzd7vm71k1yZNKkiMRElLKG2Nt1rBWqUihb6JiLSF9MEX/befczavPnyc+WbOnDPzzXB1xUZVViBsqRAaGvh6F1LLgtwyITNONt801zFmM3Tc9Uk5/JYG4SHF6Q3GuXrbgqQFEDsOBQyIigc12UFhJxiZPUhmDMnooaqFEHWC6kPo2BBUytGj0se7OQQ7o3wNXEv30fL6aPo59HRU2ooVIQr66BFaGn3gJOiPp3CiAmaQI8rH6BW7sJIhrHiAlGLGcAqBOuHC/ghhNkSU7SCmi0EyQJAOkY8nCIh7xOPBEzw/OsVkf4kXhyeYH7/HdPEaT4kzzA7eYvxsAdWNqUI3RVsPqH8dDyQFlVq3BN8wUJHJ3lRwn/S5JzSxQX6RaUfSVEivKunHyyo26B7z1SjGKXYPNrWiBiG6vl+iQzBsF6Zll9ztutS2ByfNkIU5Yj9F38sQxzm87YI6KZAkBRqGA+784hK3d7/x9eYjPt0s8eX2Dc7uTvDr2x6uzyY4//4Sfx7PcHV5hM8/l1jtz7Ham+Pv7gw/3i3wYfUKF1eHuD49QDodgKuy0uvr8W9SyR2arEEDsgOqmiZYa9uoU4XsFBo6TVKntTIh0XYwLhJkiolNo3yD04wQihfAjQtopCcDk8COHpVw421YYVHuYZV29H/gy6LW5z/gxB0d5AUsFwAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Investigate CPU Max Scale Up&quot;
        title=&quot;&quot;
        src=&quot;/static/6a09c165c8aeef047995719912afb7e4/f058b/investigate-cpu-max-scale-up.png&quot;
        srcset=&quot;/static/6a09c165c8aeef047995719912afb7e4/c26ae/investigate-cpu-max-scale-up.png 158w,
/static/6a09c165c8aeef047995719912afb7e4/6bdcf/investigate-cpu-max-scale-up.png 315w,
/static/6a09c165c8aeef047995719912afb7e4/f058b/investigate-cpu-max-scale-up.png 630w,
/static/6a09c165c8aeef047995719912afb7e4/40601/investigate-cpu-max-scale-up.png 945w,
/static/6a09c165c8aeef047995719912afb7e4/78612/investigate-cpu-max-scale-up.png 1260w,
/static/6a09c165c8aeef047995719912afb7e4/f238d/investigate-cpu-max-scale-up.png 2078w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2&gt;Cya!&lt;/h2&gt;
&lt;p&gt;Well, I hope you liked this blog post. See you around! 👋&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Terraform CI/CD with GitHub Actions and pre-commit]]></title><description><![CDATA[In today's blog post, I would like to propose a simple, yet powerful, CI/CD pipeline to be used in Terraform projects. From my experience, I…]]></description><link>https://felipetrindade.com/terraform-ci-cd/</link><guid isPermaLink="false">https://felipetrindade.com/terraform-ci-cd/</guid><pubDate>Wed, 05 Mar 2025 12:55:32 GMT</pubDate><content:encoded>&lt;p&gt;In today&apos;s blog post, I would like to propose a simple, yet powerful, CI/CD pipeline to be used in Terraform projects. From my experience, I&apos;ve been in several projects with no automated deployment for terraform, lack of code standards, and use of bad practices. Setting up a good CI/CD for Terraform is not difficult but has some important parts that most don&apos;t see. Let&apos;s deep dive into a great CI/CD pipeline that goes beyond &lt;code class=&quot;language-text&quot;&gt;terraform plan&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;terraform apply&lt;/code&gt; actions.&lt;/p&gt;
&lt;h2&gt;What makes a good CI/CD?&lt;/h2&gt;
&lt;p&gt;As you probably know CI/CD stands for &lt;code class=&quot;language-text&quot;&gt;Continuous Improvement&lt;/code&gt; (allow developer to always improve/fix the source code) and &lt;code class=&quot;language-text&quot;&gt;Continuous Deployment&lt;/code&gt; (allow developers to deploy the changes with ease).&lt;/p&gt;
&lt;p&gt;During our Terraform CI, you would like to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Lint the code&lt;/strong&gt;: We would like to run checks to make sure the code is valid and can be built/deployed with no issues, i.e. check syntax errors, and common bugs...&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Run security/compliance checks&lt;/strong&gt;: We should shift security left as much as we can to avoid adding security issues to our code.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Format the code&lt;/strong&gt;: Developers have different preferences when it comes to formatting the code, which can make the code look inconsistent. Adding a code formatter standardizes the way the code looks and improves readability.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Run tests&lt;/strong&gt;: Make sure that what is being added won&apos;t break the current code.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Plan changes&lt;/strong&gt;: We would like to see what actions will be performed by the code, i.e. the &lt;code class=&quot;language-text&quot;&gt;terraform plan&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;During the Terraform CD other actions are performed:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Deploy the infrastructure&lt;/strong&gt;: The source code gets deployed into the environments. It&apos;s a common practice to not deploy to all environments (Development, Staging, Production) all at once. Some companies use a manual approach (validate staging is working before manually approving production deployment) while others use an automatic approach based on metrics (latency, availability...).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Run Post-Deployment Tests&lt;/strong&gt;: Load tests or E2E tests after the infrastructure is deployed guarantee that everything is working as expected.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Notify the team&lt;/strong&gt; Send messages via Slack, MS Teams, and Email to notify important people about the deployment.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Do not treat these actions as a source of truth, since they vary depending on the company, but this serves as a good baseline.&lt;/p&gt;
&lt;h2&gt;Local development&lt;/h2&gt;
&lt;p&gt;When possible it&apos;s interesting to shift to the left the CI checks, i.e. run checks before pushing the code to the remote repository. If CI checks are simple and easy to run locally on the developing machine they should run on the developer machine too (not only during the CI pipeline) because:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;It removes frustration from the developer that needs to push code and wait for CI to run and check if it passed&lt;/li&gt;
&lt;li&gt;Speed up the development process&lt;/li&gt;
&lt;li&gt;Reduce CI costs&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Sometimes the checks take long and it&apos;s better to run checks in the CI, but if it&apos;s not CPU/Memory/Time consuming they should also run in the developer machine.&lt;/p&gt;
&lt;p&gt;A great tool to automate the CI checks in the developer machine is &lt;a href=&quot;https://pre-commit.com/&quot;&gt;Pre-Commit&lt;/a&gt;. We basically define a manifest with the hooks that are going to run before the &lt;code class=&quot;language-text&quot;&gt;git commit&lt;/code&gt; is executed and if it fails it will give the reasons and try to remediate, otherwise if it passes the commit is made, so you also end up with a better Git history too!&lt;/p&gt;
&lt;p&gt;Another common problem when contributing to a project as a developer is to make sure our local environment contains all the needed tools/binaries to run the project. Actually, not only having the tools/binaries are needed, but it&apos;s very common to also enforce specific versions. If you follow my blog you know that I&apos;m a bit fan of &lt;a href=&quot;https://www.jetify.com/devbox&quot;&gt;Devbox&lt;/a&gt; and &lt;a href=&quot;https://mise.jdx.dev/&quot;&gt;mise-en-place&lt;/a&gt;. For this blog post, I will use mise-en-place. With these tools, you can define a manifest with needed tools and versions needed and install all these tools with a simple bash command.&lt;/p&gt;
&lt;h2&gt;Helper Tools&lt;/h2&gt;
&lt;h4&gt;During CI and Local Development&lt;/h4&gt;
&lt;p&gt;Our development environment and CI will rely on running the &lt;code class=&quot;language-text&quot;&gt;pre-commit&lt;/code&gt; check in the repo, this guarantees that even if the developer bypasses the pre-commit check, the CI will guarantee that the code is compliant with the best practices created for the repo. Several tools and commands will be used during the CI:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;terraform fmt&lt;/code&gt;: Make sure the code is formatted and easy to read.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;terraform validate&lt;/code&gt;:  Validate runs checks that verify whether a configuration is syntactically valid and internally consistent, regardless of any provided variables or existing state.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;terraform-docs&lt;/code&gt;: Automatically updates the README markdown that contains the documentation of the terraform code auto-generated using &lt;a href=&quot;https://terraform-docs.io/&quot;&gt;terraform-docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;terraform providers lock&lt;/code&gt;: Consults registries to write provider dependency information into the dependency lock file (&lt;code class=&quot;language-text&quot;&gt;.terraform.lock.hcl&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;tflint&lt;/code&gt;: &lt;a href=&quot;https://github.com/terraform-linters/tflint&quot;&gt;tflint&lt;/a&gt; allows us to find possible errors and enforce best practices.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;trivy&lt;/code&gt;: Find vulnerabilities, misconfigurations, and exposed secrets using &lt;a href=&quot;https://github.com/aquasecurity/trivy&quot;&gt;trivy&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For simplicity, I won&apos;t be running Tests in terraform but you can use &lt;a href=&quot;https://terratest.gruntwork.io/&quot;&gt;Terratest&lt;/a&gt;.&lt;/p&gt;
&lt;h4&gt;During CD&lt;/h4&gt;
&lt;p&gt;Our CD will be simple and will simply run &lt;code class=&quot;language-text&quot;&gt;terraform apply&lt;/code&gt;. Since I won&apos;t deploy any real application I didn&apos;t load tests or send notifications but you can use some great actions, such as &lt;a href=&quot;https://github.com/slackapi/slack-github-action&quot;&gt;Slack action&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Hands-on!&lt;/h2&gt;
&lt;p&gt;Finally, it&apos;s time to check how to implement the proposed CI/CD! Once again, you can check the full code on my &lt;a href=&quot;http://github.com/felipelaptrin/terraform-ci-cd&quot;&gt;GitHub Repository&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;├── config/     # Backend configuration per AWS environment
├── modules/    # Custom Terraform modules
├── src/        # Main Terraform code to deploy infrastructure
└── vars/       # Variables values specific for every environment&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Then let&apos;s check the tools needed to run the project.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;toml&quot;&gt;&lt;pre class=&quot;language-toml&quot;&gt;&lt;code class=&quot;language-toml&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# .mise.toml&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token table class-name&quot;&gt;tools&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token key property&quot;&gt;terraform&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;1.10.5&quot;&lt;/span&gt;
&lt;span class=&quot;token key property&quot;&gt;pre-commit&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;4.0.1&quot;&lt;/span&gt;
&lt;span class=&quot;token key property&quot;&gt;terraform-docs&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;0.19.0&quot;&lt;/span&gt;
&lt;span class=&quot;token key property&quot;&gt;tflint&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;0.55.1&quot;&lt;/span&gt;
&lt;span class=&quot;token key property&quot;&gt;trivy&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;0.59.1&quot;&lt;/span&gt;
&lt;span class=&quot;token key property&quot;&gt;jq&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;1.7.1&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Installing these tools is as easy as running &lt;code class=&quot;language-text&quot;&gt;mise install&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Now, let&apos;s check our pre-commit file.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# .pre-commit-config.yaml&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;repos&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;repo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; https&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;//github.com/antonbabenko/pre&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;commit&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;terraform
    &lt;span class=&quot;token key atrule&quot;&gt;rev&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; v1.97.3
    &lt;span class=&quot;token key atrule&quot;&gt;hooks&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; terraform_fmt
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; terraform_docs
        &lt;span class=&quot;token key atrule&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;hook&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;config=&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;create&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;file&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;if&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;not&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;exist=true
          &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;--args=--lockfile=false&quot;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; terraform_validate
        &lt;span class=&quot;token key atrule&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;hook&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;config=&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;retry&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;once&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;with&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;cleanup=true
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; terraform_providers_lock
        &lt;span class=&quot;token key atrule&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;args=&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;platform=linux_amd64
          &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;args=&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;platform=linux_arm64
          &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;args=&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;platform=darwin_arm64
          &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;args=&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;platform=darwin_amd64
          &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;hook&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;config=&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;mode=always&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;regenerate&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;lockfile
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; terraform_tflint
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; terraform_trivy
        &lt;span class=&quot;token key atrule&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;hook&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;config=&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;parallelism&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;limit=1
          &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;args=&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;severity HIGH&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;CRITICAL
          &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;args=&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;ignorefile=__GIT_WORKING_DIR__/.trivyignore&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The repository that contains the hooks is owned by Anton Babenko: a huge Terraform hero! Notice that our pre-commit uses all the tools mentioned previously.&lt;/p&gt;
&lt;p&gt;It&apos;s important to mention that the developer needs to run &lt;code class=&quot;language-text&quot;&gt;pre-commit install&lt;/code&gt; in order to use pre-commit.&lt;/p&gt;
&lt;p&gt;With everything set, let&apos;s try to create a simple SQS queue and commit in the Git repository!&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/30c21c000d810b089d75cf1a8b22d9bf/0a867/precommit.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 71.51898734177216%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAOCAYAAAAvxDzwAAAACXBIWXMAAAsTAAALEwEAmpwYAAACeElEQVR42pWSOXPaUBSF1WfGMZtZDEjGYIlNgIwWEItWYxCywMRL7OClSVylzkxcZFK5y08+uZKTjJMMhYsz90lPOu/c7z6mqM7QdNdo+3cQvTscWFeoWJcQnEtwgwC76glyKlUtQC5U9Pys7K+9rOIhI0+RU2Zg2MMh9tQxKv0RWNVCUTHBalNklCXS8oI+9FHsTcl4ivxvaTMUeh72Bh443Yv2i4qDvOKCMT58A+s/oXX2HfzyCfzpD4wuHvCwZvFxXcKnmxIaugFOPgLXtZCp60gJGuIHCrINDemqgp2aTuohti+Cmdw9onn2hPLsKyreF+xPHyHNb7A6rWG1aiBYtlFWJ3CCY0iGjVzbQrY5ROxAJWkkMuLDqmC73AEjjudQXR9d2wOvmihJOtK1PrYFB7Gqg2TTRa5uon1romBo2OIUMlARJ5P/JPTBaNP3GPhr9OfXkCcXqA+JWYdYdgzsdW1UtCOwkovF5ys0HAtvOJl+3mBIYuojHwKB5fszYmRjiydGjXG0LkoWVQcsrfeblL5jI08HhcwSwgZDc3kLndKNAko5u0JZn0C0Atirezjv7qH715CcFVr2KQSaalmbIAwRcs23DbCHNgodMzq4QJ0xyvE5lMk5tXsevcyIBoGnNHQFSrJLiUwawggJvvdXkmS1/0eJF2tGNBdoGQHapNpwHp2eoo23ZRlbpO2KGk3039ZiL2o4pGjqdCgZktFgDqF7hCrVkGWVWgvbSdG0N8HfOJQstZgVR9gVx6Tnmms+1xTBj73WkJUIruIjc+giTVAzxG+HuCVb44jJqxNy9SEKdPuTZBanVMn6AAlSjNLFhd6rDX8C7wi5w+lGR9IAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Pre-Commit execution example&quot;
        title=&quot;&quot;
        src=&quot;/static/30c21c000d810b089d75cf1a8b22d9bf/f058b/precommit.png&quot;
        srcset=&quot;/static/30c21c000d810b089d75cf1a8b22d9bf/c26ae/precommit.png 158w,
/static/30c21c000d810b089d75cf1a8b22d9bf/6bdcf/precommit.png 315w,
/static/30c21c000d810b089d75cf1a8b22d9bf/f058b/precommit.png 630w,
/static/30c21c000d810b089d75cf1a8b22d9bf/40601/precommit.png 945w,
/static/30c21c000d810b089d75cf1a8b22d9bf/0a867/precommit.png 986w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Notice that it failed because:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Documentation was not updated, so it updated and is waiting for us to add to the commit&lt;/li&gt;
&lt;li&gt;SQS should be deployed with &lt;a href=&quot;https://avd.aquasec.com/misconfig/aws/sqs/avd-aws-0096/&quot;&gt;SSE enabled&lt;/a&gt; to increase security, here is the fix (that needs to be manually performed):&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;hcl&quot;&gt;&lt;pre class=&quot;language-hcl&quot;&gt;&lt;code class=&quot;language-hcl&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// src/main.tf&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;resource &lt;span class=&quot;token type variable&quot;&gt;&quot;aws_sqs_queue&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;terraform_queue&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
 &lt;span class=&quot;token property&quot;&gt;name&lt;/span&gt;                    &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;test&quot;&lt;/span&gt;
 &lt;span class=&quot;token property&quot;&gt;sqs_managed_sse_enabled&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Great! Now we have a local environment that applies best practices! Let&apos;s move to GitHub Actions. Since we have multiple environments (in my demo I will use two environments: development and production) you can imagine that repetitive actions will be performed (e.g. terraform plan will be executed in every environment), because of that we can create a composite action to simplify our CI/CD code.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# .github/actions/terraform/action.yaml&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Terraform Action
&lt;span class=&quot;token key atrule&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Run Terraform Plan or Apply

&lt;span class=&quot;token key atrule&quot;&gt;inputs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Terraform action to perform. Allowed values: plan, apply&quot;&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;required&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean important&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;environment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Account name that is being deployed. Allowed values: DEV, PROD&quot;&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;required&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean important&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;working_directory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Path to the directory containing the terraform files
    &lt;span class=&quot;token key atrule&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; src
  &lt;span class=&quot;token key atrule&quot;&gt;aws_region&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; string
    &lt;span class=&quot;token key atrule&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; AWS region to use
    &lt;span class=&quot;token key atrule&quot;&gt;required&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean important&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;tfconfig_path&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; string
    &lt;span class=&quot;token key atrule&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Path to the HCL file that configured Terraform backend
    &lt;span class=&quot;token key atrule&quot;&gt;required&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean important&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;tfvars_path&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; string
    &lt;span class=&quot;token key atrule&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Path to the TFVARS file that contains the values for variables
    &lt;span class=&quot;token key atrule&quot;&gt;required&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean important&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;aws_role_arn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; string
    &lt;span class=&quot;token key atrule&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; AWS IAM Role that will be used to assume a role using OIDC
  &lt;span class=&quot;token key atrule&quot;&gt;github_token&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; string
    &lt;span class=&quot;token key atrule&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; GitHub Token generated during GitHub Workflow runs used to comment the plan in the Pull Request. Only needed if &apos;action&apos; is &apos;plan&apos;
    &lt;span class=&quot;token key atrule&quot;&gt;required&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean important&quot;&gt;false&lt;/span&gt;

&lt;span class=&quot;token key atrule&quot;&gt;runs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;using&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;composite&quot;&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Install Mise
      &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; jdx/mise&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;action@v2

    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Configure AWS Credentials
      &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; aws&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;actions/configure&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;aws&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;credentials@v4
      &lt;span class=&quot;token key atrule&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;role-to-assume&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; inputs.aws_role_arn &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;role-session-name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Github&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;Actions
        &lt;span class=&quot;token key atrule&quot;&gt;aws-region&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; inputs.aws_region &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Initialize terraform
      &lt;span class=&quot;token key atrule&quot;&gt;shell&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; bash
      &lt;span class=&quot;token key atrule&quot;&gt;working-directory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; inputs.working_directory &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; terraform init &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;backend&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;config=$GITHUB_WORKSPACE/$&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; inputs.tfconfig_path &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Terraform Plan
      &lt;span class=&quot;token key atrule&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; inputs.action == &apos;plan&apos; &lt;span class=&quot;token important&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; github.ref &lt;span class=&quot;token tag&quot;&gt;!=&lt;/span&gt; &apos;refs/heads/main&apos; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;shell&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; bash
      &lt;span class=&quot;token key atrule&quot;&gt;working-directory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; inputs.working_directory &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; terraform plan &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;var&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;file=$GITHUB_WORKSPACE/$&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; inputs.tfvars_path &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;out .planfile

    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Comment Terraform plan in PR
      &lt;span class=&quot;token key atrule&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; inputs.action == &apos;plan&apos; &lt;span class=&quot;token important&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; github.ref &lt;span class=&quot;token tag&quot;&gt;!=&lt;/span&gt; &apos;refs/heads/main&apos; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; borchero/terraform&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;plan&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;comment@v2
      &lt;span class=&quot;token key atrule&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;working-directory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; inputs.working_directory &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;token&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; inputs.github_token &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;planfile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; .planfile
        &lt;span class=&quot;token key atrule&quot;&gt;header&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;[ ${{ inputs.environment }} - ${{ inputs.aws_region }}] Terraform Plan&quot;&lt;/span&gt;

    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Terraform Apply
      &lt;span class=&quot;token key atrule&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; inputs.action == &apos;apply&apos; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;shell&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; bash
      &lt;span class=&quot;token key atrule&quot;&gt;working-directory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; inputs.working_directory &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; terraform apply &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;auto&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;approve &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;var&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;file=$GITHUB_WORKSPACE/$&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; inputs.tfvars_path &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The terraform plan will be displayed as a comment on the Pull Request like that:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/5ac8c0f77671fabbfe114131387c3222/f98ee/plan.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 55.69620253164557%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAABYlAAAWJQFJUiTwAAABaUlEQVR42oWS2XKCQBBFedcIIjuyDDCsAm5JaeX/v+umexQqpTE+3Oqe7cztntEu128kRYuqP6NoD/BiiVDUCNIKQUJKa2xFAysQsIOMJFT+KDvMVNQMy0ckSoynK4p6wHC8oN4d0VPkXDYjVqaHj7WrxLm+8V8ogLbQHVhejKIZIAnYEIzBst2j6g4KGGc1UqqC58NEzuBn+dB40bQ8lO0IPy6w1O15A99qWIGSPkWam9w+is9oS8OB7ceoyFlODtxQzAcXBJ828z7WK9gDMFHAkkosqEQulx/KdLZYMozAM+xXP18CN26kQHnVIyt3yMgpjzMaO+TYCVJ63fSt0xnIB7r9F+r+RN/nJs7ZcTt+3taGMwRdluQNAur1SyAn3DPLSwgckyIV7bsrdjhFL8qVeGzY4bOIM/dQyB5CJOiki0xsyYVU34VdpbJTX4Zzbov6DX+UfXfoUPMjrOkG3rQy3X9f8t0r/wCxCR7fu/fAngAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Terraform plan as GitHub comment&quot;
        title=&quot;&quot;
        src=&quot;/static/5ac8c0f77671fabbfe114131387c3222/f058b/plan.png&quot;
        srcset=&quot;/static/5ac8c0f77671fabbfe114131387c3222/c26ae/plan.png 158w,
/static/5ac8c0f77671fabbfe114131387c3222/6bdcf/plan.png 315w,
/static/5ac8c0f77671fabbfe114131387c3222/f058b/plan.png 630w,
/static/5ac8c0f77671fabbfe114131387c3222/40601/plan.png 945w,
/static/5ac8c0f77671fabbfe114131387c3222/78612/plan.png 1260w,
/static/5ac8c0f77671fabbfe114131387c3222/f98ee/plan.png 1808w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;And here is our main workflow&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# .github/workflows/main.yaml&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Terraform&quot;&lt;/span&gt;

&lt;span class=&quot;token key atrule&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;branches&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; main
  &lt;span class=&quot;token key atrule&quot;&gt;pull_request&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;

&lt;span class=&quot;token key atrule&quot;&gt;permissions&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;contents&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; write
  &lt;span class=&quot;token key atrule&quot;&gt;pull-requests&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; write
  &lt;span class=&quot;token key atrule&quot;&gt;id-token&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; write

&lt;span class=&quot;token key atrule&quot;&gt;jobs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;ci&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; github.event_name == &apos;pull_request&apos; &lt;span class=&quot;token important&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; github.ref &lt;span class=&quot;token tag&quot;&gt;!=&lt;/span&gt; &apos;refs/heads/main&apos;
    &lt;span class=&quot;token key atrule&quot;&gt;runs-on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ubuntu&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;latest
    &lt;span class=&quot;token key atrule&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Checkout
        &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; actions/checkout@v4

      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Install Mise
        &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; jdx/mise&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;action@v2

      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Run Pre&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;Commit
        &lt;span class=&quot;token key atrule&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; pre&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;commit run &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;all&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;files

  &lt;span class=&quot;token key atrule&quot;&gt;plan-dev-us-east-1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;[DEV - us-east-1] Terraform Plan&quot;&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;runs-on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ubuntu&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;latest

    &lt;span class=&quot;token key atrule&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Checkout
        &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; actions/checkout@v4

      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Terraform Plan
        &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ./.github/actions/terraform
        &lt;span class=&quot;token key atrule&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; plan
          &lt;span class=&quot;token key atrule&quot;&gt;environment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; DEV
          &lt;span class=&quot;token key atrule&quot;&gt;aws_region&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; us&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;east&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;tfconfig_path&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; config/dev.hcl
          &lt;span class=&quot;token key atrule&quot;&gt;tfvars_path&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; vars/dev/us&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;east&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;1.tfvars
          &lt;span class=&quot;token key atrule&quot;&gt;aws_role_arn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; vars.DEV_TERRAFORM_ROLE &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;github_token&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; github.token &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token key atrule&quot;&gt;apply-dev-us-east-1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;needs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; plan&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;dev&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;us&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;east&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;[DEV - us-east-1] Terraform Apply&quot;&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;runs-on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ubuntu&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;latest
    &lt;span class=&quot;token key atrule&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; github.event_name == &apos;push&apos; &lt;span class=&quot;token important&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; github.ref == &apos;refs/heads/main&apos;
    &lt;span class=&quot;token key atrule&quot;&gt;environment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; dev

    &lt;span class=&quot;token key atrule&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Checkout
        &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; actions/checkout@v4

      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Terraform Apply
        &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ./.github/actions/terraform
        &lt;span class=&quot;token key atrule&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; apply
          &lt;span class=&quot;token key atrule&quot;&gt;environment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; DEV
          &lt;span class=&quot;token key atrule&quot;&gt;aws_region&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; us&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;east&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;tfconfig_path&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; config/dev.hcl
          &lt;span class=&quot;token key atrule&quot;&gt;tfvars_path&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; vars/dev/us&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;east&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;1.tfvars
          &lt;span class=&quot;token key atrule&quot;&gt;aws_role_arn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; vars.DEV_TERRAFORM_ROLE &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;github_token&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; github.token &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token key atrule&quot;&gt;plan-prod-us-east-1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;[PROD - us-east-1] Terraform Plan&quot;&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;runs-on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ubuntu&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;latest

    &lt;span class=&quot;token key atrule&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Checkout
        &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; actions/checkout@v4

      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Terraform Plan
        &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ./.github/actions/terraform
        &lt;span class=&quot;token key atrule&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; plan
          &lt;span class=&quot;token key atrule&quot;&gt;environment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; PROD
          &lt;span class=&quot;token key atrule&quot;&gt;aws_region&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; us&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;east&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;tfconfig_path&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; config/prod.hcl
          &lt;span class=&quot;token key atrule&quot;&gt;tfvars_path&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; vars/prod/us&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;east&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;1.tfvars
          &lt;span class=&quot;token key atrule&quot;&gt;aws_role_arn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; vars.PROD_TERRAFORM_ROLE &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;github_token&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; github.token &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token key atrule&quot;&gt;apply-prod-us-east-1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;needs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; apply&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;dev&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;us&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;east&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; plan&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;prod&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;us&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;east&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;[PROD - us-east-1] Terraform Apply&quot;&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;runs-on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ubuntu&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;latest
    &lt;span class=&quot;token key atrule&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; github.event_name == &apos;push&apos; &lt;span class=&quot;token important&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; github.ref == &apos;refs/heads/main&apos;
    &lt;span class=&quot;token key atrule&quot;&gt;environment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; prod

    &lt;span class=&quot;token key atrule&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Checkout
        &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; actions/checkout@v4

      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Terraform Apply
        &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ./.github/actions/terraform
        &lt;span class=&quot;token key atrule&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; apply
          &lt;span class=&quot;token key atrule&quot;&gt;environment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; PROD
          &lt;span class=&quot;token key atrule&quot;&gt;aws_region&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; us&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;east&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;tfconfig_path&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; config/prod.hcl
          &lt;span class=&quot;token key atrule&quot;&gt;tfvars_path&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; vars/prod/us&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;east&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;1.tfvars
          &lt;span class=&quot;token key atrule&quot;&gt;aws_role_arn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; vars.PROD_TERRAFORM_ROLE &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;github_token&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; github.token &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If you are a visual person, like me, here is the workflow that we just created in the GitHub console:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/67b4713099e04b6a86dd2c8a422c183b/e431d/pipeline.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 30.37974683544304%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAGCAYAAADDl76dAAAACXBIWXMAABYlAAAWJQFJUiTwAAAA7ElEQVR42p2Q23KDIBRFvcQLKop3RSEhN00mM/3/r9s9YvvQdiYPfVizgQMLDk4+jlDXB+bTCn15Qp0fqHuNMC7QmTPNVyizUn1BKw2KWoKlJdTpZvdu6/orIybgMF4hrwZkorfJywEJb+AHGXgzoGgmCJJscKqzrEJKdXN/wSwf0NcX5HGBpAexrCahqFH1imSjvX0TpEVHwhSuF8PxIiK2Y9dn8A4JDhG3+Y3r7+kHCZys6yHNHVLfMB33LJsZQZTbg38If8p+42x9x/QnISt2EoGIeHforbBsJ3TzBaKdUXaK2tc2t5b/I/wEv9aMfVZEoKoAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Ci/Cd workflow on GitHub&quot;
        title=&quot;&quot;
        src=&quot;/static/67b4713099e04b6a86dd2c8a422c183b/f058b/pipeline.png&quot;
        srcset=&quot;/static/67b4713099e04b6a86dd2c8a422c183b/c26ae/pipeline.png 158w,
/static/67b4713099e04b6a86dd2c8a422c183b/6bdcf/pipeline.png 315w,
/static/67b4713099e04b6a86dd2c8a422c183b/f058b/pipeline.png 630w,
/static/67b4713099e04b6a86dd2c8a422c183b/40601/pipeline.png 945w,
/static/67b4713099e04b6a86dd2c8a422c183b/78612/pipeline.png 1260w,
/static/67b4713099e04b6a86dd2c8a422c183b/e431d/pipeline.png 1882w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Please notice that I&apos;m using GitHub Environment and the &lt;code class=&quot;language-text&quot;&gt;prod&lt;/code&gt; requires manual approval to deploy to production!&lt;/p&gt;
&lt;h2&gt;Cya!&lt;/h2&gt;
&lt;p&gt;I hope you&apos;ve liked it and the explanation was helpful!&lt;/p&gt;
&lt;p&gt;See you around! 👋&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Deploy a bastion host with Terraform or AWS CDK]]></title><description><![CDATA[As you probably know the best practice is to deploy resources (such as databases and servers) within a private network and if they need to…]]></description><link>https://felipetrindade.com/bastion/</link><guid isPermaLink="false">https://felipetrindade.com/bastion/</guid><pubDate>Sat, 01 Feb 2025 12:55:32 GMT</pubDate><content:encoded>&lt;p&gt;As you probably know the best practice is to deploy resources (such as databases and servers) within a private network and if they need to be exposed you should expose them using Load Balancer to handle the requests and traffic distribution.&lt;/p&gt;
&lt;p&gt;Some resources, such as databases, you don&apos;t want ever to expose publicly. A common request in tech companies is to allow developers to connect to development databases deployed in the cloud provider. Some companies also create processes for allowing access in production in case of an urgent need!&lt;/p&gt;
&lt;p&gt;The problem is that since databases are deployed within a private network, the developers won&apos;t be able to connect to the database. There are two common options here:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Use a VPN to connect the developers to the private network&lt;/li&gt;
&lt;li&gt;Use a bastion host to connect to the private resources&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This blog post aims at the latter and you will see how easy and simple it is to deploy a bastion host securely! All the source code used in this repository is available in my &lt;a href=&quot;http://github.com/felipelaptrin/bastion&quot;&gt;GitHub Repository&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Bastion Host and Systems Manager&lt;/h2&gt;
&lt;p&gt;A bastion Host (also known as jump box) is simply a server that has access to a private network but it&apos;s exposed publicly in a way that users (usually developers) can connect to it. The connectivity is usually done using SSH for Linux-based instances or RDP if it&apos;s a Windows instance. You would also need to configure the firewall of the instance correctly to allow inbound traffic from developers&apos; IPs (and change this constantly, since internet providers usually offer dynamic IP) or from anywhere (too permissive - turning your bastion into a possible source of attacks). This is the standard way to deploy a bastion host in a VPS.&lt;/p&gt;
&lt;p&gt;In AWS we can deploy bastion hosts using the approach mentioned above or in a different way, that relies on a service called &lt;a href=&quot;https://docs.aws.amazon.com/systems-manager/latest/userguide/what-is-systems-manager.html&quot;&gt;AWS Systems Manager&lt;/a&gt; (as known as SSM). It is a big AWS service used to manage servers (e.g. EC2, edge devices, on-premise servers): execute scripts, perform updates, install tools... It is a great service to manage nodes at scale!&lt;/p&gt;
&lt;p&gt;For this blog post, the only thing we need to know about SSM is:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Systems Manager Agent&lt;/strong&gt;: A node can only be managed by SSM if it has an agent (a piece of software that runs as a daemon) installed and running in the machine. This agent is called &lt;a href=&quot;https://docs.aws.amazon.com/systems-manager/latest/userguide/ssm-agent.html&quot;&gt;Systems Manager Agent&lt;/a&gt; and its source code is open and available on &lt;a href=&quot;https://github.com/aws/amazon-ssm-agent&quot;&gt;GitHub&lt;/a&gt;. This agent can be installed in EC2, Virtual Machines, edge devices... When using EC2 machines you can use AMIs that already have this agent &lt;a href=&quot;https://docs.aws.amazon.com/systems-manager/latest/userguide/ami-preinstalled-agent.html&quot;&gt;preinstalled&lt;/a&gt; or &lt;a href=&quot;https://docs.aws.amazon.com/systems-manager/latest/userguide/manually-install-ssm-agent-linux.html&quot;&gt;install&lt;/a&gt; it yourself (commonly done using the EC2 &lt;a href=&quot;https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/user-data.html&quot;&gt;UserData&lt;/a&gt; script). Notice that the agent needs to access the SSM Endpoint (publicly or via VPC Endpoint) to be able to communicate with the service, so make sure the EC2 instance is in a public subnet with Internet Gateway configured properly or in a private subnet with access to NAT Gateway or VPC Endpoint configured.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Session Manager&lt;/strong&gt;: As mentioned, SSM allows us to run several different actions in a machine that contains the SSM Agent installed. These actions are called &lt;a href=&quot;https://docs.aws.amazon.com/systems-manager/latest/userguide/documents.html&quot;&gt;Documents&lt;/a&gt;, and we are interested in the &lt;code class=&quot;language-text&quot;&gt;AWS-StartPortForwardingSessionToRemoteHost&lt;/code&gt; document that is part of the &lt;a href=&quot;https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager.html&quot;&gt;Session Manager&lt;/a&gt; tool. The Session Manager is a secure way to manage/access your servers via web-based sessions (via AWS Console) or AWS CLI (but you will need to install a &lt;a href=&quot;https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager-working-with-install-plugin.html&quot;&gt;plugin&lt;/a&gt;), in other words, it&apos;s a &quot;replacement&quot; for using pure SSH connection, which means that now you can rely on SSM to handle security and traffic (no need to manage firewall rules anymore). Notice that this is only possible because the SSM Agent is handling all of that, so it&apos;s a must-have. Instead of managing firewall and SSH keys, the way we manage/control who will have access to the instance is via IAM policies on two fronts: permission of the EC2/On-prem instance to allow usage of SSM service (on-premise servers use a &lt;a href=&quot;https://docs.aws.amazon.com/systems-manager/latest/userguide/hybrid-activation-managed-nodes.html&quot;&gt;hybrid activation method&lt;/a&gt;) and permission of the IAM User/Role that will access the instance.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 541px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/69adfe6a1aade0ec61ec40639dc0327b/9d576/Bastion.drawio.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 44.93670886075949%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAYAAAAywQxIAAAACXBIWXMAAAsTAAALEwEAmpwYAAABcklEQVR42oVSyUoDQRDN2X/xjzz4F6J48+LFn5CAXhVBBEFBFHOJGjAhG5h1sowzmSST3vtZMwntZDxYUBT1uuu9rq4qYGPWmDTOn2voHJ1DjmdgLQ/SC2CEhFkJGCZguUxjmm+w1LmAXjIUHKG1aTQxh1WaBCwVK1ipkbVIKUhDmJFURPcyZ0ldATlL1PJmnShwUH7D3dBHfQG8+zH6y5V7jCNUpBrNIkiKPFpCWwM+jRAe32B+9gBJ7SitoKRAbc7QqNxjXNxH7+0aA2Ggjd4mZIwhCAIIKcFmCwhLAvUBJrunmO0VU4wTGV/FGHKLzssF2OEOgtsTePT1msQcIecc1WoVpVIJ3W4XklpITDOJ5tUrek+fWz37VPQ16uHj8RKtZhme0PRC80sYx7EjbLfbEIt4TUiX+uE3RlG4Jtx4KAQakwCVUYSGH2HKOEz+D/8bSn5ANqPwZ8rJhLKe7FayKokboWg7lMsdLpXzLZxJ/ADdRLMdhN07LgAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Bastion Host infrastructure&quot;
        title=&quot;&quot;
        src=&quot;/static/69adfe6a1aade0ec61ec40639dc0327b/9d576/Bastion.drawio.png&quot;
        srcset=&quot;/static/69adfe6a1aade0ec61ec40639dc0327b/c26ae/Bastion.drawio.png 158w,
/static/69adfe6a1aade0ec61ec40639dc0327b/6bdcf/Bastion.drawio.png 315w,
/static/69adfe6a1aade0ec61ec40639dc0327b/9d576/Bastion.drawio.png 541w&quot;
        sizes=&quot;(max-width: 541px) 100vw, 541px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2&gt;Demo time!&lt;/h2&gt;
&lt;p&gt;Let&apos;s demo the Bastion Host deployment using Terraform and CDK! To test that the bastion host is working I will also deploy an RDS instance (I will use Postgres) in a private subnet. Our goal is to deploy a bastion instance in a private subnet, be able to connect to it using SSM Session Manager, and connect to the RDS using a GUI (such as DBeaver) and CLI (psql). The following resources will be created in Terraform and CDK:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;VPC&lt;/li&gt;
&lt;li&gt;EC2 to serve as Bastion&lt;/li&gt;
&lt;li&gt;IAM Roles&lt;/li&gt;
&lt;li&gt;Single instance RDS (Postgres)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Terraform&lt;/h3&gt;
&lt;p&gt;For simplicity, I will rely on Terraform community modules to deploy the needed resources. Let&apos;s check the variables of our code:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;hcl&quot;&gt;&lt;pre class=&quot;language-hcl&quot;&gt;&lt;code class=&quot;language-hcl&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// variables.tf&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;variable&lt;span class=&quot;token type variable&quot;&gt; &quot;aws_region&quot; &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;description&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Region that AWS resources will be deployed&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;type&lt;/span&gt;        &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; string
  &lt;span class=&quot;token property&quot;&gt;default&lt;/span&gt;     &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;us-east-1&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;variable&lt;span class=&quot;token type variable&quot;&gt; &quot;vpc_cidr&quot; &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;description&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;VPC CIDR to use&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;type&lt;/span&gt;        &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; string
  &lt;span class=&quot;token property&quot;&gt;default&lt;/span&gt;     &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;10.123.0.0/16&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;variable&lt;span class=&quot;token type variable&quot;&gt; &quot;azs&quot; &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;description&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Number of AZs to use in the VPC&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;type&lt;/span&gt;        &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; number
  &lt;span class=&quot;token property&quot;&gt;default&lt;/span&gt;     &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;variable&lt;span class=&quot;token type variable&quot;&gt; &quot;instance_type&quot; &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;description&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Defines the instance type of the EC2 bastion host&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;type&lt;/span&gt;        &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; string
  &lt;span class=&quot;token property&quot;&gt;default&lt;/span&gt;     &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;t3.nano&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And then the data sources:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;hcl&quot;&gt;&lt;pre class=&quot;language-hcl&quot;&gt;&lt;code class=&quot;language-hcl&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// data.tf&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;data &lt;span class=&quot;token type variable&quot;&gt;&quot;aws_availability_zones&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;available&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;data &lt;span class=&quot;token type variable&quot;&gt;&quot;aws_ec2_instance_type&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;this&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;instance_type&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; var.instance_type
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;data &lt;span class=&quot;token type variable&quot;&gt;&quot;aws_ami&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;ubuntu_latest&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;most_recent&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;owners&lt;/span&gt;      &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;099720109477&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# Canonical&apos;s AWS account ID (maintainer of Ubuntu)&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;filter&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;name&lt;/span&gt;   &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;name&quot;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;values&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;ubuntu/images/hvm-ssd/ubuntu-*-*-&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;local&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token type variable&quot;&gt;instance_architecture&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;x86_64&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;amd64&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;arm64&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;-server-*&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;filter&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;name&lt;/span&gt;   &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;architecture&quot;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;values&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;local&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token type variable&quot;&gt;instance_architecture&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And now the constants file:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;hcl&quot;&gt;&lt;pre class=&quot;language-hcl&quot;&gt;&lt;code class=&quot;language-hcl&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// locals.tf&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;locals&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;azs&lt;/span&gt;                   &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; slice(data.aws_availability_zones.available.names, &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;, var.azs)
  &lt;span class=&quot;token property&quot;&gt;instance_architecture&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; contains(data.aws_ec2_instance_type.this.supported_architectures, &lt;span class=&quot;token string&quot;&gt;&quot;arm64&quot;&lt;/span&gt;) ? &lt;span class=&quot;token property&quot;&gt;&quot;arm64&quot;&lt;/span&gt; : &lt;span class=&quot;token string&quot;&gt;&quot;x86_64&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Finally, here is the creation of the resources:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;hcl&quot;&gt;&lt;pre class=&quot;language-hcl&quot;&gt;&lt;code class=&quot;language-hcl&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// main.tf&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;module&lt;span class=&quot;token type variable&quot;&gt; &quot;vpc&quot; &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;source&lt;/span&gt;          &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;terraform-aws-modules/vpc/aws&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;version&lt;/span&gt;         &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;5.16.0&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;name&lt;/span&gt;            &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;bastion-terraform&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;azs&lt;/span&gt;             &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; local.azs
  &lt;span class=&quot;token property&quot;&gt;cidr&lt;/span&gt;            &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; var.vpc_cidr
  &lt;span class=&quot;token property&quot;&gt;private_subnets&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;for k, v in local.azs : cidrsubnet(var.vpc_cidr, &lt;span class=&quot;token number&quot;&gt;8&lt;/span&gt;, k)&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;public_subnets&lt;/span&gt;  &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;for k, v in local.azs : cidrsubnet(var.vpc_cidr, &lt;span class=&quot;token number&quot;&gt;8&lt;/span&gt;, k + &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;)&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;

  &lt;span class=&quot;token property&quot;&gt;database_subnets&lt;/span&gt;                   &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;for k, v in local.azs : cidrsubnet(var.vpc_cidr, &lt;span class=&quot;token number&quot;&gt;8&lt;/span&gt;, k + &lt;span class=&quot;token number&quot;&gt;8&lt;/span&gt;)&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;create_database_subnet_route_table&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;create_database_nat_gateway_route&lt;/span&gt;  &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;

  &lt;span class=&quot;token property&quot;&gt;enable_nat_gateway&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;single_nat_gateway&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;module&lt;span class=&quot;token type variable&quot;&gt; &quot;bastion_sg&quot; &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;source&lt;/span&gt;  &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;terraform-aws-modules/security-group/aws&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;version&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;5.3.0&quot;&lt;/span&gt;

  &lt;span class=&quot;token property&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;bastion-terraform-sg&quot;&lt;/span&gt;

  &lt;span class=&quot;token property&quot;&gt;vpc_id&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; module.vpc.vpc_id
  &lt;span class=&quot;token property&quot;&gt;egress_with_cidr_blocks&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;from_port&lt;/span&gt;   &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;to_port&lt;/span&gt;     &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;65535&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;protocol&lt;/span&gt;    &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;tcp&quot;&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;description&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Allow all outbound traffic&quot;&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;cidr_blocks&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;0.0.0.0/0&quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;,
  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;module&lt;span class=&quot;token type variable&quot;&gt; &quot;bastion&quot; &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;source&lt;/span&gt;  &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;terraform-aws-modules/ec2-instance/aws&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;version&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;5.7.1&quot;&lt;/span&gt;

  &lt;span class=&quot;token property&quot;&gt;name&lt;/span&gt;                   &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;bastion-terraform&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;instance_type&lt;/span&gt;          &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; var.instance_type
  &lt;span class=&quot;token property&quot;&gt;vpc_security_group_ids&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;module.bastion_sg.security_group_id&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;subnet_id&lt;/span&gt;              &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; module.vpc.private_subnets&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;ignore_ami_changes&lt;/span&gt;     &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;ami&lt;/span&gt;                    &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; data.aws_ami.ubuntu_latest.id

  &lt;span class=&quot;token property&quot;&gt;create_iam_instance_profile&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;iam_role_policies&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;AmazonSSMManagedInstanceCore&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore&quot;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token property&quot;&gt;user_data&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token heredoc string&quot;&gt;&amp;lt;&amp;lt;EOF
#!/bin/bash

echo &quot;Installing SSM Agent&quot;
sudo snap install amazon-ssm-agent --classic
sudo snap list amazon-ssm-agent
sudo snap start amazon-ssm-agent
sudo snap services amazon-ssm-agent
EOF&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;


&lt;span class=&quot;token keyword&quot;&gt;module&lt;span class=&quot;token type variable&quot;&gt; &quot;database_sg&quot; &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;source&lt;/span&gt;  &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;terraform-aws-modules/security-group/aws&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;version&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;5.3.0&quot;&lt;/span&gt;

  &lt;span class=&quot;token property&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;database-terraform-sg&quot;&lt;/span&gt;

  &lt;span class=&quot;token property&quot;&gt;vpc_id&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; module.vpc.vpc_id
  &lt;span class=&quot;token property&quot;&gt;ingress_with_source_security_group_id&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;from_port&lt;/span&gt;                &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5432&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;to_port&lt;/span&gt;                  &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5432&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;protocol&lt;/span&gt;                 &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;tcp&quot;&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;description&lt;/span&gt;              &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Allow inbound traffic on default Postgres port&quot;&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;source_security_group_id&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; module.bastion_sg.security_group_id
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;,
  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;egress_with_cidr_blocks&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;from_port&lt;/span&gt;   &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;to_port&lt;/span&gt;     &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;65535&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;protocol&lt;/span&gt;    &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;tcp&quot;&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;description&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Allow all outbound traffic&quot;&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;cidr_blocks&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;0.0.0.0/0&quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;,
  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;module&lt;span class=&quot;token type variable&quot;&gt; &quot;database&quot; &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;source&lt;/span&gt;  &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;terraform-aws-modules/rds/aws&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;version&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;6.10.0&quot;&lt;/span&gt;

  &lt;span class=&quot;token property&quot;&gt;identifier&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;database-terraform&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;db_name&lt;/span&gt;    &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;main&quot;&lt;/span&gt;

  &lt;span class=&quot;token property&quot;&gt;engine&lt;/span&gt;               &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;postgres&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;engine_version&lt;/span&gt;       &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;14&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;family&lt;/span&gt;               &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;postgres14&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;major_engine_version&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;14&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;instance_class&lt;/span&gt;       &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;db.t4g.large&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;allocated_storage&lt;/span&gt;    &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;

  &lt;span class=&quot;token property&quot;&gt;username&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;postgres&quot;&lt;/span&gt;

  &lt;span class=&quot;token property&quot;&gt;db_subnet_group_name&lt;/span&gt;   &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; module.vpc.database_subnet_group
  &lt;span class=&quot;token property&quot;&gt;vpc_security_group_ids&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;module.database_sg.security_group_id&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;

  &lt;span class=&quot;token property&quot;&gt;skip_final_snapshot&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;deletion_protection&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Notice that we are getting the AMI image from &lt;code class=&quot;language-text&quot;&gt;data.tf&lt;/code&gt; and based on the &lt;code class=&quot;language-text&quot;&gt;var.instance_type&lt;/code&gt; we are getting the correct Ubuntu AMI for the bastion instance. Also, we are manually installing the SSM Agent via EC2 User Data script.&lt;/p&gt;
&lt;p&gt;Deploying that is as easy as running &lt;code class=&quot;language-text&quot;&gt;terraform apply&lt;/code&gt; (or &lt;code class=&quot;language-text&quot;&gt;tofu apply&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;Now, after the infrastructure is deployed we can create a simple script to automatically:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Get EC2 Instance ID based on the Bastion name&lt;/li&gt;
&lt;li&gt;Get RDS Endpoint, master Username/Password (in Secrets Manager), Port&lt;/li&gt;
&lt;li&gt;Use &lt;code class=&quot;language-text&quot;&gt;AWS-StartPortForwardingSessionToRemoteHost&lt;/code&gt; document to connect to RDS&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;BASTION_NAME&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;bastion-terraform&quot;&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;DATABASE_NAME&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;database-terraform&quot;&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;AWS_REGION&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;us-east-1&quot;&lt;/span&gt;

&lt;span class=&quot;token assign-left variable&quot;&gt;SECRET_ARN&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;aws rds describe-db-instances --db-instance-identifier &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  $DATABASE_NAME &lt;span class=&quot;token parameter variable&quot;&gt;--query&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;DBInstances[0].MasterUserSecret.SecretArn&apos;&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--output&lt;/span&gt; text&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;SECRET_VALUE_RAW&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;aws secretsmanager get-secret-value --secret-id $SECRET_ARN &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; jq &lt;span class=&quot;token string&quot;&gt;&apos;.SecretString&apos;&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;DB_USERNAME&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; $SECRET_VALUE_RAW &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; jq &lt;span class=&quot;token parameter variable&quot;&gt;-r&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; jq &lt;span class=&quot;token parameter variable&quot;&gt;-r&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;.username&quot;&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;DB_PASSWORD&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; $SECRET_VALUE_RAW &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; jq &lt;span class=&quot;token parameter variable&quot;&gt;-r&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; jq &lt;span class=&quot;token parameter variable&quot;&gt;-r&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;.password&quot;&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;DB_PORT&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;aws rds describe-db-instances --db-instance-identifier &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  $DATABASE_NAME &lt;span class=&quot;token parameter variable&quot;&gt;--query&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;DBInstances[0].Endpoint.Port&apos;&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--output&lt;/span&gt; text&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;DB_DATABASE&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;aws rds describe-db-instances &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  --db-instance-identifier $DATABASE_NAME &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;--query&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;DBInstances[0].DBName&apos;&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--output&lt;/span&gt; text&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Connect to the database using the following inputs:&quot;&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token entity&quot; title=&quot;\n&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;token entity&quot; title=&quot;\n&quot;&gt;\n&lt;/span&gt;Host =&gt; localhost&lt;span class=&quot;token entity&quot; title=&quot;\n&quot;&gt;\n&lt;/span&gt;Database =&gt; &lt;span class=&quot;token variable&quot;&gt;$DB_DATABASE&lt;/span&gt;&lt;span class=&quot;token entity&quot; title=&quot;\n&quot;&gt;\n&lt;/span&gt;USERNAME =&gt; &lt;span class=&quot;token variable&quot;&gt;$DB_USERNAME&lt;/span&gt;&lt;span class=&quot;token entity&quot; title=&quot;\n&quot;&gt;\n&lt;/span&gt;PASSWORD =&gt; &lt;span class=&quot;token variable&quot;&gt;$DB_PASSWORD&lt;/span&gt;&lt;span class=&quot;token entity&quot; title=&quot;\n&quot;&gt;\n&lt;/span&gt;PORT =&gt; &lt;span class=&quot;token variable&quot;&gt;$DB_PORT&lt;/span&gt;&quot;&lt;/span&gt;

&lt;span class=&quot;token assign-left variable&quot;&gt;CLUSTER_HOST&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;aws rds describe-db-instances &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  --db-instance-identifier $DATABASE_NAME &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;--query&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;DBInstances[0].Endpoint.Address&apos;&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--output&lt;/span&gt; text&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;BASTION_INSTANCE_ID&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;aws ec2 describe-instances &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;--filters&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Name=tag:Name,Values=&lt;span class=&quot;token variable&quot;&gt;$BASTION_NAME&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;--filters&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Name=instance-state-name,Values=running&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;--query&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Reservations[*].Instances[*].InstanceId&quot;&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--output&lt;/span&gt; text&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
aws &lt;span class=&quot;token parameter variable&quot;&gt;--region&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;us-east-1 ssm start-session &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;--target&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$BASTION_INSTANCE_ID&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  --document-name AWS-StartPortForwardingSessionToRemoteHost &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;--parameters&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;{&lt;span class=&quot;token entity&quot; title=&quot;\&amp;quot;&quot;&gt;\&quot;&lt;/span&gt;host&lt;span class=&quot;token entity&quot; title=&quot;\&amp;quot;&quot;&gt;\&quot;&lt;/span&gt;:[&lt;span class=&quot;token entity&quot; title=&quot;\&amp;quot;&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$CLUSTER_HOST&lt;/span&gt;&lt;span class=&quot;token entity&quot; title=&quot;\&amp;quot;&quot;&gt;\&quot;&lt;/span&gt;],&lt;span class=&quot;token entity&quot; title=&quot;\&amp;quot;&quot;&gt;\&quot;&lt;/span&gt;portNumber&lt;span class=&quot;token entity&quot; title=&quot;\&amp;quot;&quot;&gt;\&quot;&lt;/span&gt;:[&lt;span class=&quot;token entity&quot; title=&quot;\&amp;quot;&quot;&gt;\&quot;&lt;/span&gt;5432&lt;span class=&quot;token entity&quot; title=&quot;\&amp;quot;&quot;&gt;\&quot;&lt;/span&gt;], &lt;span class=&quot;token entity&quot; title=&quot;\&amp;quot;&quot;&gt;\&quot;&lt;/span&gt;localPortNumber&lt;span class=&quot;token entity&quot; title=&quot;\&amp;quot;&quot;&gt;\&quot;&lt;/span&gt;:[&lt;span class=&quot;token entity&quot; title=&quot;\&amp;quot;&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$DB_PORT&lt;/span&gt;&lt;span class=&quot;token entity&quot; title=&quot;\&amp;quot;&quot;&gt;\&quot;&lt;/span&gt;]}&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This script will output the following:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 517px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/8856f54d6ddbff2ae9d9c7057ea534a2/fa2f5/script-output.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 34.810126582278485%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAYAAAAIy204AAAACXBIWXMAAAsTAAALEwEAmpwYAAABXUlEQVR42o1RWU/CYBDsLzAKGg9EjcohhxyWlpaj9ICW+74JRlESffHJ3z9+uwGefZjM7G6/3dmtJNfGCGt1xIotqPUZIoUGjh5k+CJ5nAgwh1Xm/0Cqz7dYfv8i6wxQ6qxg9l9x+2LDH83jLKbj9Ek/sD+q8SPivfbttP/QcLbFaPMDrTEXTYfcVGvOIddGiAq3yUoXtEXS6B7cHocUHBPv9Q7cMF5u4052xOOmcGZx8jxexGWyjKvnMgIpAxeJktAGx1S7TpsCFc5RPSA05cippDcXMHprFIUzZ/SOSn/NDilHseJNoLgT1KYfKLSXsIdvaK++OLZF3Z19ojrZiNqCzyIp3gI5d4yMPWDON2ZQvSmvGsyY7I7c3GQt4YbYZk01chXMWMy0EZ1DUpyq+LMthPIuQpqHhNHBfa4q4IDO8ai6iOh1ccMOYqU235WGpwXo25TZ4+Epq8+D/wD4vuZRmahFjAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Output of the script to connect to database&quot;
        title=&quot;&quot;
        src=&quot;/static/8856f54d6ddbff2ae9d9c7057ea534a2/fa2f5/script-output.png&quot;
        srcset=&quot;/static/8856f54d6ddbff2ae9d9c7057ea534a2/c26ae/script-output.png 158w,
/static/8856f54d6ddbff2ae9d9c7057ea534a2/6bdcf/script-output.png 315w,
/static/8856f54d6ddbff2ae9d9c7057ea534a2/fa2f5/script-output.png 517w&quot;
        sizes=&quot;(max-width: 517px) 100vw, 517px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;You can then use DBeaver or PSQL to connect to the database. Notice that your terminal will be &quot;blocked&quot; with a pending session with the bastion host. If you close or cancel the command the connection is lost and you won&apos;t be able to reach the database. This makes possible to access the database as it was running locally.&lt;/p&gt;
&lt;p&gt;Connecting via GUI is as simple as inputing the information that came from the script output.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/d560de3bcbcb08aea2f0bffc2862de2e/76823/dbeaver.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 60.12658227848101%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAAsTAAALEwEAmpwYAAACEUlEQVR42m1TyY7TQBTsE3zKIGVxiJQbyWHEYWJns9Pet7S32M4yCZobfwDidxB8XfHaThiG5FByy+pXVa9eP5ZuK5T7A0S+hRttYNgultyCbtpY/QPdct5gZbngnONR7PHux2+8//6T8AtMm83g+T5UTcOn8RhTbQZOBWtZyG0YRGbQeaFzzJbGDdQVhxoXWIgKWpSDTSYTGIaBMZF9eHggwjnyeg87KhCIHGGSwg5CmF6AtePdATk1LZiWDct2wGbkUCN3o9EInU4H6nwBQTFEaUExlLD9CG64IcREKokjmH7Ynt2QxOL2flbAoXtsOp1CVVUoioJut4snark8nBCn5E5kCDYJnEjA8GJw1wO/OOOu/xc+3QmTrBFmw+EQg8HgDeF2dySHOY5fXrA7npDVB9iibMi4t4FFDrkrI/AbyEHqBCnE+v1+QyZJr4RZtWvsS1WpLhGIpGnXpgFEadaQvjoNSChocmaS7IawrCk7KiDVa4EXt23lJFbsDq+gAW7pX1qfYcopK4p0SBgo6HU7+PykgkcJ1qFov0H7Nu9Bp7eoy7bDpAV1wNr8BuTwI3q9HjmcI8hKOBtqi1wZNMn/H/UVcimq4xlRUaM+vyCt9mDLtYkVbYbEwuDNVshW5WM2LoU3ri6QmbXRyCwjyjQEk7nUzydSem5W796a3YO8F4gU1WGPVbDFOPyGR+8r/gDnUt6xHsiTfgAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;DBeaver connection page&quot;
        title=&quot;&quot;
        src=&quot;/static/d560de3bcbcb08aea2f0bffc2862de2e/f058b/dbeaver.png&quot;
        srcset=&quot;/static/d560de3bcbcb08aea2f0bffc2862de2e/c26ae/dbeaver.png 158w,
/static/d560de3bcbcb08aea2f0bffc2862de2e/6bdcf/dbeaver.png 315w,
/static/d560de3bcbcb08aea2f0bffc2862de2e/f058b/dbeaver.png 630w,
/static/d560de3bcbcb08aea2f0bffc2862de2e/40601/dbeaver.png 945w,
/static/d560de3bcbcb08aea2f0bffc2862de2e/76823/dbeaver.png 1038w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;It&apos;s also simple to connect via PSQL.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/139aa16ebbb86112a7505bfeb6ea585a/3996e/psql.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 23.417721518987342%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAFCAYAAABFA8wzAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA40lEQVR42q2P3U7CQBCF9wUUNHqBaAw/RVpUaiNthUJbK6DSmlhCjDEoj+abfu4scuuVF1/O2Z0zs7Oq+faNO1/jZa+Ez+/clxvOvYzucIEzzjmyh+y1BlTaPhWjW/Y1Vcs3XlQ46ASorPwkKj5Iyi+y5Ybb2YrGYEq9n9AKZsY7UYEd5fQmBd3Rgt644DJ+MTU5t4M5TX/KST9GeQ9L3DTH1oXO3RPHzkgPi6ldTzh1E2pXoil17c9uUnMnj4mXjKjJ65z0Kit8xAk8s7qsLFStnfrG7770J7+9ajsk5PDif/gBfL2aNt5OWz8AAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;PSQL connection&quot;
        title=&quot;&quot;
        src=&quot;/static/139aa16ebbb86112a7505bfeb6ea585a/f058b/psql.png&quot;
        srcset=&quot;/static/139aa16ebbb86112a7505bfeb6ea585a/c26ae/psql.png 158w,
/static/139aa16ebbb86112a7505bfeb6ea585a/6bdcf/psql.png 315w,
/static/139aa16ebbb86112a7505bfeb6ea585a/f058b/psql.png 630w,
/static/139aa16ebbb86112a7505bfeb6ea585a/3996e/psql.png 648w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;We can also connect to the instance terminal using SSM Session Manager:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token assign-left variable&quot;&gt;BASTION_INSTANCE_ID&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;aws ec2 describe-instances &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;token parameter variable&quot;&gt;--filters&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Name=tag:Name,Values=&lt;span class=&quot;token variable&quot;&gt;$BASTION_NAME&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;token parameter variable&quot;&gt;--filters&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Name=instance-state-name,Values=running&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;token parameter variable&quot;&gt;--query&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Reservations[*].Instances[*].InstanceId&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;token parameter variable&quot;&gt;--output&lt;/span&gt; text&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
aws ssm start-session &lt;span class=&quot;token parameter variable&quot;&gt;--target&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$BASTION_INSTANCE_ID&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--region&lt;/span&gt; us-east-1&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 612px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/6d9acd804229cf524dd08c29a0d4bea8/8c76f/start-session.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 32.911392405063296%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAYAAAAIy204AAAACXBIWXMAAAsTAAALEwEAmpwYAAABQElEQVR42m1R127CQBD0e6SEUEwgxAVXbOOGCy60NFC+g7/JR092D2EFiYfRece3s7NzUnT+hXU8I0kqlNU7smKPdf2BOG0JDSI6o6RBkm2wyreCm1sRFkGBmeqhN1DwPFQJGp76M0imkyCly364xu7wg3rzjXZ7RN1+IS8PaKje7k9iyGZ3QkFDg7DCeGqTmNqBBRkSi7FQ1XwKIRbm6a6fw/UyctfCW5aiDqIKy7iGvVhhMnMwejEhTyyB/ki/CL6RbUUPyL5Pq8R4VTxxwaBvlXjbXWFuRrCcFLwNcyxkuSl0I4RGMOmfPDGFU4kd8dSU8tGpkZv4MrtkIYazyMhVBn9J7v2i24Jr7h1PLcpPuThsKZeQQjfsRNgeyPMb3OOuGI4Ngeu6QpCzYVcPj1NB/A+6N9C6F7zl1bsPwoJ/5JzLNEt52TIAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Start Session Manager using AWS CLI&quot;
        title=&quot;&quot;
        src=&quot;/static/6d9acd804229cf524dd08c29a0d4bea8/8c76f/start-session.png&quot;
        srcset=&quot;/static/6d9acd804229cf524dd08c29a0d4bea8/c26ae/start-session.png 158w,
/static/6d9acd804229cf524dd08c29a0d4bea8/6bdcf/start-session.png 315w,
/static/6d9acd804229cf524dd08c29a0d4bea8/8c76f/start-session.png 612w&quot;
        sizes=&quot;(max-width: 612px) 100vw, 612px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Notice that we didn&apos;t specify a document to be used. This is because if no document is given it will launch the shell to manage the node by default.&lt;/p&gt;
&lt;h3&gt;CDK&lt;/h3&gt;
&lt;p&gt;For CDK a single Stack will be created using Typescript.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// mais.ts&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;source-map-support/register&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; cdk &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;aws-cdk-lib&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; Stack &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;./stack&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; InstanceClass&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; InstanceSize&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; InstanceType &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;aws-cdk-lib/aws-ec2&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; app &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;cdk&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;App&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Stack&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;app&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;BastionStack&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  env&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    region&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;us-east-1&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  instanceType&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; InstanceType&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;InstanceClass&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;T3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; InstanceSize&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;SMALL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  vpcCidrBlock&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;10.130.0.0/16&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The main code instantiate the Stack that was created in a separate file.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; cdk &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;aws-cdk-lib&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  BastionHostLinux&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  InstanceClass&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  InstanceSize&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  IpAddresses&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  Port&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  SubnetType&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  Vpc&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;aws-cdk-lib/aws-ec2&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; Construct &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;constructs&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; InstanceType &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;aws-cdk-lib/aws-ec2&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  Credentials&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  DatabaseInstance&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  DatabaseInstanceEngine&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  PostgresEngineVersion&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;aws-cdk-lib/aws-rds&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BastionProps&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;cdk&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;StackProps &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  vpcCidrBlock&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  instanceType&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; InstanceType&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Stack&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;cdk&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Stack &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;readonly&lt;/span&gt; vpc&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Vpc&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;scope&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Construct&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; id&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; props&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; BastionProps&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;scope&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; props&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;vpc &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Vpc&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Vpc&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      ipAddresses&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; IpAddresses&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;cidr&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;vpcCidrBlock&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      subnetConfiguration&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          cidrMask&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;22&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Private&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          subnetType&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; SubnetType&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;PRIVATE_WITH_EGRESS&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          cidrMask&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;22&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;PrivateForDB&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          subnetType&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; SubnetType&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;PRIVATE_ISOLATED&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          cidrMask&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;22&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Public&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          subnetType&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; SubnetType&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;PUBLIC&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      natGateways&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; database &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DatabaseInstance&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Database&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      vpc&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;vpc&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      engine&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; DatabaseInstanceEngine&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;postgres&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        version&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; PostgresEngineVersion&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;VER_14&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      instanceType&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; InstanceType&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;InstanceClass&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;T4G&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; InstanceSize&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;LARGE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      multiAz&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      allocatedStorage&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      vpcSubnets&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        subnetType&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; SubnetType&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;PRIVATE_ISOLATED&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      credentials&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Credentials&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fromGeneratedSecret&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;postgres&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      removalPolicy&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; cdk&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;RemovalPolicy&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;DESTROY&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      databaseName&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;main&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      instanceIdentifier&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;database-cdk&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; bastion &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BastionHostLinux&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;BastionEc2&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      vpc&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;vpc&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      instanceName&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;bastion-cdk&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      instanceType&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;instanceType&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      subnetSelection&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        subnetType&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; SubnetType&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;PRIVATE_WITH_EGRESS&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    bastion&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;connections&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;allowTo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;database&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Port&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;tcp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;5432&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;Cya!&lt;/h2&gt;
&lt;p&gt;Well, I hope you liked this blog post. See you around! 👋&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Learn AWS Step Functions from practical examples with Terraform]]></title><description><![CDATA[AWS Step Functions is an amazing AWS service that allows you to create state machines (workflows) to orchestrate processes, microservices…]]></description><link>https://felipetrindade.com/step-functions/</link><guid isPermaLink="false">https://felipetrindade.com/step-functions/</guid><pubDate>Mon, 06 Jan 2025 12:55:32 GMT</pubDate><content:encoded>&lt;p&gt;AWS Step Functions is an amazing AWS service that allows you to create state machines (workflows) to orchestrate processes, microservices, jobs, etc. It&apos;s commonly used for orchestrating data pipelines and performing machine learning training/executions but it can be used whatever way fits your needs.&lt;/p&gt;
&lt;p&gt;I would like to go over this service and explain to you how it works, what you can do with it, and how to use it with several practical examples that you can reproduce using Terraform.&lt;/p&gt;
&lt;h2&gt;State Machine&lt;/h2&gt;
&lt;p&gt;A state machine is comprised of several event-driven steps (known as &lt;code class=&quot;language-text&quot;&gt;state&lt;/code&gt;) that can modify/interact with the &lt;code class=&quot;language-text&quot;&gt;flow&lt;/code&gt; of the execution or execute a &lt;code class=&quot;language-text&quot;&gt;task&lt;/code&gt; (action) in AWS (basically an API call to an AWS service). Explaining in simple words, a state machine is a bunch of actions that are executed in a sequential order (unless &lt;code class=&quot;language-text&quot;&gt;Parallel&lt;/code&gt; state is used - more on that later!) and that you can decide the actions to execute based on logic/data. In AWS, a state machine is defined via a JSON that specifies the list of states, order, handling errors, and content... Usually, this JSON is not built manually and it&apos;s common to use the AWS Console to generate this JSON file that represents the state machine: an image worth more than a million sentences.&lt;/p&gt;
&lt;p&gt;We can easily transform a cooking recipe into a state machine, let&apos;s take for example a cake recipe: we start by preheating the oven, then we get a bowl, then we add all the ingredients (eggs, milk, butter, baking powder, sugar...), mix the ingredients, pour the batter in a baking pan inside the oven, wait for 30 minutes and check if the cake is baked, if not, wait 5 more minutes and check again (keep checking until the cake is baked), once baked remove from the oven. Well, I&apos;m not a cook so this recipe might not be 100% accurate but the importance here is to understand the flow of actions, which would visually be something like this:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 403px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/cfb2e0f05cbd1d27979d6e9797a38bdd/045fd/cake.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 228.48101265822785%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAuCAYAAAAoaDnGAAAACXBIWXMAAAsTAAALEwEAmpwYAAAHRElEQVR42p1Xa2wcVxUOaRLqEGhFcIJQqUAgflSt+kD8ScMjIdRRCPygcqJWbQWhDRKJG0ExcSqohfkDP1JF0JK6CTVNooZaiiIVqFqFym2crL3bjW12197NPuc9O687dx77nF0fzl3bwaZrp86Vzs7MmTt37p7zne87s2bNCgMAPsGOtj3+5ZofOUBI9I7F/lUPgOHb2JFaoSGAAhAz1DPnH1m3ikVgLdq6edvAfKYy+pShvh/VpA+2zc/ZsGjO2lXv9ODBg+u7u3++aZV/b+5Nqq5/33Gc31NK+9Cel2X1GCdI/dPJ6y8Wdf15gj7qusz6bJzHyfJDi59fvGArLpQSjBdA3oegFDSAUgcc1wPdsIDgeaPRgPlRZz/40kOLn7+RzYV4WZb1su6WIFOkpk5dynMcnZlJUkEQaS6Xp2zgorRer5tscZz/zKK4fjT7g9HoelmObgQQOgQh1IGTbk+b5mfOXLiwBc8/GRKEjuPDwzdseHgOCUuGrusPmab5I4eQvY5l7SkIym5mgoImy108L/1wKj7zlEVpF/X93cx8v4Lm7yaOgzH399q2/SjP819ZMwdc+00WkKzhB265CopSBEXVQJQVECUFSqUy1Gp1qNcDKFcqUK3VoFqtQqVSnfOVKwvxPNpakJjmmza6pkQSGNSDfD4PydR1iCemIZPNQS5fgHyhAIIoQWJ6BrK5PPqzgLGFdDrDfHXP93FBd25BxTTvoab5CO50ByH6t9xy+WFN07bzsry9jOeEkG+LoroTE/Bw2XW34/V2nNO6p+O9SqWyA3fXlcvl7v5IPEdGEptiY2NbQUl3picmOi9dCm9+9ezZu/782rn7UqnU596+fLmzZW9f7nz33atbhkZGbm9LAguwwV2edKoB5A3Pdv2SpxZ1TylqPi/JZUlWvGq15uFcb3Z2lrTi5roH28LmBrAJGQrwIkWbAa0EoCoKqEUNCrwAHC8Cse2bA3tx6RSLxe9att1LCD1kEtKDsTwsyvKRQoH/haqqh/G6Bxdhdgj/TS/67m1beiuN7u7+Dd0/6elcLTm0EG9Y1oBBXYHX6RR1nDjiMSZK6nQuz2U4UY5jRuPNZjOOeJz0y2URi2L/XDJH1rUlB0KsoVm8yJUg8OpNsIkNBE1CgLP4MZC7rrcoht7KMdQ088fEds8bxBlEdjmNMTolK8W/cjz/mmZop7CKTvOieLpULg86rvt31TC+w55rW9MrEeyuXd133BLBYgx/ZtnuPwqafQ53cF7RjDcQg8O8IP+rCfBGEDTPl0oVZuc8r/RPRMX3FuegbQxbBFtqBpWgCbpugmERcByPkUDLluJw+Ri23qCb5m8s6iWzqh2yXTfMcRyziCTJkUKBG0dSCGN2w5Vq9SoSwnWM+aNtd3hzkeq+NZHSdWsPis/vRM06itVyTFW1vjwnvJBMZ49Xq/U+3y8fw/pldhTnDUi6/uDHEqkkbQRVFkMUJ920wPV8aP5PoJav5XmmYeK+viXqpvky9SvAm55uu56bzWQdpHYqipKDRGrzvOAFQUDRiihUgPV8oG1SFkYURYqJ06XXX9984sSprSrApwSADlmWNzKKuvjOO1/8y+kz916dmtoyFottHSkUlvKhJElfYwJFmEAR8gNRVPbwsvbI+6PjB9Ict1ezrC5KUZio32Xg/YwgfLWVqP7+jW13pBnGAKvd67of0FINcWcAlhoIkgzYMYCHsWNChKTAaBIcz/sje27o4sU72y+oqQMuxnpCsAPN9kEURMhksoCYm2VHnhErx6NopVoLliqVP7Dn+l96aQFGS8W9oKpfagmUYexA28kECcVnWzKT72oJUb28jaAwuWgailMslvz6VGz6xWuTsRPhRPjzN+0Xkdc2sYAnr135wvE/nbz/7NkLd0VRnKbQNzk5eed7o6P3T8VmTmDzNBCJTHwjFAp1LNnp/8OGiVQFm6R00SXIfRUk2BKC2xVE2XM9zwsaDQfn8qwvQKFisHlmuVqeJ4d5YJNGUKoFmCQdu4gioOK1RGqeXFclUrsQKqzsenSTHEFIPSspyi85Tvg1YvFZfOER3OgRJlQGth5CsXjfTUXqP/Hp3th08m+JVOrkTCrzSmw69Wo6mz+VzuReicWTgxOT8ZPZXOFMoSA8tmxSFpyhROKzV6PhvRMffvjY2Fhk/2Qsti9ybfKJf4+MHg5HJ/ddiUT2j4bC+6ITE4+PR6d2Dr711sa28FngM9ZwEq9cy+megGSqYOwUTIiKyTFQ7RSsYQUbTQXPeezImpiUpz9WUni/EdQw27KiArYiYCDjmBYyj2Fi10pQAWkrKRjPlZOiYJNJiPnbomE8h7vtNQzrV7Kq9omifExSVewUtF5V03px3nNKsfgCcsEDt9Q5PPnkoc23+PUEa1kXwCyRSMx9+Kgf/FSV3ksT/co32TXzL8zp7+9fu+pPM5eMY1zz4NHw6j/N2kGJkNG7G+VrTxjJK5+e869Zlgz+C2tzZXYTcU74AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Cake state machine&quot;
        title=&quot;&quot;
        src=&quot;/static/cfb2e0f05cbd1d27979d6e9797a38bdd/045fd/cake.png&quot;
        srcset=&quot;/static/cfb2e0f05cbd1d27979d6e9797a38bdd/c26ae/cake.png 158w,
/static/cfb2e0f05cbd1d27979d6e9797a38bdd/6bdcf/cake.png 315w,
/static/cfb2e0f05cbd1d27979d6e9797a38bdd/045fd/cake.png 403w&quot;
        sizes=&quot;(max-width: 403px) 100vw, 403px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Every state machine can be executed with an input event, that can be (and most of the time is) relevant to coordinate the flow or introduce data into the workflow. For example, in your cake state machine, we could have expected an input of the &lt;code class=&quot;language-text&quot;&gt;flavor&lt;/code&gt; of the cake. If it&apos;s a chocolate cake, we would add that ingredient to the batter.&lt;/p&gt;
&lt;p&gt;When creating a state machine in AWS you must select what type the workflow will be: &lt;code class=&quot;language-text&quot;&gt;Standard&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;Express&lt;/code&gt;. They have different use cases and are completely different in a way that we can express their difference in a few points:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Execution&lt;/strong&gt;: Standard workflows are &lt;code class=&quot;language-text&quot;&gt;exactly-once&lt;/code&gt;, while express workflows are &lt;code class=&quot;language-text&quot;&gt;at-least-once&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Execution rate&lt;/strong&gt;: Standard workflows have a 2,000 executions per second rate (and 4000 per state transition, i.e. going from one state to another), while express workflows can run 100,000 executions per second (and unlimited transition rate).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Duration&lt;/strong&gt;: Your workflow can run up to one year in the standard mode, while the express is up to five minutes.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Billing&lt;/strong&gt;: Standard workflows are billed based on the number of transitions, while in express machines you pay based on the duration and memory of executions (behind the scenes Express workflows use Lambda - that&apos;s why the billing is like a Lambda!).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Debugging&lt;/strong&gt;: Visual and history execution is available when using standard workflows, while express workflows use CloudWatch.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Express workflows are intended to be used for high-volume, short-duration, low-latency workflows where the &lt;code class=&quot;language-text&quot;&gt;at-least-once&lt;/code&gt; execution strategy is not a problem, such as real-time events and microservices coordination. In every other scenario, you will be using the standard workflow type.&lt;/p&gt;
&lt;h3&gt;States&lt;/h3&gt;
&lt;p&gt;As mentioned, there are two types of states:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;Flow&lt;/code&gt;: There are only seven flow states (at the time that I&apos;m writing this blog post) that are used to manipulate the flow of execution of the state machine. You can, for example, use the &lt;code class=&quot;language-text&quot;&gt;Choice&lt;/code&gt; block to add a conditional block (if-else logic), add a &lt;code class=&quot;language-text&quot;&gt;Parallel&lt;/code&gt; block to execute actions in parallel, or use the &lt;code class=&quot;language-text&quot;&gt;Pass&lt;/code&gt; block to perform data manipulation in the events that triggered the Step Function.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 301px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/dde773bbbf8b41ca438dab55a15634a3/fb933/flow-state.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 144.30379746835442%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAdCAYAAACqhkzFAAAACXBIWXMAAAsTAAALEwEAmpwYAAAD+klEQVR42o1WaVfbSBD0//9V+UiyC2w2wOL4QLYkW9dY9y27tqtlKSwQ3uq9YgbJ6umuri57UdcNiqJEU9eoqxpFWSFJU6RZjjwvUMr/RFFc17Kc772GxmgaLOI4Qdu2eIkqWKYELmc8L3/i5usf+PvHA4ZhAK/L5TLjfD6/Q9/3GnRhIoMkL/F1n+HbLpWHF5jTCcejL2uMUJ6naYY4SZAkKcIw0kzeXgxaSvaLUqLGWYEgKeHHBXJJ/ySBgiBEJvfD0Og+CCLdu+5RD2BVDDyhqsbSF5nwFJ8SFHmGIpNMhAJmN73M4JnwySBpmuuemcbxf2FMMpY8ceimLZxESrkAq9Uat3d3+PP2TgKeZg4/u+aS+UKUFnj0SjwcCxTNgCiKYDsHzfIg2TJjlk3+jkdP17cHzAEpjdDEsAOBH8NI+iwzik4I/QBR4MOXlQF9P4Sn++hKwYiJBi2Zf4i+bVSLpejNsvbY7/cI4wobXzRWTeS3Cmq3Es1OYDNyUQobs8ikEVXTwYobeHmnslmtt7gXDtdCw5dlgbRs5f6Aph0DUnNt26lGu66bS6boVYdGOLwRDd5YsXDYI2R5hOfh6DqSrQ3HPcBxRrgHTygx+hkqgcHIqcpGy+0HmKJDVIwZHj1fwaZ4wtt+L0F3jpb0eZe15Fy7lsokpHGsJzOQkUYZc9LmvN7z+S9Mz8f72mXNsOtRNoTwItn+eHjE3f135Wqa37fzTAzDeUbHGMzQGINMOrsMKvwMSgxSMk/bCW+UiUrIjFMTx2OmrOAkA/FhyZyUXjr1LAHX4ciReziqdHbC3WZjaRDKo7nOr+7fGMQsbJZMDdWqqdHXyBkPYiAKmeKlXCbdUYfEaz/M5b2SOuSkFIIoyRU8heVa1k4Ntrm+OA3A70BnunI4lvOXm+PWTjV9Guy9NIWi/awpH5bM0vKyxk4mZS2u3UvH2JSDmICvhmC0MeSV2qTA/es8/55D+qBMSyJQCpRXllDrykNP4pmjc6dqBAkTyYsZ1PMsbBpnkovFZwkyCbrebLFcrvD09KyiJyUf8fY6IA9Tt6HS0zLDvn6BVWzRn3vYtqNBabQ0D140gv/FIdOvRVP76gVuZesDcki5qGw8X/niPE+GQJMlx3ScdwHZbhOJ7qTsOMlwMolSwO8JBhjn+C3G+SavEyKJoSWz/opCTRNFKzP5+PiPaHGvp3JGCc71tA7DuGeGEyahL0LhsBSemufvqFdPuAhXtuMgCMNrwE6kNOgXGU2WL3Nqph8AH3PInyG2hcrZoRO3oSnYtqtmynk+iAY3W0t5pQ63Lzu5512zHWbn1pI55Ey1lpMr4jr4BLPiB8f1V4njvn33vcJ7/wI17q84IxkDIAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Flow states&quot;
        title=&quot;&quot;
        src=&quot;/static/dde773bbbf8b41ca438dab55a15634a3/fb933/flow-state.png&quot;
        srcset=&quot;/static/dde773bbbf8b41ca438dab55a15634a3/c26ae/flow-state.png 158w,
/static/dde773bbbf8b41ca438dab55a15634a3/fb933/flow-state.png 301w&quot;
        sizes=&quot;(max-width: 301px) 100vw, 301px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;Actions&lt;/code&gt;: Hundreds of API calls to AWS services are available in the action states. Several services offer blocks to perform actions, such as invoking Lambda Functions, manipulating objects in S3, modifying data in the DynamoDB table, configuring Route53 records, using Bedrock models, executing Athena queries, running Glue jobs... The list of available actions is huge! It is important to mention that when you create a state machine you need to define an IAM Role that will be assumed by this state machine to run all the state actions and make sure the permissions are set correctly. Please do not confuse this role with the role used by the execution of some state actions, e.g. imagine you would like to trigger a Lambda using the state machine; for that, you will need to grant &lt;code class=&quot;language-text&quot;&gt;lambda:Invoke&lt;/code&gt; permission to the IAM Role associated with the state machine, but the Lambda will run using it&apos;s configured IAM Role!&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 305px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/b60f39d4f12aaa2f7f421e7dd2cbd682/a3e09/actions-state.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 200.63291139240508%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAoCAYAAAD+MdrbAAAACXBIWXMAAAsTAAALEwEAmpwYAAAFlklEQVR42q1WyY8UZRztP8OzB0/eODkHTUw46MElxpio0RiMC54wnjyMGJaAbCIYCKIzimiiRmQZHJTAgIMwAzO9zNbTPT29d/VW1UtVde31/P2+XgRm6B6MlfxSqe37vvfq/d77ApZlQddbVLooTWsX3zcMQ5wf5gjMzy/i3PkxXPz9EsKROVwYG8f4pT9x5eo1XLg4jsjcvHjR9/2NDei5HlzPh+36oBNc14XneTBNS6yuOxCfB5UYUDM9FM4Mo3D8GchfD0ENjqBQ0SBXq6hWFeTzEsrlCloto0PN2uJnzaYqFhCo6Q5WD74Iafcm1PY8AuP6DiylKohGl5BYTaJcqUKWlYFQLcsmVCYCTVphfN+rSHz4GKTtj0Kd+AyreQXhcAjpdGbD/PUG1E0bqUu/YmV0D9Lf70Yl/BdMl16ghy3dgNrUBJx6vdm3FKUO27aJQ9tCcvtPSLx8GIU3T6D2yxSkhoJctoAKwU2lMihIRfGTmKv1yjBMMSm/E5A1FdEPjiD92l4UX9+PxuhlLKSTCM6EsLgYRaPRFC9uHLLnYP6do5jd9BGWnxyGPDKBXK2K+HJccOg4zkDZtAe02ivUadTk+BjiJ48i9e1RFGduQjUsaKoGlYqlw8V/ul9Vq3KHQ9NH8dTbqB4bgv7N47Bu74OkmMhm0iiR/nL5Amq1ulgFC3694lUy3DaHzRZWj2xBYecQap8PQZs8jNBSEosLC5iZDaJUKm9IOj0ONctD7KttiO56Gsn9T0Ce/BKS3EKpWEQ2lyc51AQUnr1d5ppi/jRN63BIF+mzV7B6+BSyx39EZTqCOrVTo94gKWiksQZxVOuVojSIgrUlyx0dqqzDrceQeXYXSpt3QB+5ipRcRiFXQCabR4V6ukiwJakEqVgiwVv9ISukw/mtR7Dy1iEkn9sJ+eQfiCTjuD11B9PTM5gNhhEKR+gcEh8M5LDlOlj+ZARzLw0j9sqnqPw8AY202SAYDJtbqlZvw+o6y7qdQhITbmPZBq6fl3DmWA4XTpDL3FHRaCqCLx6k+wEP1nXz9YpbT3Com018sW0BB99dwYE3cpg4XUdWSiKTyQv7Yv74Rd/3+nZKVwGBltXE3vdn8PELQQw/H8Xl76pIZKIIBiOIUCTcmppGMBQW+bIhHdqOibOjURwfDmNkRxQzV+lPOi1qO13A5Jce5DJ3c8htKiDfP1MXBreU47jCHLhczp4BxegDSq2G6HIMy+Qu7IFZ0l4svgK91XqotOulXjqTxc2bU5glnsLhOdz4+xauT96gLKkMHNBfN0YZGkWn33EOhsnL77qHgGw7MC2rxxcXR4TnWHCpHJt5NsX3AXZk3jGU6jrYeVicebIslkuxWCaTzSJHbcg9zWviv+lTM2SqJsYiTUzGVMymWp0fyDGqUH5UGriVbuJOViVzdZBIJNq8xuKYmr4tdg9MR4bo6R667SOteJQ/LhSiu+1IJBuVFC7XNaxWVGRkcmndRJlXVyqJVdXrdeHGHFgc+DbB53KJCt8nqnyKCDp3JRZgvjzikEgUZ7dT/BHD45mZQ+aS73U5XFtGm0Mmmk1UVtrZwHY1qCv6yia+khC7Ld5pdXddhYL0nzR4l2w8ccFQWUIM7/4QetB1u7P+nbzdeq0qdXcD/8cR8CwD0oGnYPy2BeV6i+QRxsLiErViDPF4AvMLi7QdSSMUimBpKUq1LOTEG9UguXk6kxHGwHHbHtCxIV/7AVroHHTLEbEpUeKxVGRZFtecy3zNUqrSPTZTbs0cpSJLiyli2YgBBQeGTMpsrunNjfyULn89Dj2TIB/aDOPceyjVdMxSuHNXMLwgBRPDu9vW1puwqapip9uB7ECeOA0teBaG7fX2Ktwh3Occ4P02S3yw+O+F7OpUBtrP/Xtmv18y/fY3POA/uEzF1UpITCUAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Actions states&quot;
        title=&quot;&quot;
        src=&quot;/static/b60f39d4f12aaa2f7f421e7dd2cbd682/a3e09/actions-state.png&quot;
        srcset=&quot;/static/b60f39d4f12aaa2f7f421e7dd2cbd682/c26ae/actions-state.png 158w,
/static/b60f39d4f12aaa2f7f421e7dd2cbd682/a3e09/actions-state.png 305w&quot;
        sizes=&quot;(max-width: 305px) 100vw, 305px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3&gt;Data Flow and JSON Query Languages&lt;/h3&gt;
&lt;p&gt;Data is passed from the previous state to the following state and can be modified, transformed, and filtered during runtime using JSON data languages: &lt;code class=&quot;language-text&quot;&gt;JSONata&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;JSONPath&lt;/code&gt;.&lt;/p&gt;
&lt;h4&gt;JSONPath&lt;/h4&gt;
&lt;p&gt;When using &lt;code class=&quot;language-text&quot;&gt;JSONPath&lt;/code&gt; there are 5 fields that you could pass to the state:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/4de5c3cfe42ed53129cde197a6749c25/ea964/jsonpath.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 80.37974683544303%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAQCAIAAACZeshMAAAACXBIWXMAAAsTAAALEwEAmpwYAAACJElEQVR42qWTa3eaMBjH+/2/yM7Z5ezsxba2p66TtooKQQhySUK4hKsMhG5aFcVFnVu30/bNnkM4kOSX/5P/k5zt/iPOeGvb3fYU/Hu3ijK/79k3vi0sS7hdzw8z26dh3t/uV9gPr5t1XXqhp5j6bRpNflSkaRYvwf7UuxQvBKV7JV7eajcGs0yG78YiSVy/CIPC32w3z6btTMmri9fvOu8/9j5dq1+C3A1TX9aGlqNHuU8y1Gyaw+5+xe8s9jArA8mVgTdGBZLoSI8YcFlHUoY2gWFqhHZ9XxOXWdh9aNqHZjdftYslX67dw2EVwEyDiQZ8mSuLaHJrGOdi70oeiRiPLCmMkwnNewADIxDHBFjxtFqelGeBlZqCIihEEdTuCIkqBdejjmQPVCpDX43TXNSTt+fDDx3lzef+wCqjYnVSnjE3pgCCvcsOcFLHdCygA2jrNHK5I0mWIVaNUSoZ4dhOcLyYzh7+KMNUE3GfvwVdgJ5mR3Z39FVBCkkxztCsKqtqVvOnrlt+Frabg20nWE/hAPHUoOxJZmDSmEq6ZLqmN/Vwho9uH+OIPXL7CGMRxtok092cIoZ64E7DGk0dMuWlWj9b57AM9EQb4sEkhjBWUUxwxCsmG56FYxclaPNI+V94vpiziPXFHiIoL77Nv3PjBio4d9BdmWvLZf3SxVitVowxhDCltCjKzSKMSD8kA2re3Gfa9uWz3f498Pi3fRI6wT8BR7ByA+jrn78AAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;JSONPath Task state&quot;
        title=&quot;&quot;
        src=&quot;/static/4de5c3cfe42ed53129cde197a6749c25/f058b/jsonpath.png&quot;
        srcset=&quot;/static/4de5c3cfe42ed53129cde197a6749c25/c26ae/jsonpath.png 158w,
/static/4de5c3cfe42ed53129cde197a6749c25/6bdcf/jsonpath.png 315w,
/static/4de5c3cfe42ed53129cde197a6749c25/f058b/jsonpath.png 630w,
/static/4de5c3cfe42ed53129cde197a6749c25/40601/jsonpath.png 945w,
/static/4de5c3cfe42ed53129cde197a6749c25/78612/jsonpath.png 1260w,
/static/4de5c3cfe42ed53129cde197a6749c25/ea964/jsonpath.png 1312w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;InputPath&lt;/strong&gt;: Filters the JSON input from the previous task.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Parameters&lt;/strong&gt;: Argument of the state itself. Notice that this is different than the &lt;code class=&quot;language-text&quot;&gt;InputPath&lt;/code&gt;. Think of &lt;code class=&quot;language-text&quot;&gt;Parameters&lt;/code&gt; as the input to the API call (e.g. payload of the &lt;a href=&quot;https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutObject.html&quot;&gt;S3 PutObject&lt;/a&gt; state), while the &lt;code class=&quot;language-text&quot;&gt;InputPath&lt;/code&gt; is all the JSON data available that can or can not be used to create the &lt;code class=&quot;language-text&quot;&gt;Parameters&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ResultSelector&lt;/strong&gt;: Here you can select part of the result of the state that you would like to carry forward.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ResultPath&lt;/strong&gt;: You can combine the initial state input and the &lt;code class=&quot;language-text&quot;&gt;ResultSelector&lt;/code&gt; to build a new JSON and pass forward.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;OutputPath&lt;/strong&gt;: Finally you will build the end JSON, filtering the &lt;code class=&quot;language-text&quot;&gt;ResultPath&lt;/code&gt; JSON data, that will be passed to the next state.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;JSONPath allows the usage of some intrinsic functions to manipulate the JSON data. The functions are very limited but handful and can sometimes substitute the need to develop a Lambda Function only to manipulate the data, such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Array operations&lt;/strong&gt;: Select array item, check array length...&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;String operations&lt;/strong&gt;: Split by delimiter&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Math operations&lt;/strong&gt;: Generate random numbers, sum numbers...&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Generic operations&lt;/strong&gt;: String concat/template&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;and much more... Please check the official &lt;a href=&quot;https://docs.aws.amazon.com/step-functions/latest/dg/intrinsic-functions.html&quot;&gt;documentation&lt;/a&gt; to get to know the complete set of available functions. Due to the limitations of JSONPath and the fact that it&apos;s not the recommended query language anymore, I will prefer to use JSONata (although in the final exercise I will provide the code for JSONata and JSONPath).&lt;/p&gt;
&lt;p&gt;When using JSONPath there are two syntaxes that you need to know:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;For accessing the data using JSON path you will use the &lt;code class=&quot;language-text&quot;&gt;$.&lt;/code&gt; operator to access the data. For accessing the context object (information about the state machine, state, execution, and task) you will use the &lt;code class=&quot;language-text&quot;&gt;$$.&lt;/code&gt; operator.&lt;/li&gt;
&lt;li&gt;For using the intrinsic functions you will use the &lt;code class=&quot;language-text&quot;&gt;States.&amp;lt;FunctionName&gt;(arguments)&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let&apos;s use an example for easy understanding. Let&apos;s suppose the following input for your state:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Felipe&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;occupation&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;title&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;DevOps Engineer&quot;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;hobbies&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;cooking&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;hiking&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We can access the job title with &lt;code class=&quot;language-text&quot;&gt;$.occupation.title&lt;/code&gt; and get the first hobby (cooking) with &lt;code class=&quot;language-text&quot;&gt;State.ArrayGetItem($.hobbies, 0)&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Every JSON key that will access a JSON data using JSONPath or that will use an intrinsic function should end with &lt;code class=&quot;language-text&quot;&gt;.$&lt;/code&gt; so AWS can be aware that it needs to apply JSONPath logic there.&lt;/p&gt;
&lt;p&gt;JSONPath also offers usage of &lt;code class=&quot;language-text&quot;&gt;Variables&lt;/code&gt;, i.e. values that you can store to use later in future states!&lt;/p&gt;
&lt;h4&gt;JSONata&lt;/h4&gt;
&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;JSONata&lt;/code&gt; is the new supported way to query JSON data in Step Functions and it was introduced in the re:Invent 2024. Before that only &lt;code class=&quot;language-text&quot;&gt;JSONPath&lt;/code&gt; was available and used, that&apos;s why you may still see more content using JSONPath out there.&lt;/p&gt;
&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;JSONata&lt;/code&gt; format also simplifies the fields of the States. Instead of five fields to configure, we have only two:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/4de5c3cfe42ed53129cde197a6749c25/ea964/jsonpath.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 80.37974683544303%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAQCAIAAACZeshMAAAACXBIWXMAAAsTAAALEwEAmpwYAAACJElEQVR42qWTa3eaMBjH+/2/yM7Z5ezsxba2p66TtooKQQhySUK4hKsMhG5aFcVFnVu30/bNnkM4kOSX/5P/k5zt/iPOeGvb3fYU/Hu3ijK/79k3vi0sS7hdzw8z26dh3t/uV9gPr5t1XXqhp5j6bRpNflSkaRYvwf7UuxQvBKV7JV7eajcGs0yG78YiSVy/CIPC32w3z6btTMmri9fvOu8/9j5dq1+C3A1TX9aGlqNHuU8y1Gyaw+5+xe8s9jArA8mVgTdGBZLoSI8YcFlHUoY2gWFqhHZ9XxOXWdh9aNqHZjdftYslX67dw2EVwEyDiQZ8mSuLaHJrGOdi70oeiRiPLCmMkwnNewADIxDHBFjxtFqelGeBlZqCIihEEdTuCIkqBdejjmQPVCpDX43TXNSTt+fDDx3lzef+wCqjYnVSnjE3pgCCvcsOcFLHdCygA2jrNHK5I0mWIVaNUSoZ4dhOcLyYzh7+KMNUE3GfvwVdgJ5mR3Z39FVBCkkxztCsKqtqVvOnrlt+Frabg20nWE/hAPHUoOxJZmDSmEq6ZLqmN/Vwho9uH+OIPXL7CGMRxtok092cIoZ64E7DGk0dMuWlWj9b57AM9EQb4sEkhjBWUUxwxCsmG56FYxclaPNI+V94vpiziPXFHiIoL77Nv3PjBio4d9BdmWvLZf3SxVitVowxhDCltCjKzSKMSD8kA2re3Gfa9uWz3f498Pi3fRI6wT8BR7ByA+jrn78AAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;JSONata Task state&quot;
        title=&quot;&quot;
        src=&quot;/static/4de5c3cfe42ed53129cde197a6749c25/f058b/jsonpath.png&quot;
        srcset=&quot;/static/4de5c3cfe42ed53129cde197a6749c25/c26ae/jsonpath.png 158w,
/static/4de5c3cfe42ed53129cde197a6749c25/6bdcf/jsonpath.png 315w,
/static/4de5c3cfe42ed53129cde197a6749c25/f058b/jsonpath.png 630w,
/static/4de5c3cfe42ed53129cde197a6749c25/40601/jsonpath.png 945w,
/static/4de5c3cfe42ed53129cde197a6749c25/78612/jsonpath.png 1260w,
/static/4de5c3cfe42ed53129cde197a6749c25/ea964/jsonpath.png 1312w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Arguments&lt;/strong&gt;: Represents the inputs parameter for task state, it&apos;s equivalent to the &lt;code class=&quot;language-text&quot;&gt;Parameters&lt;/code&gt; of JSONPath.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Outputs&lt;/strong&gt;: Corresponds to the JSON data that will be passed to the following state, it&apos;s similar to the &lt;code class=&quot;language-text&quot;&gt;OutputPath&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Differently from the JSONPath, the JSONata offers more complex and rich query transformations that can be fully checked in their &lt;a href=&quot;https://docs.jsonata.org/overview.html&quot;&gt;documentation&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;There is a single syntax when using JSONata: &lt;code class=&quot;language-text&quot;&gt;{% &amp;lt;expression&gt; %}&lt;/code&gt;. The expression can be any valid JSONata expression or a special and reserved keyword: &lt;code class=&quot;language-text&quot;&gt;states&lt;/code&gt;. With &lt;code class=&quot;language-text&quot;&gt;$states&lt;/code&gt; you can access four data:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;input&lt;/code&gt;: Original input to the state&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;result&lt;/code&gt;: API result&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;errorOutput&lt;/code&gt;: Error Output in a Catch&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;context&lt;/code&gt;: Context object&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Exercise 01: Should I Deploy? (JSONata)&lt;/h3&gt;
&lt;p&gt;Let&apos;s put into action all the theory that was just explained! This will be the first exercise of this blog post! All the Terraform code for deploying this is in the &lt;a href=&quot;https://github.com/felipelaptrin/step-functions/tree/main/iac/modules/should-i-deploy&quot;&gt;GitHub Repository&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you are a DevOps Engineer or even a Software Engineer you probably hear about people saying that you should not deploy on Fridays, the end of the working day, before a holiday... For this exercise we will use &lt;a href=&quot;https://shouldideploy.today/&quot;&gt;Should I Deploy&lt;/a&gt; API to the deployment of the infrastructure via the AWS keys of an IAM User. The idea is to check the response of the API: if we get a red light we will attach &lt;a href=&quot;https://docs.aws.amazon.com/aws-managed-policy/latest/reference/AWSDenyAll.html&quot;&gt;AWS Deny All Policy&lt;/a&gt; to the user, otherwise, we will remove this permission. Imagine that the AWS Access Keys of this IAM User will be used in a CI/CD to deploy Terraform code.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 262px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/52ed1d6d889eaa35b7ff5d8a318eb75e/8ff13/infrastructure.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 32.911392405063296%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAYAAAAIy204AAAACXBIWXMAAAsTAAALEwEAmpwYAAABrklEQVR42m2QTWsTURSG8x/cCy6lLnTpxlqwCu5Ef4H+BTeCCLrQgILQqhGhLnUluBFBMNgmNDEmk0yTSSYfzMRMtJnvNJNo0/l4nExwofTA5b2Xc8/D+56UvvIIc22T0an7TO5+YFFRECbqeR6qqjIYDLBte9kLo0T9tsH+iTuYFzfRTz/EvLBB5IekjDNprPUXWGvPGKc/cYiPPz/Csix6vR6FQoFsNku1WsVxHEI/wPN/o30RsS9ncG++wYrVvvGa6NCPgasbmFcy6Ctp9NvvcOdTXNuhVquRy+UQBIF6vU4+n6dYLOLETk3PRXq/g31tC+fWW4zzT7Guby2BztknjNdfcrD6nOm9j0mcyfgAWZbRNA1FURJdRJckCUPXl5E7BtbJB7hXX2Gfe4x7KQPxqlKmoDD8KvOj1GbU0Zj7Pv9XFEX/vI/iP/vDEVajj7mn4kgD9NZ3JhOPVLnZYrtcptRoUBBqeNNZMhSGYQJanL/3IAiS3mz2i887u+QqAnK/z64osl0qI7U6pOx4X+JeE23481g3xzmdxsBKtU4zBkjNDt8qIt2uSrur8Acmb+xIJtmtBwAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Should I Deploy Infrastructure&quot;
        title=&quot;&quot;
        src=&quot;/static/52ed1d6d889eaa35b7ff5d8a318eb75e/8ff13/infrastructure.png&quot;
        srcset=&quot;/static/52ed1d6d889eaa35b7ff5d8a318eb75e/c26ae/infrastructure.png 158w,
/static/52ed1d6d889eaa35b7ff5d8a318eb75e/8ff13/infrastructure.png 262w&quot;
        sizes=&quot;(max-width: 262px) 100vw, 262px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;The state machine has the following graph:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/978f4df2b4cefedf39c4caf32b6b9825/c1c45/graph.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 60.75949367088608%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAAsTAAALEwEAmpwYAAACZklEQVR42mNgwAPq99ezgOjnj/YkP3u4Ow3E3r9/PwsDuaC+3h6s+dLZLcZnT642AbHTjNNYSTWHEWxYVhaPiZ3nYUMrlzdhIeF3A/1D7wT4Bb728vA54urqyo2sligwMy2N1dDOPUjH0iUxIS4uJT4mPjkuJibR1zcgyNjYmJVkFzK4hPLvcvHcsC8w+ExAZPSJuOi4E1GR0adDQ8M3WllZ8eJ1YX19PVNoaCgzDP8HKYyP51hn4xi53t0nPTI5OTM5LjEzLSUlLTYqNkpe3p4DXQ/QGCawYWADgJLoLpxZn8alauq4WtvK9WxCTOzB8LCIA86Obmcd7FxW+/ikcaG78P///4xQgxkYONUNpaRVDfRllfW1lXSNdfUtbM28vb2lpcztI6QsnDICfQMnBAaGTvDx8k93cfSKtHbxkbOy9zRRN7bQlVHS01HVNtNX1beQhgS+u7fLQk//Pk+fwJV+gSHLvL0DFtnZOS+Wt3PRvO7l53grMjLYIyisPgiIw8OjghwdXZ3c3X21DSysFlk5OC8KCApf7uzitdzGxnmCs7O7C0O/g9viU57+T2JjYi4lJiZeCwsOu+Fi73JG0t3HaJWd256NvoEffMIir8RERl8JCgh+b2/nvDfYL9ZQ09TojL2by42EuPhrbi4elxztnZ8Cg2Q+LAhAfmdhkJHh1AOmsZiYGO7Q0Ho2j1wPdpDkmRMrLEAYxJ44cSJ7aH0oW1paPZerawy3DFCPvb09CzgugOEIjuH/q1aBYwrErodEELO9fT3LNqBmkCEP7+7IfHR3ZzbMQJAcAygC6hmYkGMbpBcAxe3XCMA3oEAAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Should I Deploy Graph&quot;
        title=&quot;&quot;
        src=&quot;/static/978f4df2b4cefedf39c4caf32b6b9825/f058b/graph.png&quot;
        srcset=&quot;/static/978f4df2b4cefedf39c4caf32b6b9825/c26ae/graph.png 158w,
/static/978f4df2b4cefedf39c4caf32b6b9825/6bdcf/graph.png 315w,
/static/978f4df2b4cefedf39c4caf32b6b9825/f058b/graph.png 630w,
/static/978f4df2b4cefedf39c4caf32b6b9825/c1c45/graph.png 824w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Notice that it performs exactly what we discussed above. It is also checking two important cases: if it&apos;s allowed to deploy and the deny policy is already not attached, and if it should not deploy and the policy is already attached. This is needed because the detach user policy action will raise an error if it tries to detach a policy that is not attached to the user!&lt;/p&gt;
&lt;p&gt;For this case, the input of the Step Function is not relevant, it&apos;s just a cron-based trigger. The state machine starts with the &lt;code class=&quot;language-text&quot;&gt;HTTP Endpoint&lt;/code&gt; state with the following configuration:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/396348306e6dc2aa5e1fae040fbf3a8d/b2982/state-http-endpoint.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 95.56962025316456%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAATCAYAAACQjC21AAAACXBIWXMAAAsTAAALEwEAmpwYAAAC60lEQVR42o1U2W7cOBDULwSxx55DF3WTukcazWGPYzuziwUCbILd//+VSjXtGSTAeu2HQrfIVrO7q0in3z5iujthPDxD/LvHvzAdnuAqjetFhNmSWCW4dlN8miusohKm26Hs9lhlPeb9Cf7hB5b1EYsgh+MyoJseiC/Q7RaruIbbPTLgHguzx7J7xkzVCNIazXREkLVYhBrV+gDTH1COD5jHDW68HLdeJgkN8npjT02KDq6eEN3/g+jwHWr3DfHxX4TDCSETGValih5uXCHjP3kzoSBW7GYelphJQtXcIRmekBHF9gSz+5OBW2TlgJQQq/mdliMiJpMkSTUgzFskXMuqDRIzwC13mPkFnHr/BzZPf0Pswhwwi1tcrVJ8Xsa4esVnzvFqFWPGOV7LPN+A7DvteI/d8QSxBVuS9nW7Q1Fv6b+g6u8Q64E/pSQoxY2bcWa/Y8Y1SerEem1b8ZSBIkEhsaTv5RW8rIRPq0xjrZsa2hILVdgkt35uIZUJF4np4RjOR+Ud2rTBl3KDRnecWYewKMl4gVWimcQwmbYIuD6nPC5tsmIZi59UlNIEp6jJKtlVkcZYrrHmKVsho2gRmBqhrhDo0lYW0F8oVhWwOiadv+KKib2E+mw3cFKzRk7W9HZAdz+h2ZHNsoaqasR1g7TvEDe0XYeENqprooFiTGgqixs/o3QKxHkNx08o2qxB1qyRtSOSeoDH9n3C454bi6hbRJy1+LL2K+R/iQ0ImaPTjEds9s88ceIGW6eALUkMvOGwzyyeCfg/SJwz7J9w/PoNSbun+icrUoEIWRSQVi/iPbP5HhxpV1H1Hq+TbZPWwrZbXvx5UHyoSkeulVQUUX8FIcMNDPVoSisXnwyrqrLECISEpCU5XPM1D0p7+xpJdTahsBxTNrOL4l9K/83/QKuXhInuEZGI8118C+/t/1KhvCijfefeQsibJASJ779K6r8gj69zDo7ZujxHZ5ZlzbJtxgvriqN5TzY/AY1eKMhXi0XyAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;HTTP Endpoint State Configuration&quot;
        title=&quot;&quot;
        src=&quot;/static/396348306e6dc2aa5e1fae040fbf3a8d/f058b/state-http-endpoint.png&quot;
        srcset=&quot;/static/396348306e6dc2aa5e1fae040fbf3a8d/c26ae/state-http-endpoint.png 158w,
/static/396348306e6dc2aa5e1fae040fbf3a8d/6bdcf/state-http-endpoint.png 315w,
/static/396348306e6dc2aa5e1fae040fbf3a8d/f058b/state-http-endpoint.png 630w,
/static/396348306e6dc2aa5e1fae040fbf3a8d/b2982/state-http-endpoint.png 729w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://docs.aws.amazon.com/step-functions/latest/dg/call-https-apis.html&quot;&gt;HTTP Endpoint&lt;/a&gt; state requires us to pass an &lt;a href=&quot;https://docs.aws.amazon.com/eventbridge/latest/APIReference/API_Connection.html&quot;&gt;EventBridge Connection&lt;/a&gt; that contains the credentials (we are using a public API that does not need credential but we still need to create this resource).&lt;/p&gt;
&lt;p&gt;We are also specifying the API Endpoint (&lt;code class=&quot;language-text&quot;&gt;https://shouldideploy.today/api&lt;/code&gt;) and the HTTP method (&lt;code class=&quot;language-text&quot;&gt;GET&lt;/code&gt;). The only relevant information from the JSON response of the API is the &lt;code class=&quot;language-text&quot;&gt;shouldideploy&lt;/code&gt; key: a boolean that says if we should deploy or not. Notice how I&apos;m passing this as the following JSON:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;deploy&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;{% $states.result.ResponseBody.shouldideploy %}&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I&apos;m using JSONata to extrat the response of the API (&lt;code class=&quot;language-text&quot;&gt;$states.result&lt;/code&gt;) the desired key. This becomes clear when we check an execution of this State Machine. Let&apos;s check the entire output of this state.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 582px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/f508a59a588bd67c2d2b4007f9f116c8/7c1cd/state-http-endpoint-execution.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 121.51898734177216%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAYCAYAAAD6S912AAAACXBIWXMAAAsTAAALEwEAmpwYAAAD7UlEQVR42oVV6XrbNhDUIzQSbwAESfAWqdOSdTq28zVt2r7/60wHkJWjzfFjvgVIarHYnRlN5usTTu//xPHyin71CNHvoeZ7ZKsLRNEhTkskacX4BnXfl4jsO11xbyBMD1mvMcnqJSLdQogEnREYa+WilhGqPEFrJKPAvFIYm9ShziX3qUOZCcRhCNPOUa3PmBTdxiVUMsGnS4+Ppx77RYnzpkZbahSZhEwiKBEhlTEylbi9XRsmE1wniUCWptBFjYlhQlnM4QUhll3GRJVLeN1W2A4GeaZdYpMlKFlpoW8oWaXn+/A8H77nYTabQvDqE1WOCGSFvs5wYZJ/ngb8ce7xvGu4r90BF+LpocFhVeK4Lt36xGcPQ4FVn7MAw4oj10vXw1DVmDcFNqxoqLXDotHYjQXXKU/3WInn4ozRZ2U2hoGPgPAt/BmEfksosoZ9kLfSpzNieoNLZH8UfAPfv0ffrd2ea5mVmJSDpcgT8uUF+eKEnPt8OKBYXZG1K6TlHKocGAcoc0NafsH9XWrmpFCDSTUeYPYfYbbPiNsd4m4HyYSSiZOMHFM5e1P8BAaRSJHw4LR/fJty3kHGAY4LjYdeoS0kFu2GlEjc1QOfffNmCAjft22YfYadrhQx2n5AtWDCbnmAqhaIohhXTu/lscProcfDssfQVYhIWjsA20vb03dT7613XxDymzgiV+1QrPQU7//b1CffFD4ce1KhcBR6XBo8jBXVod27NSlyWlesKHFTvye0h3kzO2UOpVsdHbHfzXz0lNfLvnX8s9iSZ50xWHU1arbBqiSOQlYU/K9K2wp5r1AUPcIogclTNPxhY1Jq+yatVMXUskRXpk5+05n3nWRfJRw3Z1ehlII9bN2VzqyurQw23eA+tn2bTr1vrvnDhHYoIu8RJzFKHXHC1GkW0wBCXlPc1MCP7/B/iBkTsof1sEO72KMetqj6NW1oxbhBPbemQY/L6UT0xV8ip9p0fevh9cPfONBgd8dnPDw+4XB+xvH6AZpkDUSBiKZqESpDI7khdPvb8yAh+csFFEUxyZoVnbdxVJhXwl1ZxqG7/rySjuQNn3WMI43CYt1nNA2FnlRy+1ahaUukFImzL5uwb3LXO03jjKLATTRTNFJrrCLkPnQuXqSxg13fUegEKvYgKNNJ3lgDGB0pg1ghTFJEDhqhyAhNrWoXQz4LklsMxVdIbt9IutZk3J5xff0L55dPDgWdJ1u9R777Hfn62bmIPfBncC5E+SbFYP9T1sibJQwV0dDqc5I7kQpC8mTq879e+D1EVI6uexj+Uzq3sSdonWHRFRjbAlqlMCSpNYxfIQzZR52jaVqU7Yh/AS4Vma6p6rkyAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Execution Output of the HTTP Endpoint State&quot;
        title=&quot;&quot;
        src=&quot;/static/f508a59a588bd67c2d2b4007f9f116c8/7c1cd/state-http-endpoint-execution.png&quot;
        srcset=&quot;/static/f508a59a588bd67c2d2b4007f9f116c8/c26ae/state-http-endpoint-execution.png 158w,
/static/f508a59a588bd67c2d2b4007f9f116c8/6bdcf/state-http-endpoint-execution.png 315w,
/static/f508a59a588bd67c2d2b4007f9f116c8/7c1cd/state-http-endpoint-execution.png 582w&quot;
        sizes=&quot;(max-width: 582px) 100vw, 582px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;I&apos;m also exporting the &lt;code class=&quot;language-text&quot;&gt;deploy&lt;/code&gt; as a variable since I plan to use this in the future states (choice).&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/4264d29a40dddb2be984576703cfcbe9/be86f/state-http-endpoint-variables.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 72.15189873417721%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAOCAYAAAAvxDzwAAAACXBIWXMAAAsTAAALEwEAmpwYAAACRElEQVR42n2T6XbaMBCFeYSSpjYY4w1bNl5kGwM2YSehJKecdPnR93+T2xk5JCkn7Y/veCxp7oyupE4+XeNu94jtwxn16ojN/TdFtThg4CXQrRB9O4I2FLACiaxao2x28OIpzOIAZ/kT1vQEU5Qw3DE6nKANA5XwxQzQo2SG41cG7bxm+tAMT6HTuO5QQS+H5iZqHWt1eGG12GO5e0Kz+QpbSLhRgcX2hPnqAVWzV+NhOoMe1bA3vxWam2Esa0zqLZr1EbPlvRLtmLSt0XgCP67gRSUckSs4ZtywoG+BEVNsEMyPEM0j/MmWCpcIqBCvN5yx2kUnKe/IszPWxHzVVuKK7OPh9EN1J7fPkPtfyHbfka7PiKizWM4hsjk43ybBz8aoFYzyGsv9Eya07ZqEWMyIZrgdhuSdj1vihjzsGgF9haLbH+FTz0G359LXxU3faz1mQd6SrJaIiwZR3iCbLCFkA49sYCuCZEp2cFxCpFNlDY+H1B2vYbhjtk55aMsVxOpZ4SQ1POUZ+9cKMhcvX2P2jgq5LzEXMUcpCVKHFt0fP2sw8HP06dh71HbPDl/RLQHdpjGn/e87b3NqjHJ09T9W160T0HYS8tGmlhM/g++lGIoUXkbXJ5Vw4lThFzl8KREUBdwkw0jmFOcUJ7CSEj2fsF48HJMfl2O/Rh+2XWrqAYi243exZr2tVR26YY4oqShR/P06ruDT/t/85ZW1gtlMvVvDjZW5H8Hv+F9zF1hDbZmPneFbz4nX8MXlE/5o7j0s+AcYfZoi2VT6zwAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;HTTP Endpoint State Variables&quot;
        title=&quot;&quot;
        src=&quot;/static/4264d29a40dddb2be984576703cfcbe9/f058b/state-http-endpoint-variables.png&quot;
        srcset=&quot;/static/4264d29a40dddb2be984576703cfcbe9/c26ae/state-http-endpoint-variables.png 158w,
/static/4264d29a40dddb2be984576703cfcbe9/6bdcf/state-http-endpoint-variables.png 315w,
/static/4264d29a40dddb2be984576703cfcbe9/f058b/state-http-endpoint-variables.png 630w,
/static/4264d29a40dddb2be984576703cfcbe9/be86f/state-http-endpoint-variables.png 662w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;By default, the entire task output is passed to the following state but I wanted to pass only the &lt;code class=&quot;language-text&quot;&gt;ResponseBody.shouldideploy&lt;/code&gt; value.&lt;/p&gt;
&lt;p&gt;Moving to the next state: ListAttachedUserPolicies. We will check all the IAM policies attached to this user.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/c1d585df6bb8f1a179c1bb1bca1a689b/a0209/list-attached-user-policies.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 94.9367088607595%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAATCAYAAACQjC21AAAACXBIWXMAAAsTAAALEwEAmpwYAAACzUlEQVR42oWU2XraQAyF/Q4hbF7xOt4XDBhIIWnzEdLlIjd9/zc5leRAQhrai2PN2DOyRvo1WrXcY3v/jPXuSWz35YDtwzP8tMWN7mNkRRiaoejWCKC7CbJ6g5r2uckCxuIZzv43zPkB01kCjR+6m8pCthNHYerEmNgRRuRkZPZ2bKuz+CcsmdPasZNgZMfyTuNH092THpDWawzpo94eodOf9eYAs/uFcbKB5WdoN98Q5UvZmM/vUC13tG9PkcUYTF05hcYL/biGylcIkgZBtYXa/kS0+Y5wfST7A4qsny4QpHNx6MUNPF5LaVHFCp6qYagWo1kKzWvuoWhTTJuCjrR4xEw1cPwUtpfBCTLMwgJOWJIqceRSAHZQ0Dqez+GSNcnh0CGHdfcV+6cXrHZH2PkWRlhDJ0cXuX2dD6hIf8sTe0t2aAbQomKJsr1DXHYIiw6qXCGp1pKCiMQ2q7d97qhAY0tdFOijtIBC5hxYVIyYInIpkqmnYMd05CQVa4SxjN2sgOEnhE94Rum9xGFIiQ0p2To5sggB3WYMCB2XxiwvpuMqsayxHX3q7OzQowRzlQcE7UkM8EDvdfuqwdTv58Z1iUOfEAipcidQ36CNBPKJ3csQ6OPz/DNJp1h+LkhwLn06epAthC/Ggd/bjEtUSSv26FwX+9K63QGPxxd45Ra2IidRKXxZxNn7/AgaRvBfaYxJvdojIlQiQifIFxIl48RdoAillC4DZpI3DAkdqbJUOhKd0iS9zG3k0/EsN4NNreOQbAaawfYSGB5hQ13DYxFV3gpTWFEmc+7jE4OcYy3KemzSqkZVNSjLBkXRIGkbpN0cqq0RL3jcIlnRz9s1paeGlxcww0SQusDG5yanCMd8Y1DrDKxQNLRJVngBcZ+nCDfTADeTN4wuHAaMDUV4vt+u6P33E04f1wg2XFWXW4+qek2MjuT5H2tYpp/jD8MsItXHRcz3AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;ListAttachedUserPolicies State Configuration&quot;
        title=&quot;&quot;
        src=&quot;/static/c1d585df6bb8f1a179c1bb1bca1a689b/f058b/list-attached-user-policies.png&quot;
        srcset=&quot;/static/c1d585df6bb8f1a179c1bb1bca1a689b/c26ae/list-attached-user-policies.png 158w,
/static/c1d585df6bb8f1a179c1bb1bca1a689b/6bdcf/list-attached-user-policies.png 315w,
/static/c1d585df6bb8f1a179c1bb1bca1a689b/f058b/list-attached-user-policies.png 630w,
/static/c1d585df6bb8f1a179c1bb1bca1a689b/a0209/list-attached-user-policies.png 725w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://docs.aws.amazon.com/IAM/latest/APIReference/API_ListAttachedUserPolicies.html#API_ListAttachedUserPolicies_ResponseElements&quot;&gt;ListAttachedUserPolicies&lt;/a&gt; API call returns a JSON that the only relevant field is &lt;code class=&quot;language-text&quot;&gt;AttachedPolicies&lt;/code&gt;: an array of objects, where each object is composed of &lt;code class=&quot;language-text&quot;&gt;PolicyArn&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;PolicyName&lt;/code&gt;. JSONata allows us to extract values from a key of an array of objects and return this as a list of values. Then we can use the &lt;a href=&quot;https://docs.jsonata.org/comparison-operators#in-inclusion&quot;&gt;in&lt;/a&gt; comparison to check if a given value is in that list:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;policyAttached&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;{% &apos;arn:aws:iam::aws:policy/AWSDenyAll&apos; in $states.result.AttachedPolicies.PolicyArn %}&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Notice how &lt;code class=&quot;language-text&quot;&gt;$states.result.AttachedPolicies.PolicyArn&lt;/code&gt; is powerful! This would be a pain to do using JSONPath, since the JSONPath in Step Functions &lt;a href=&quot;https://docs.aws.amazon.com/step-functions/latest/dg/amazon-states-language-paths.html&quot;&gt;do not allow some operators&lt;/a&gt; that could help us in this case. Check the state input/output during execution:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/c1d585df6bb8f1a179c1bb1bca1a689b/a0209/list-attached-user-policies.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 94.9367088607595%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAATCAYAAACQjC21AAAACXBIWXMAAAsTAAALEwEAmpwYAAACzUlEQVR42oWU2XraQAyF/Q4hbF7xOt4XDBhIIWnzEdLlIjd9/zc5leRAQhrai2PN2DOyRvo1WrXcY3v/jPXuSWz35YDtwzP8tMWN7mNkRRiaoejWCKC7CbJ6g5r2uckCxuIZzv43zPkB01kCjR+6m8pCthNHYerEmNgRRuRkZPZ2bKuz+CcsmdPasZNgZMfyTuNH092THpDWawzpo94eodOf9eYAs/uFcbKB5WdoN98Q5UvZmM/vUC13tG9PkcUYTF05hcYL/biGylcIkgZBtYXa/kS0+Y5wfST7A4qsny4QpHNx6MUNPF5LaVHFCp6qYagWo1kKzWvuoWhTTJuCjrR4xEw1cPwUtpfBCTLMwgJOWJIqceRSAHZQ0Dqez+GSNcnh0CGHdfcV+6cXrHZH2PkWRlhDJ0cXuX2dD6hIf8sTe0t2aAbQomKJsr1DXHYIiw6qXCGp1pKCiMQ2q7d97qhAY0tdFOijtIBC5hxYVIyYInIpkqmnYMd05CQVa4SxjN2sgOEnhE94Rum9xGFIiQ0p2To5sggB3WYMCB2XxiwvpuMqsayxHX3q7OzQowRzlQcE7UkM8EDvdfuqwdTv58Z1iUOfEAipcidQ36CNBPKJ3csQ6OPz/DNJp1h+LkhwLn06epAthC/Ggd/bjEtUSSv26FwX+9K63QGPxxd45Ra2IidRKXxZxNn7/AgaRvBfaYxJvdojIlQiQifIFxIl48RdoAillC4DZpI3DAkdqbJUOhKd0iS9zG3k0/EsN4NNreOQbAaawfYSGB5hQ13DYxFV3gpTWFEmc+7jE4OcYy3KemzSqkZVNSjLBkXRIGkbpN0cqq0RL3jcIlnRz9s1paeGlxcww0SQusDG5yanCMd8Y1DrDKxQNLRJVngBcZ+nCDfTADeTN4wuHAaMDUV4vt+u6P33E04f1wg2XFWXW4+qek2MjuT5H2tYpp/jD8MsItXHRcz3AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;ListAttachedUserPolicies Output Execution&quot;
        title=&quot;&quot;
        src=&quot;/static/c1d585df6bb8f1a179c1bb1bca1a689b/f058b/list-attached-user-policies.png&quot;
        srcset=&quot;/static/c1d585df6bb8f1a179c1bb1bca1a689b/c26ae/list-attached-user-policies.png 158w,
/static/c1d585df6bb8f1a179c1bb1bca1a689b/6bdcf/list-attached-user-policies.png 315w,
/static/c1d585df6bb8f1a179c1bb1bca1a689b/f058b/list-attached-user-policies.png 630w,
/static/c1d585df6bb8f1a179c1bb1bca1a689b/a0209/list-attached-user-policies.png 725w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Now, with the two relevant pieces of information in our hands (&lt;code class=&quot;language-text&quot;&gt;deploy&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;policyAttached&lt;/code&gt;) we can define what action will be executed:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If we can not deploy and the policy is not attached -&gt; We would like to attach the deny policy&lt;/li&gt;
&lt;li&gt;If we can deploy and the policy is attached -&gt; We would like to detach the deny policy&lt;/li&gt;
&lt;li&gt;If we can deploy and policy is not attached OR if we can not deploy and policy is attached -&gt; We are already done and nothing should be done&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/dfbf11f5e38704126aa5b788ce04902e/be86f/choice.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 87.34177215189874%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAARCAYAAADdRIy+AAAACXBIWXMAAAsTAAALEwEAmpwYAAACSUlEQVR42pVU227aQBD1F7QSAQwGY7CxwfYa3w02EC5NSZWWKOpDpf7/h5zOLJAQqKLwcDTr8e6ZM5ddJZ6usHrYI58/YLN7Rrl6RMsYQ+2N3qGpOzDsACKZIyruYXoZOtEGRvUCPduhY0do0zmlRRtbHQtNzSRL0O0rsktiBq9bA4GWFREmqHeGcPwESpOcevUbxvov9Ps/UL0KDSJXmZgPXuKcvGtLsP+ubSGIp1AGYgZ/8Qve/CfEco/J8gnGKIba9z9Ueg4u0Z02hIgKKFG+xPfHZ4RphTApUVRrmH6OphlQavZrih+BSWukUMREqA8DmG5CUVx8aRj4qvZR16im3eGnyN4RskKfurbe7ZFVW1heCn0ooA28m9AxD+WZpDMoWt/FaDKViIoVWDFvuAVdS0ClOkpCP66w2D7BorRrbfPTjbhuyjHloZ/JYeVa8Cw1aAxuBZ9lMYLHxhY54ukaabnFYBzLmWpTGTjqOdr98fEGja/+MWqk0JcKqRHJbI0gncMmtaYby/Qt7wha856TtUUGJ8jhsJXIpY9rKcemS11Nyw3G1BRuCKs0Cf1RJMHfhhPKNRdfpqnbb/Z4VQ36H2blYQ65u140o4MTDEYhkTBZKIlZJatmsI9JebR0sl3Ll5DfxBOmRMjRq9UP5NU3Ii0xyZaU/kI2yo9LSjOT6XIQJh8FBQWNpO+tvi4FTw6EzMy1O4G7fl43VqJbwVFZcIB1Uihe/WwlYY/euLBYIqJ3kdXW6cqdnim15/y3o5fQBq6sNRP+A3WB6QaOWiTvAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Choice State&quot;
        title=&quot;&quot;
        src=&quot;/static/dfbf11f5e38704126aa5b788ce04902e/f058b/choice.png&quot;
        srcset=&quot;/static/dfbf11f5e38704126aa5b788ce04902e/c26ae/choice.png 158w,
/static/dfbf11f5e38704126aa5b788ce04902e/6bdcf/choice.png 315w,
/static/dfbf11f5e38704126aa5b788ce04902e/f058b/choice.png 630w,
/static/dfbf11f5e38704126aa5b788ce04902e/be86f/choice.png 662w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Notice how we are using the variable previously created (&lt;code class=&quot;language-text&quot;&gt;$deploy&lt;/code&gt;) and also using the ListAttachedUserPolicies output which is now the input of our choice state (&lt;code class=&quot;language-text&quot;&gt;$states.input.policyAttached&lt;/code&gt;). I also could have exported the &lt;code class=&quot;language-text&quot;&gt;policyAttached&lt;/code&gt; as a variable and used it in the choice state!&lt;/p&gt;
&lt;p&gt;Both &lt;a href=&quot;https://docs.aws.amazon.com/IAM/latest/APIReference/API_AttachUserPolicy.html&quot;&gt;AttachUserPolicy&lt;/a&gt; and &lt;a href=&quot;https://docs.aws.amazon.com/IAM/latest/APIReference/API_DetachUserPolicy.html&quot;&gt;DetachUserPolicy&lt;/a&gt; accept the exact same input: the ARN of the policy and the IAM User name.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/b60391e423632c5aaf8ecbf09e7421e5/be86f/detach-attach-user-policy.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 108.86075949367088%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAWCAYAAADAQbwGAAAACXBIWXMAAAsTAAALEwEAmpwYAAADFElEQVR42o1V2W7bMBDUPzQ+JVmy7tux5duOHTRGgxYJkocG7f//yHSWlJ04sIM+DLgkxeHuznJlzNbfsXt4wmr3iN3hCcvdD/TcFG07QmcQnyBz08tR1VuMF/fI6w3MYgtn/Qpn84p+toQ5zGDYQQknGsHyC3VAcCKyAiJExw7V2CVpz0lOF3S9Er14hk40RcfJ0HcTGC1+KJtxOUc2XiEdLWEHBTouyfONurmfzGmv0Q0nJIyR3q6Qj9ckjjCMSqTVTJ3pMzJDhxYhKmcoJmtkt0sMZLO6h7v/C+fuN+zVC8c32Mtn9L0CSTVXhF1668S3iKsFI8uU90ZYzJBPd0gnW2T1DhnHYrZHtXxAUd8hpzcF81ZNt8i5p73TkSQkkshSOiH7w3gEIxmtEMwO8OePcOqDSrQ12sOsduhFNdrMY7vJmUAEkzz3KYB4KHOxNRjyfHPAr5c/uKfC2XSPYbGAF1cUqlI3ugxJRPOzCee3ilTy3rIbWBo3ZqDya/jpROXOz2oEDD9gCDFzFGZThLmAa7TTaqXsthWjZUY45v69rGKtckDCkLebdN1hCK6EwA/6fgIrTGEFDJEQ2ySGWQmvYARJSYF02N2mlCzOjbRcIKHCHutwTY+29CJkyOmcni+nKFYa2bxGuZ5htKXCXB/EObrue24lbFFaeZiSsEvPvjEPN01OWqbkRY96Hmlb1vt6r93U8Bmhn7CO8vq0cQ2tD4fbg8v7itBLxogohiiowPnJbiDfiECf1z/DZWUYXjpWBSoFLkortXlYIO9bvVkm/Vh319AZJPrpzbcH/Hx+Q1rvYSVTpZTtlyTLFeGxaKWYTzV4AWd1KO8yHS34zJaqQSTKXqknJXZGu+TzkwukPL700GfIIop8/BHS2yzvOOq2ZjeEl6D7pVJ5rMpGSNxGACesFNHx2XmNUHaT02tVcFY2covU4A2bgX6rl/P1X4SJqsPwasI1gi/3RRT1CxiSMCThgL8C+wzFaZTOI+U0CPWezC+jFMKxapKqWbLLnBon7eO6rAnkAci6kB7/Px8hzeEf5z56xevxQlAAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Detach and Attach User Policy&quot;
        title=&quot;&quot;
        src=&quot;/static/b60391e423632c5aaf8ecbf09e7421e5/f058b/detach-attach-user-policy.png&quot;
        srcset=&quot;/static/b60391e423632c5aaf8ecbf09e7421e5/c26ae/detach-attach-user-policy.png 158w,
/static/b60391e423632c5aaf8ecbf09e7421e5/6bdcf/detach-attach-user-policy.png 315w,
/static/b60391e423632c5aaf8ecbf09e7421e5/f058b/detach-attach-user-policy.png 630w,
/static/b60391e423632c5aaf8ecbf09e7421e5/be86f/detach-attach-user-policy.png 662w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Here is a final workflow execution in a case where the IAM User was blocked from deploying but now it&apos;s allowed to deploy (i.e. the state machine should detach the deny policy):&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 561px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/4773e2ec8bf0a19232a31adabec545f5/410f3/execution.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 79.74683544303798%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAQCAYAAAAWGF8bAAAACXBIWXMAAAsTAAALEwEAmpwYAAACdElEQVR42p2U22rUUBSGe+mNiO1kJufJJNnZO8mc7Inp2WmntZlOO7QoikVEEPGigrWtpdQHEKQqKIj0wifw0rfxzvfwd+09WiitdezFzyYrK99e61+LDOSdCBep4Ea4Vgjw9GETXz/dQ224iutGqOLn5Q/8Cyg1ZDJ8+3If+LGLR5tzuDJYglHklwDafyoM0WqO4+BZBicQyFm9d0p9A+kjd1PAP0jg78Swt2MUtmIUtzn8PYrtJzAaHJpOuW6fwOIDgeBVjNI2nfsxQpL3nIAvYwSHMYyJ/wFSO7rgMOoCeo3DHkkQTNShV3/H6Mz7Z9v+O5BuDQ4TsA9U2due2LuewqMY/DiF3aYKNcot9jkUOxNw1gXsFQ53jVpfL8PqULUk966AMRJBM/ptmZTLMZhTBFgVKHYShBs1WBmHsyaQZwxagZ2CnQFqNlOn4ZHhHlUWpDSIMthHavU9AY8EmGz3cwo3i6GbpBI/+U4B5UPOCkghrFICy08VyAnLML0EekpDGOWwxmjCE1XoN+jCYQ4zSmgnKypX6gQ4MjqFpaUVtFpttNtdLC6uYGZ6HkFURc4J4T0m73ZpTXZT8P0qPLlCNCyzSZZoIcYbM2hnXcw3b6FcHSXg2DQyCiwSVJ7LpNnZBfisQkAG7wkB92g4tH/6VgTvBS077aa5wDFEwMbkHDqdDSzMLxNwDAOy1UHDVyq4XPkn23bCCswSeenG0CyOWr2O292bKDi0g65QdrhRReVKv6Vt0r5zh6IXhZL0xovk3yXG8Zs7+Pl9B0utSWiO9C9VObKIU0O5aG16iQxX8wFWswZeH7ThMYFBM1Tx8/42vwAjt9wb9Jm31wAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Full state machine execution&quot;
        title=&quot;&quot;
        src=&quot;/static/4773e2ec8bf0a19232a31adabec545f5/410f3/execution.png&quot;
        srcset=&quot;/static/4773e2ec8bf0a19232a31adabec545f5/c26ae/execution.png 158w,
/static/4773e2ec8bf0a19232a31adabec545f5/6bdcf/execution.png 315w,
/static/4773e2ec8bf0a19232a31adabec545f5/410f3/execution.png 561w&quot;
        sizes=&quot;(max-width: 561px) 100vw, 561px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3&gt;Sync jobs, callback, and pooling pattern&lt;/h3&gt;
&lt;p&gt;By default, Step Functions execute states and move to the following state when it gets a response. Sometimes this behavior is not desired, such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You would like to wait for an execution of an ECS Task or Batch Job&lt;/li&gt;
&lt;li&gt;You would like to treat some asynchronous jobs as synchronous&lt;/li&gt;
&lt;li&gt;You would like to continue the execution only after a manual approval/action&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Sync Jobs&lt;/h4&gt;
&lt;p&gt;Some job execution in AWS is asynchronous, i.e. the API to start the job (e.g. Glue &lt;a href=&quot;https://docs.aws.amazon.com/glue/latest/webapi/API_StartJobRun.html&quot;&gt;StartJobRun&lt;/a&gt;, Batch &lt;a href=&quot;https://docs.aws.amazon.com/batch/latest/APIReference/API_SubmitJob.html&quot;&gt;SubmitJob&lt;/a&gt;, ECS &lt;a href=&quot;https://docs.aws.amazon.com/AmazonECS/latest/APIReference/API_RunTask.html&quot;&gt;RunTask&lt;/a&gt;) and return a successful response, which means that the action to initiate the job was successful, but does not monitor the job itself (i.e. if the job execution will fail or succeed).&lt;/p&gt;
&lt;p&gt;When adding these types of states in the state machine, you can optionally mark these jobs as synchronous and AWS will internally use pooling (EventBridge events and polls APIs) to monitor the job status. This is as simple as marking a checkbox in the Step Functions Console or as simple as adding &lt;code class=&quot;language-text&quot;&gt;.sync&lt;/code&gt; at the end of the resource name (note that not all Jobs accept this - please check the &lt;a href=&quot;https://docs.aws.amazon.com/step-functions/latest/dg/integrate-services.html&quot;&gt;documentation&lt;/a&gt;) in the JSON definition, e.g.:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token property&quot;&gt;&quot;Glue StartJobRun&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;Type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Task&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;Resource&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;arn:aws:states:::glue:startJobRun.sync&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;Arguments&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;JobName&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;myJobName&quot;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;Next&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Batch SubmitJob&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You could consider the Lambda &lt;a href=&quot;https://docs.aws.amazon.com/pt_br/lambda/latest/api/API_Invoke.html&quot;&gt;Invoke&lt;/a&gt; as a job, but in reality, AWS treats this synchronous by default, so if you run the &lt;code class=&quot;language-text&quot;&gt;aws lambda invoke&lt;/code&gt; command, the command will be pending until the Lambda finishes (success or error). So this is the reason why Lambdas are not considered as an async job.&lt;/p&gt;
&lt;p&gt;This feature is only available for Standard Step Functions.&lt;/p&gt;
&lt;h4&gt;Task Token Callback&lt;/h4&gt;
&lt;p&gt;Some states allow you to pause the execution of the workflow and wait for a token to be returned (up to 1 year!). When this feature is enabled, the state can pass a token (available in the context JSON), e.g. if it&apos;s an SQS message it can pass this token in the message, if it&apos;s an SQS topic it can pass in the message of the topic, if it&apos;s a Lambda invoke state it can pass the Lambda in the Payload.&lt;/p&gt;
&lt;p&gt;The step function execution will wait until it receives a &lt;code class=&quot;language-text&quot;&gt;SendTaskSuccess&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;SendTaskFailure&lt;/code&gt; API action (via AWS CLI or SDK) passing the token as input. This becomes very handy when you need manual approval or wait for external events!&lt;/p&gt;
&lt;p&gt;This feature is only available for Standard Step Functions.&lt;/p&gt;
&lt;h4&gt;Pooling Pattern&lt;/h4&gt;
&lt;p&gt;Some services can not be turned into sync jobs (e.g. AWS Transcribe) and task token callback can be overkill or unnecessary for your use case, so a common pattern is to create a pooling system using wait state, choice state, and an AWS API state to check the status of a job.
The pooling pattern uses &lt;code class=&quot;language-text&quot;&gt;Choice&lt;/code&gt;, a &lt;code class=&quot;language-text&quot;&gt;Wait&lt;/code&gt;, and an API Action to check the status of the job:
Wait X seconds
Check status using the API call
If the status is ready, move to another state to deal with a success response. If it&apos;s not ready, go to Wait step again. If the status is a failure, move to another state to deal with the failure.&lt;/p&gt;
&lt;h3&gt;Exercise 02: Manual approval for creating a new IAM User&lt;/h3&gt;
&lt;p&gt;This is the second exercise of this blog post! All the Terraform code for deploying this is in the &lt;a href=&quot;https://github.com/felipelaptrin/step-functions/tree/main/iac/modules/create-new-user&quot;&gt;GitHub Repository&lt;/a&gt;. From now on, I won&apos;t comment on every detail about the workflow, I highly encourage you to deploy the code in your AWS account and check, test, and explore. If I explain in deep detail every exercise this post will be tedious and extremely long (more than it already is).&lt;/p&gt;
&lt;p&gt;Imagine a company that is constantly growing and would like to create AWS users (IAM users) fast but only if the manager approves it. Also, the manager can select if that person will be assigned with admin permissions or developer permission. Here is the proposed Step Function diagram:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 264px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/e3271128b18cc1a308af1c01fea9f75a/e61aa/graph.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 151.26582278481013%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAeCAYAAAAsEj5rAAAACXBIWXMAAAsTAAALEwEAmpwYAAAE5ElEQVR42qWWTWwTRxSADW3pD3BB0CuqmiYkqtqgpCS1naz31971emM7Hrwbr+Psxk4chxBIALWXugcEjgSnHnootAckpEZIlUpRRUgh/Ii0qtoeEMUgakRrkTZCBcLfRt3kdcYhNEGp6sJIn+fte+PnmX1v3rPDUca4eWO8+uaNsWrHs4zh4eHnyDxRPKU+mBqfnb7/3ewklhfa/tcAgOVknrpz4fDln49C/uKXcG/qwuGFtqdyOFEcDTy8M36fMHF9LFCWQ4qinj9FZR+TxRBdXV3dC8Q+Onr4LQKRiY7YlqLs3eqDgysJZS2mEomXWvwtniObuuhzdT30T87t9BGmhxYkiaZpkeF50aVENEMObTY8Ht4VCIQYSVLoJ/FjHwnsyyFG45USL8Pp+gz8Ie6Gu6F9cIYdgHBEBbMjBahVhTatHYxEEhDWMbQAAi8Bz4mLEHg/BIPRSkc0maxUBMUabdpq/d5/wLqVOWCNePotVdWsLb0Zq12PWx3tiel0d/e0aRiWl5esZjdjeZrZRdAe3io5VGKxqoCgwEi1Cb/RWbgV3gcnGjLQnkjA4PZ+SBomJDtN2Dm4DXYM9EO4pRUYCu+S9QHP/IPASaCgWJVDQGiNJMh7Pn0dDR1/I5E7W53KHao3ck00n3M76ZynmdtL08L+piZuv9vJ7GVZMef1+hchCNKQzyfvQahzTVmBy2azrxDKzYplCKEVWYIDU0Pk7IqampoVFRUVL5IFWqJrQxhDZKIj65eC+HJI4bb1Xla8OlLvulag2MKkIBZO+vwFSQ4X2rR4IdQS+aW1VS1GVb2IUBuWowX8vgos7SuwzGOu8Zx0NYx9OZCZrpLxCz7r8cKtdB88TJhwDr/4YDACiXYDgkoYYlp8tiNuzGqqDm0YjvECjizQFPcYFqeTglCVA6X7qxTBD2MNFPyZ2gL3ozqc9sqg6zHYtWMbfPjB+zjaW+G9XQPQ15sGt5MqfflJeLypUpRLR6b5S5+vr7n8feXG/MXaxvwX77jznCDlZSmQbwkE8axcUQLBK378zDG+PMsIi8DJfpljfZdKR8alY1kmk1llKspq0+mcQzFXm+YcFEKrjL6+dYbRt07X9ZWmuXP1UmQy2VXE139mAPkYyGbXDgxk1y7U/euYT+yv6l1DFze5c9dZb+64KOdEfzAXCqGhgD+0OxzRDobCmw8SORhEQ4ocyYmikvPy/hI46kM+4VFiK6ZZFcAX+0d8nf6KmwCaDj+EEYRxUUia3aDhIJEikcA2A896rAPLndBpdIGKbSztLV077BAQCQpKp7FDaeYbN2dPbttl3+5I2SdFxcbFwcbRtdNdSXtLJm339nTbfb09+Dll93SnbG2zalNNjI3TpwQuDjOKgtOGlC8f/pVjFW/DVScLN5wMHH/XA8FQBLpSSYi1xSCV7ARN1fDuYhDXdejGejWqlXKR5CRJGxzlufLViNDLstzKflTxJj/86mv8sQ21/Ccuhnc1C3xtnYtvbKQ4p5NTOM6v1GO5oYHha2tdJXsgFOLFwBxySytbKrDljEFc/gfLbQGPqsnyrMOxGKyb77+fHfrYQ5jvyaX1S1B2G7Xujt+bvvvtPSI/c1+ews29cOVr+PXayLM1+vnjTk6cUR/cOT/z4Pb5GSI/9V+RhaNYPLFxsji2sZzFfwMaQcN8TP6bLgAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Graph of the Create New User Step Function&quot;
        title=&quot;&quot;
        src=&quot;/static/e3271128b18cc1a308af1c01fea9f75a/e61aa/graph.png&quot;
        srcset=&quot;/static/e3271128b18cc1a308af1c01fea9f75a/c26ae/graph.png 158w,
/static/e3271128b18cc1a308af1c01fea9f75a/e61aa/graph.png 264w&quot;
        sizes=&quot;(max-width: 264px) 100vw, 264px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;When the Step Function gets triggered with the &lt;code class=&quot;language-text&quot;&gt;username&lt;/code&gt; (string) to create an email is forwarded (via SNS topic) to the manager.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/d7925ad4ca8f917f932e717c127f38ba/dc0c1/email.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 20.88607594936709%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAECAYAAACOXx+WAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAoklEQVR42n1PXQuDMBDr//95otYnHxxsrPUb1NpiMxJw7GmFcLlrmuZMiBnzPKPve4zjiGEY1K/riuM48O9c14WcsxBjlN7YR8Lz9Yb3DtM0yYzY9x0hBFUKCXLiPE9s2/bVLsuiGT8wdG/bFtZaoSxLVFUlzloUhWYEOWd1XX91TdOg6zptRlPD6EzmvdfKrPf6zjnxG7zjw9+eCRkqpaSEHwz5LojsdFMBAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Email that Manager receives&quot;
        title=&quot;&quot;
        src=&quot;/static/d7925ad4ca8f917f932e717c127f38ba/f058b/email.png&quot;
        srcset=&quot;/static/d7925ad4ca8f917f932e717c127f38ba/c26ae/email.png 158w,
/static/d7925ad4ca8f917f932e717c127f38ba/6bdcf/email.png 315w,
/static/d7925ad4ca8f917f932e717c127f38ba/f058b/email.png 630w,
/static/d7925ad4ca8f917f932e717c127f38ba/40601/email.png 945w,
/static/d7925ad4ca8f917f932e717c127f38ba/78612/email.png 1260w,
/static/d7925ad4ca8f917f932e717c127f38ba/dc0c1/email.png 2348w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;The manager will use AWS CLI to approve or deny the creation of the new user and decide if the user will be an admin or not.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Deny&lt;/span&gt;
aws stepfunctions send-task-failure --task-token &lt;span class=&quot;token variable&quot;&gt;$TASK_TOKEN&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Approve as admin&lt;/span&gt;
aws stepfunctions send-task-success &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
 --task-output &lt;span class=&quot;token string&quot;&gt;&apos;{&quot;admin&quot;: true}&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
    --task-token &lt;span class=&quot;token variable&quot;&gt;$TASK_TOKEN&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Approve as developer&lt;/span&gt;
aws stepfunctions send-task-success &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
 --task-output &lt;span class=&quot;token string&quot;&gt;&apos;{&quot;admin&quot;: false}&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
    --task-token &lt;span class=&quot;token variable&quot;&gt;$TASK_TOKEN&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Running the &lt;code class=&quot;language-text&quot;&gt;send-task-success&lt;/code&gt; action with admin set to true we will have the user created as expected!&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/59516a9592f524594f42d1219050f443/7ae9c/user-created.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 56.9620253164557%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAAsTAAALEwEAmpwYAAABoElEQVR42oVS23aCMBDkG9qKIhclgATkKqnQqj22Pfb//2i6G4RiL6cPcxLI7GR2NkahTmi6V4ishelJLFY95l78PwbeKoHbXGBnBxhC1lBPbwjSBvfLADMn0jCvRb+JL64w3c2PCwxPNsjUC6KtQpQ9EhRC2nthjl13Rpzv+wuomDFzQgTlAbL9gLVpYI5nkRY1liIjdwrWOsHST0ewM5dEWdjyk1FQu6IW58Tn9eY/C/pxibw5gFsXskJIrTtBpgn8j52vomKMYur2O7Tg4MRmZ2EJS/R7drUksHP9TStfxPhzaC4J2mILWbRE3MLKTjCTDutNCY9cxcUesmyRlB0c4sXEYy4LzibZjRnSanA72/oZ2e6Asj1DHS8QyU6Tpk6mezYRUTTruIZbv8Kr3+BWZyyCnDOsoJ7fUagjEro9rTqdG7v0KUNfZ1vffK+pZkMcHmZQnSDKI9y0pUGlMDgbbk9WT/Co6M4SeLDD6xP5GoIX5f3gyD1nzhx9btPbtQW1G1HOshd0wgIrtl4c9YMdWhza5HX6pLhmOJ+Cp/wJDG44GQrLvF4AAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;IAM User created via Step Functions&quot;
        title=&quot;&quot;
        src=&quot;/static/59516a9592f524594f42d1219050f443/f058b/user-created.png&quot;
        srcset=&quot;/static/59516a9592f524594f42d1219050f443/c26ae/user-created.png 158w,
/static/59516a9592f524594f42d1219050f443/6bdcf/user-created.png 315w,
/static/59516a9592f524594f42d1219050f443/f058b/user-created.png 630w,
/static/59516a9592f524594f42d1219050f443/40601/user-created.png 945w,
/static/59516a9592f524594f42d1219050f443/7ae9c/user-created.png 972w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;In a real-world scenario, you would create something more sophisticated (e.g. send only the URL to approve/deny integrating with another system that will perform the Step Functions send-task-success or send-task-failure). For learning purposes the example above works great!&lt;/p&gt;
&lt;h3&gt;Exercise 03: Bucket replication with pooling system&lt;/h3&gt;
&lt;p&gt;This is the third exercise of this blog post! All the Terraform code for deploying this is in the &lt;a href=&quot;https://github.com/felipelaptrin/step-functions/tree/main/iac/modules/bucket-replication&quot;&gt;GitHub Repository&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;As commented earlier, some AWS actions can not be turned into synchronous jobs. In this case, the solution is to implement a pooling system which is a loop that after the run job action you will:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Get the status of the job&lt;/li&gt;
&lt;li&gt;Check the status of the job&lt;/li&gt;
&lt;li&gt;If the job is still running sleep for X seconds&lt;/li&gt;
&lt;li&gt;If the job finished with success/error you will handle these cases&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Imagine you would like to manually trigger a bucket replication using Data Sync. Since the DataSync &lt;code class=&quot;language-text&quot;&gt;StartTaskExecution&lt;/code&gt; action does not allow synchronous jobs we will implement the pool system to wait until it finishes with success/error. For simplicity, we will simply exit the step function execution with success or error but you could implement additional logic that makes sense in your use case.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 329px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/d6807a4c1e8a0ef02de8e71920826aef/004a5/infrastructure.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 22.151898734177216%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAECAYAAACOXx+WAAAACXBIWXMAAAsTAAALEwEAmpwYAAABIklEQVR42j2OzUoCYRSGvYQ2dgNBtWrZopVB1CKiFt1A6+4gaBMtI0JBikgwCFykQiAVZThtgqhcCI46zYyDlglaOqI5zqdPX0q9h5fD+Xs4nppt4r+Yl/ZxmFihYdfp9Vwcpyvt4LuN4I0HGYsGuK9YIAZ0uh36LiRf/OzFZtmPz6FkAvzKU7MNdiPT7EQmOIgtYJgF2u1vWi2bpm2zpsSYTIQYl1ClpCPkrNlq0vhsc363zdapV95Pkcr4R8C6/DB0s87x9SpnqQ1E3+FPAxmbz0mWlCgzV2HSX9Vh3+2LYX7InXB0uUwwschjPjwCGkULw9LI6yq5V5W3ysc/8L1S5SmrktZ1CuWy3DERQtBzXYxiiYKuoWpZNDMnaxPdtPgB0HUMPwduS7YAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Infrastructure of the Bucket replication with pooling system&quot;
        title=&quot;&quot;
        src=&quot;/static/d6807a4c1e8a0ef02de8e71920826aef/004a5/infrastructure.png&quot;
        srcset=&quot;/static/d6807a4c1e8a0ef02de8e71920826aef/c26ae/infrastructure.png 158w,
/static/d6807a4c1e8a0ef02de8e71920826aef/6bdcf/infrastructure.png 315w,
/static/d6807a4c1e8a0ef02de8e71920826aef/004a5/infrastructure.png 329w&quot;
        sizes=&quot;(max-width: 329px) 100vw, 329px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Here is the Step Function graph:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 544px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/ca998c15f86b1d05b04329667e799abf/b3e51/graph.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 107.59493670886076%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAWCAYAAADAQbwGAAAACXBIWXMAAAsTAAALEwEAmpwYAAAD7UlEQVR42pVVW28bRRR2Q6u2EhRIJSpxU6OkcSPjOInjje21vV7v1bte78Veb+LU8dpxltjppkmhqElazEWVSKREjXMpLUjQ9i0/AYkQ8UCLCuIBfgIUlYJU8eriYSY0KEhgkpGO9sw5M2e/c74zMw5HkwEAOIC+v97fzD/4cTO/27a/Ua22eC3rkPe6dQhNf3uwdffhz1t3kE4QxEHL8iJ7y77j7qAB4NHzUH96/8iAYzsAFZeN0OLlT8Tr86uapq8lBO2GwKvXUpqxpmmZVU01Pk3ENeNJOi1NMv3LKaQy6/zNqyB5c7lu2zZ4Y3oanLMnwfmpKTBWsuqZdBYoSX0NrdV1/almGLcRBnml/eTMGOG58iYej0t4MqnRRsqIQnS4JEk4QVBRnuTbd+9pGlBlVc9rzoGCs8OXVaTUkK5nLZ5NFskInaVJNstSfJFlRU/TgIg9ftk+jPRBfXDBTcjgdICv25UJMHn2HNA1AwichKQuSymQENT5PacsiqK3s2eg7PKFyqqslpSkUuQYzmSY+ITAJS/FYtxbUMf/E6H+sv9o2CpG6NtXKXmyQimigg0Pl8L57Cis2WC3LGd7TNPqGzLNU4i4jY2NpqgcQz30KWqq8nvis1vAmLvQsMfKwLLGQXm8DCbGK+B1a/yPkTMFkNaMe0S1elCfnj7atLHVAPMCljVqfQsXP+aswo1iLr9umuZ60TRXC+ZIzRwxazwrfkiR3IUBnIpiBO3fV39jGHbsxLMn2ro8uKuzG2trf9H1isfjf4kg2A48ynyEh5krCqUcb8byAXR2YTpH0EQW5YWuAAecGF0fGc6B0UIJQJRAlhRARplHTIwDNMXP73THP4Ii2neo3zkpibhccvaT33X0Ep9nUqktwxi8M5LL3U2r6S9jBLNFkez3NB0v7W6b3XEcQYLp8QbId7t94YseX2TO3R+axYLRWV+AuOzGCP3vUgRjJBaiq33Q14NFZt3e4Fwf/KI9Xhx/dXsRFeECLpd/FAZ4O0wwMxST+IDlpfdxqEeibDUa5c6zLNuaz+ef6x+IVLp6B96BNZxh+eR7NJ+cD0XoWYJgLlEUP8yyQtAR5+Wvu8heAH/+2C6fBRXYJmktAyCrDZ4VGglBBrKcCg8VxggmEgOBztMNwxhuoLqKcQlwjIDksSSqQBKVb2FRWbuzv3vFh4eXMmljKZXKrNAxrhYj2cVtgTrHcSclSW/z+/Flt9O1KAjSYkJM1iD6FehfQoL2MJQw+T8XrOPJBftVKwA/tO7pCUDMIOp3C7Lxtn0Y1u1Iawd27OH9L7755afNe8edwWeQDfl0x7/v29MTAAPmoJzZC8I/AYhohfc9ccoCAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Bucket replication with pooling system graph&quot;
        title=&quot;&quot;
        src=&quot;/static/ca998c15f86b1d05b04329667e799abf/b3e51/graph.png&quot;
        srcset=&quot;/static/ca998c15f86b1d05b04329667e799abf/c26ae/graph.png 158w,
/static/ca998c15f86b1d05b04329667e799abf/6bdcf/graph.png 315w,
/static/ca998c15f86b1d05b04329667e799abf/b3e51/graph.png 544w&quot;
        sizes=&quot;(max-width: 544px) 100vw, 544px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Once the step function gets triggered the DataSync task will start and the workflow will check the task execution state every 10s. The easiest way to visualize that the pooling system is working is by checking the state view.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/fcebf65b4a584ad163106e645931d7fd/9451d/state-view.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 53.16455696202532%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAABYlAAAWJQFJUiTwAAABy0lEQVR42n1T7ZKaQBDkHRLlUwGR3QVBVDyxchqrjLlcVZJK3v9lOtPjx11+JD+mgN2enu7exUvbZ0Rmg7gaEJo1wmqH2PZIzRKZW2NqOkzKpT6DrMJoYv5bXlGtkZYtcrtCZjuEmcNM1mZuI+sdotkCsRTBH+I5/NRpfUxKjKdWi+8cxncvFxVBVguowmS+lMYrKHIL+AUbjahbypAecdFomWYHf+oE57AaTnCrPcrmSQY38Fy3F3srnTAVRSQYCaHfiBInhLFBlC+QVxvFRdJkl8PNvsX59Tf2p29w3YBEBHlkpkIlNDdCAQa1EFqHUWLVYiAO+AwFm0scVEfc8fIDT4cLTLtDUrTwGHwybxVMJXdC34ilwuo3STiMDRyeSt53wnZ7UHVcC6XfIzOJqODfCqsbppamN4Ws4+U7ts9fNAa1zAx5GDpZMrpnGLzLkHsTyZehk7iotw/C8+sv7D+/KGFMyzxlSqUKnuDDspCN57RsH3tUR/Ki7nWNhL2oa7dHVa2WuUkwAczyL8LyHaGoC2+HR9z1ejlsPp2x6A96C3gDPErln8DpzJAXeKyWKwTO6TcvN0NnNBzOmEjMgzy9/MRw/Hq1nNf4A3jdOjsLDyfBAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;State view of the Step Function exeuction&quot;
        title=&quot;&quot;
        src=&quot;/static/fcebf65b4a584ad163106e645931d7fd/f058b/state-view.png&quot;
        srcset=&quot;/static/fcebf65b4a584ad163106e645931d7fd/c26ae/state-view.png 158w,
/static/fcebf65b4a584ad163106e645931d7fd/6bdcf/state-view.png 315w,
/static/fcebf65b4a584ad163106e645931d7fd/f058b/state-view.png 630w,
/static/fcebf65b4a584ad163106e645931d7fd/40601/state-view.png 945w,
/static/fcebf65b4a584ad163106e645931d7fd/78612/state-view.png 1260w,
/static/fcebf65b4a584ad163106e645931d7fd/9451d/state-view.png 2782w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Notice the loop: &lt;code class=&quot;language-text&quot;&gt;Wait 10s&lt;/code&gt; -&gt; &lt;code class=&quot;language-text&quot;&gt;DescribeTaskExecution&lt;/code&gt; -&gt; &lt;code class=&quot;language-text&quot;&gt;Job Complete?&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;Exercise 04: Profile picture resize&lt;/h3&gt;
&lt;p&gt;This is the second exercise of this blog post! All the Terraform code for deploying this is in the &lt;a href=&quot;https://github.com/felipelaptrin/step-functions/tree/main/iac/modules/resize-image&quot;&gt;GitHub Repository&lt;/a&gt;. This will be the only exercise which I&apos;ve created two definitions: one in JSONata and another in JSONPath, so you can deploy as you wish and check the differences. The goal of this exercise is to show you how to deploy with Terraform.&lt;/p&gt;
&lt;p&gt;Imagine the following scenario: your platform accepts profile pictures of the users but you need to standardize the profile picture size to use in two cases: small pictures (128x128 px) and big pictures (512x512 px). You would like the user to upload it&apos;s profile picture (PNG file) and then resize the image of the profile picture automatically. We could automate that using the following infrastructure.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/3b0cb3504166f4762b7fd8a71534672d/30d16/infrastructure.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 24.050632911392405%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAFCAYAAABFA8wzAAAACXBIWXMAAAsTAAALEwEAmpwYAAABNElEQVR42iWQy0rDQBSG8zri1nfwdXwKwYVrVyIuFUHQlYgbN9ZSL7VWWhUtvWiaZJLJpM1tMvM5tQc+/sO5cC4ezuTVI/KyA8rSzDNsVTs0NGaVXvu1Wau1WOPQLuf415U1DY1c4NW6Jo0VQk1pfRwTqk+qQqOxlO0R+dE9Oi8JE+HiFfnZM/nNkEBE+CKkGEcsD1s0X5EbZPCSJOal+0Z3eM3u6Qad4Qnf7xP8PEbsXhFt7iGnAQ+9Z8IfH7F9QLpzTq//ymO/i7wdEG3tU168uhXA01qzSDPKakl/1CYrBMaur8gmIbO7AavKoq7cBwzj9oBZ74vKXVY6gtmc95snfr9n2FrjrRrNokDHC5SfomWOUWvyRJGImCZdYlXxryqWZEJisgLrSP2IwJ8jA0EdSP4AP0Vz0denCbMAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Resize Profile Picture Infrastructure&quot;
        title=&quot;&quot;
        src=&quot;/static/3b0cb3504166f4762b7fd8a71534672d/f058b/infrastructure.png&quot;
        srcset=&quot;/static/3b0cb3504166f4762b7fd8a71534672d/c26ae/infrastructure.png 158w,
/static/3b0cb3504166f4762b7fd8a71534672d/6bdcf/infrastructure.png 315w,
/static/3b0cb3504166f4762b7fd8a71534672d/f058b/infrastructure.png 630w,
/static/3b0cb3504166f4762b7fd8a71534672d/30d16/infrastructure.png 672w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;When the bucket receives new profile pictures it will send this information to the SQS Queue via S3 events notification. The Step Function will consume the queue using EventBridge Pipe (since there is no direct integration between SQS and Step Function directly).&lt;/p&gt;
&lt;p&gt;The following Step Function will be used:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/b903abe87e18892e365121ab21f5f414/dba9a/graph.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 123.41772151898734%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAZCAYAAAAxFw7TAAAACXBIWXMAAAsTAAALEwEAmpwYAAAFMElEQVR42p1VWXBTVRgO6IyMvusgQ4vYNLn7cu6S5Ca5SUqWNkn3m6UB2qJFagF1XFCIDQVEoeoMDAgvjk+K9NHRB4c0REUWZ7CNgugwsoySFhDwwUfI9T9J6VSlMOOd+eae5fu+e+45//8fi2X+Z0Eul3sYN65eya+/PlUYwu2xMeMhPGf5P485NobFllvXv34Nozpm5hbeVxSJRB5h2eBjCMUevQtd712k+cMUxSHN5wuqnkC7omkRRygUVZqb41o4HKfq6/VFczXYQ9f1RRZalDMgHKF4aTO8twCynCBvcvuC55ewutnakbidSfWYAV/I1D2B203+kOnXQ78SjPgq5lY1Ne0Ig+ReC8lJ/TD5DsGgrYBtdlrcTjEoixTPEU71mYFgc6Up2Gw2BVvMSEvbnWA4asqK+4iNErZgLtZgLbR3Ubz8rIXk5V6SlXZQnJyF93AN8ps2IPGSeoBGyn5KUg/ysuugoGj7OUU70EiLubn8Whu9RQvKGgvDyX0kg0YpTtqGjWewnePVvQQl7MNvh9P3geT0HiAocS/Lq/t4ybkXc+7ya1q0m+KVZyyMqD5PcspxmpPHYWVHMWheybv14I061m22dRiVDUND5pq+fnNVT6aSNBKmTw/eBKM8cIszmgLJK8fgYxvBUBlqpNCJJVYpv8wuFRpJoWAl+HEeaRMNvHYOObxn3Z7Az06P/xfBGTyLHE3nRMk1aaXEPObaaYSRh338luGUFy0Erw4ipJQGmonJqI8vcZK3pDr1MyIYCkg9zQrqBMHKpxhePrZ6BTHR5mNPC5J3wuHyneGRq8TySokVHJMM8Gi8wjpSXRv1ea/9udNuHnrFXVm9ZtAcXDtg9vf2melkqoJDJt7a+VfQ473821bK/GKTUuWsGxiobgPmrMqsNJtWhKetpLB+doVrvfWTrRoBK/SUFNX9o6R4SrID2k69BCH0vSCq3/W7l//QqVlLvAxzVY5W5Thc+qQA20Dz8kYLLcjrWFH7yZf4uNyU3FeOxWLl9o7EdFtbVzkabb8CmTLt1PznKVY65ev+cDqQ/qgci0arnHhrVzkW78CcKQE5z8DhbIDARs8hyXspufJLs7v38J2k0VlJGKnbPemeSiqZupNKpMxQOHqT5aRSMvOZ2dX3eZWTNNKzHKM7YTpdvgtwOOstDA9hQ0snG+xMwWpjiw12/iic6AmY/MpKCMVGUizaKLGI+0/bmCLm4HFND5yEUy7OoECw0onqKWND6ByvxZUEISOMt3e2n5QU91EbDgdOGieYOYA+DhMj2XUKsqwAYxjjJCMfA68XLDTkMuTzHpjcTXLyKMZTNnaXnRagj0YbSX63jZLet7HS+3gMzEbtkFmYc5df00p7mNlcpsUduCCQrJizk/zrist9uGuH/0Y6G5kyOlJTmczKa8lE+lo4FJ1qaY6VI7CnTk3/FHNJnNdYSwu1XMaGEOU7cZITUIIICm1BDtehFS+rv8dfClxMGelLhpG4mEikLhrdxqVEInmhs72rrDq8n8DebgbtSFULHtVc/odhrYThUjRstaKs1SZkFy+1bna4vG9run/X4nr7G8ttTBYD/maW/0BD/FW8WkqQR+DUc5GWlnejbfH3oD0MKTYC5WpkDve/hriW4RoIE7l/g+alHITOcAPJD0MVyt2LU9PO1EPW6XzcxqnLSKTVzYc6O1psE1xPUpJn6Xwc7EGr6hP3v/XM2q33x3Qxd+vqN1trY+bCB92WC+bD2Mw1Wr58ZPB6Ob+uei8bs/fyPfE3ILpHDrUW5v0AAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Resize Profile Picture Step Function Graph&quot;
        title=&quot;&quot;
        src=&quot;/static/b903abe87e18892e365121ab21f5f414/f058b/graph.png&quot;
        srcset=&quot;/static/b903abe87e18892e365121ab21f5f414/c26ae/graph.png 158w,
/static/b903abe87e18892e365121ab21f5f414/6bdcf/graph.png 315w,
/static/b903abe87e18892e365121ab21f5f414/f058b/graph.png 630w,
/static/b903abe87e18892e365121ab21f5f414/dba9a/graph.png 652w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Notice that the Step Function will consume the SQS Queue in batches, i.e. the input of the Step Function workflow can be more than one event about the image uploaded, that&apos;s why we are using the &lt;code class=&quot;language-text&quot;&gt;Map&lt;/code&gt; flow state. For every profile picture uploaded in the input event of the workflow we would like to (in parallel) execute the lambda that will resize the profile picture to the desired size. We store in the DynamoDB the S3 key of the resized profile picture, this will be used in the backend application (out of scope of this project). Finally, when the images are resized we can delete the original profile picture, since it&apos;s not helpful anymore.&lt;/p&gt;
&lt;p&gt;The main entry file of the Terraform code is available below:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;hcl&quot;&gt;&lt;pre class=&quot;language-hcl&quot;&gt;&lt;code class=&quot;language-hcl&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// main.tf&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;locals&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;project&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;resize-profile-picture&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;##############################&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;##### S3 + SQS&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;##############################&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;resource &lt;span class=&quot;token type variable&quot;&gt;&quot;aws_s3_bucket&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;this&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;bucket_prefix&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;profile-pictures&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;resource &lt;span class=&quot;token type variable&quot;&gt;&quot;aws_sqs_queue&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;this&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;profile-pictures&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;resource &lt;span class=&quot;token type variable&quot;&gt;&quot;aws_sqs_queue_policy&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;this&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;queue_url&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; aws_sqs_queue.this.id
  &lt;span class=&quot;token property&quot;&gt;policy&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; jsonencode(&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;Version&quot;&lt;/span&gt; : &lt;span class=&quot;token string&quot;&gt;&quot;2012-10-17&quot;&lt;/span&gt;,
    &lt;span class=&quot;token property&quot;&gt;&quot;Id&quot;&lt;/span&gt; : &lt;span class=&quot;token string&quot;&gt;&quot;AllowS3Notification&quot;&lt;/span&gt;,
    &lt;span class=&quot;token property&quot;&gt;&quot;Statement&quot;&lt;/span&gt; : &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;Effect&quot;&lt;/span&gt; : &lt;span class=&quot;token string&quot;&gt;&quot;Allow&quot;&lt;/span&gt;,
        &lt;span class=&quot;token property&quot;&gt;&quot;Principal&quot;&lt;/span&gt; : &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;token property&quot;&gt;&quot;Service&quot;&lt;/span&gt; : &lt;span class=&quot;token string&quot;&gt;&quot;s3.amazonaws.com&quot;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;,
        &lt;span class=&quot;token property&quot;&gt;&quot;Action&quot;&lt;/span&gt; : &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
          &lt;span class=&quot;token string&quot;&gt;&quot;SQS:SendMessage&quot;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;,
        &lt;span class=&quot;token property&quot;&gt;&quot;Resource&quot;&lt;/span&gt; : aws_sqs_queue.this.arn,
        &lt;span class=&quot;token property&quot;&gt;&quot;Condition&quot;&lt;/span&gt; : &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;token property&quot;&gt;&quot;ArnLike&quot;&lt;/span&gt; : &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;&quot;aws:SourceArn&quot;&lt;/span&gt; : aws_s3_bucket.this.arn
          &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;)
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;resource &lt;span class=&quot;token type variable&quot;&gt;&quot;aws_s3_bucket_notification&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;this&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;bucket&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; aws_s3_bucket.this.id

  &lt;span class=&quot;token keyword&quot;&gt;queue&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;queue_arn&lt;/span&gt;     &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; aws_sqs_queue.this.arn
    &lt;span class=&quot;token property&quot;&gt;events&lt;/span&gt;        &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;s3:ObjectCreated:*&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;filter_prefix&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;uploaded/&quot;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;##############################&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;##### DYNAMODB TABLE&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;##############################&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;resource &lt;span class=&quot;token type variable&quot;&gt;&quot;aws_dynamodb_table&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;this&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;name&lt;/span&gt;         &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;user&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;billing_mode&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;PAY_PER_REQUEST&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;hash_key&lt;/span&gt;     &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;userId&quot;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;attribute&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;userId&quot;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;S&quot;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;##############################&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;##### LAMBDA &amp;amp; ECR&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;##############################&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;resource &lt;span class=&quot;token type variable&quot;&gt;&quot;aws_ecr_repository&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;this&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;name&lt;/span&gt;                 &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;local&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token type variable&quot;&gt;project&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;image_tag_mutability&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;MUTABLE&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;resource &lt;span class=&quot;token type variable&quot;&gt;&quot;docker_image&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;this&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;aws_ecr_repository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;this&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;repository_url&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;:latest&quot;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;build&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;context&lt;/span&gt;  &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token type variable&quot;&gt;cwd&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;/../lambda/resize-image&quot;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;platform&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; var.lambda_architecture &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;arm64&quot;&lt;/span&gt; ? &lt;span class=&quot;token property&quot;&gt;&quot;linux/arm64&quot;&lt;/span&gt; : &lt;span class=&quot;token string&quot;&gt;&quot;linux/amd64&quot;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;force_remove&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;resource &lt;span class=&quot;token type variable&quot;&gt;&quot;docker_registry_image&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;this&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;name&lt;/span&gt;          &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; docker_image.this.name
  &lt;span class=&quot;token property&quot;&gt;keep_remotely&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;resource &lt;span class=&quot;token type variable&quot;&gt;&quot;aws_lambda_function&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;this&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;depends_on&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;docker_registry_image.this&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;

  &lt;span class=&quot;token property&quot;&gt;function_name&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; local.project
  &lt;span class=&quot;token property&quot;&gt;role&lt;/span&gt;          &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; aws_iam_role.lambda.arn
  &lt;span class=&quot;token property&quot;&gt;package_type&lt;/span&gt;  &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Image&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;image_uri&lt;/span&gt;     &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;aws_ecr_repository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;this&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;repository_url&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;:latest&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;architectures&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;var.lambda_architecture&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;timeout&lt;/span&gt;       &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;environment&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;variables&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;BUCKET_NAME&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; aws_s3_bucket.this.id
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;resource &lt;span class=&quot;token type variable&quot;&gt;&quot;aws_iam_role&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;lambda&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;lambda-&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;local&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token type variable&quot;&gt;project&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;assume_role_policy&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; jsonencode(&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;Version&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;2012-10-17&quot;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;Statement&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;Action&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;sts:AssumeRole&quot;&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;Effect&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Allow&quot;&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;Sid&lt;/span&gt;    &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;Principal&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;token property&quot;&gt;Service&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;lambda.amazonaws.com&quot;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;,
    &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;)
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;resource &lt;span class=&quot;token type variable&quot;&gt;&quot;aws_cloudwatch_log_group&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;lambda&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/aws/lambda/&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;local&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token type variable&quot;&gt;project&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;resource &lt;span class=&quot;token type variable&quot;&gt;&quot;aws_iam_policy&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;lambda&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;lambda-&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;local&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token type variable&quot;&gt;project&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;path&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;policy&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; jsonencode(&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;Version&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;2012-10-17&quot;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;Statement&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;Sid&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;S3Actions&quot;&lt;/span&gt;,
        &lt;span class=&quot;token property&quot;&gt;Action&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
          &lt;span class=&quot;token string&quot;&gt;&quot;s3:List*&quot;&lt;/span&gt;,
          &lt;span class=&quot;token string&quot;&gt;&quot;s3:Get*&quot;&lt;/span&gt;,
          &lt;span class=&quot;token string&quot;&gt;&quot;s3:Put*&quot;&lt;/span&gt;,
        &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;Effect&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Allow&quot;&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;Resource&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
          &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;aws_s3_bucket&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;this&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;arn&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;,
          &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;aws_s3_bucket&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;this&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;arn&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;/*&quot;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;,
      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;Sid&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;CloudWatchActions&quot;&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;Effect&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Allow&quot;&lt;/span&gt;,
        &lt;span class=&quot;token property&quot;&gt;Action&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&quot;logs:CreateLogStream&quot;&lt;/span&gt;,
        &lt;span class=&quot;token string&quot;&gt;&quot;logs:PutLogEvents&quot;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;,
        &lt;span class=&quot;token property&quot;&gt;Resource&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;aws_cloudwatch_log_group&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;lambda&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;arn&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;:*&quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;)
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;resource &lt;span class=&quot;token type variable&quot;&gt;&quot;aws_iam_role_policy_attachment&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;lambda&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;role&lt;/span&gt;       &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; aws_iam_role.lambda.name
  &lt;span class=&quot;token property&quot;&gt;policy_arn&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; aws_iam_policy.lambda.arn
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;##############################&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;##### STEP FUNCTIONS&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;##############################&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;resource &lt;span class=&quot;token type variable&quot;&gt;&quot;aws_iam_role&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;sfn&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;sfn-&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;local&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token type variable&quot;&gt;project&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;assume_role_policy&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; jsonencode(&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;Version&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;2012-10-17&quot;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;Statement&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;Action&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;sts:AssumeRole&quot;&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;Effect&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Allow&quot;&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;Sid&lt;/span&gt;    &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;Principal&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;token property&quot;&gt;Service&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;states.amazonaws.com&quot;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;,
    &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;)
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;resource &lt;span class=&quot;token type variable&quot;&gt;&quot;aws_iam_policy&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;sfn&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;sfn-&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;local&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token type variable&quot;&gt;project&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;path&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;policy&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; jsonencode(&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;Version&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;2012-10-17&quot;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;Statement&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;Sid&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;S3Actions&quot;&lt;/span&gt;,
        &lt;span class=&quot;token property&quot;&gt;Action&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
          &lt;span class=&quot;token string&quot;&gt;&quot;s3:List*&quot;&lt;/span&gt;,
          &lt;span class=&quot;token string&quot;&gt;&quot;s3:Get*&quot;&lt;/span&gt;,
          &lt;span class=&quot;token string&quot;&gt;&quot;s3:Put*&quot;&lt;/span&gt;,
          &lt;span class=&quot;token string&quot;&gt;&quot;s3:Delete*&quot;&lt;/span&gt;,
        &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;Effect&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Allow&quot;&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;Resource&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
          &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;aws_s3_bucket&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;this&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;arn&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;,
          &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;aws_s3_bucket&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;this&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;arn&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;/*&quot;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;,
      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;Sid&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;LambdaActions&quot;&lt;/span&gt;,
        &lt;span class=&quot;token property&quot;&gt;Action&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
          &lt;span class=&quot;token string&quot;&gt;&quot;lambda:InvokeFunction&quot;&lt;/span&gt;,
        &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;Effect&lt;/span&gt;   &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Allow&quot;&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;Resource&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
          &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;aws_lambda_function&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;this&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;arn&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;,
          &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;aws_lambda_function&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;this&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;arn&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;*&quot;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;,
      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;Sid&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;DynamoDBActions&quot;&lt;/span&gt;,
        &lt;span class=&quot;token property&quot;&gt;Action&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
          &lt;span class=&quot;token string&quot;&gt;&quot;dynamodb:UpdateItem&quot;&lt;/span&gt;,
        &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;Effect&lt;/span&gt;   &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Allow&quot;&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;Resource&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;aws_dynamodb_table&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;this&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;arn&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;,
    &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;)
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;resource &lt;span class=&quot;token type variable&quot;&gt;&quot;aws_iam_role_policy_attachment&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;sfn&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;role&lt;/span&gt;       &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; aws_iam_role.sfn.name
  &lt;span class=&quot;token property&quot;&gt;policy_arn&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; aws_iam_policy.sfn.arn
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;resource &lt;span class=&quot;token type variable&quot;&gt;&quot;aws_sfn_state_machine&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;this&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;name&lt;/span&gt;     &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;local&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token type variable&quot;&gt;project&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;role_arn&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; aws_iam_role.sfn.arn
  &lt;span class=&quot;token property&quot;&gt;definition&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; templatefile(
    &lt;span class=&quot;token string&quot;&gt;&quot;machine-definition/resize-image/&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token type variable&quot;&gt;query_language&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;.json&quot;&lt;/span&gt;,
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;LAMBDA_NAME&lt;/span&gt;         &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; aws_lambda_function.this.id
      &lt;span class=&quot;token property&quot;&gt;DYNAMODB_TABLE_NAME&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; aws_dynamodb_table.this.id
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  )
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;##############################&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;##### EVENTBRIDGE PIPE&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;##############################&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;resource &lt;span class=&quot;token type variable&quot;&gt;&quot;aws_iam_role&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;pipe&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;pipe-&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;local&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token type variable&quot;&gt;project&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;assume_role_policy&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; jsonencode(&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;Version&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;2012-10-17&quot;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;Statement&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;Action&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;sts:AssumeRole&quot;&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;Effect&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Allow&quot;&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;Principal&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;token property&quot;&gt;Service&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;pipes.amazonaws.com&quot;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;,
    &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;)
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;resource &lt;span class=&quot;token type variable&quot;&gt;&quot;aws_iam_policy&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;pipe&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;pipe-&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;local&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token type variable&quot;&gt;project&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;path&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;policy&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; jsonencode(&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;Version&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;2012-10-17&quot;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;Statement&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;Sid&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;SqsAccess&quot;&lt;/span&gt;,
        &lt;span class=&quot;token property&quot;&gt;Action&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
          &lt;span class=&quot;token string&quot;&gt;&quot;sqs:ReceiveMessage&quot;&lt;/span&gt;,
          &lt;span class=&quot;token string&quot;&gt;&quot;sqs:DeleteMessage&quot;&lt;/span&gt;,
          &lt;span class=&quot;token string&quot;&gt;&quot;sqs:GetQueueAttributes&quot;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;Effect&lt;/span&gt;   &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Allow&quot;&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;Resource&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;aws_sqs_queue&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;this&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;arn&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;,
      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;Sid&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;StepFunctionsAccess&quot;&lt;/span&gt;,
        &lt;span class=&quot;token property&quot;&gt;Action&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
          &lt;span class=&quot;token string&quot;&gt;&quot;states:StartExecution&quot;&lt;/span&gt;,
          &lt;span class=&quot;token string&quot;&gt;&quot;states:StartSyncExecution&quot;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;Effect&lt;/span&gt;   &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Allow&quot;&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;Resource&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;aws_sfn_state_machine&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;this&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;arn&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;,
    &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;)
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;resource &lt;span class=&quot;token type variable&quot;&gt;&quot;aws_iam_role_policy_attachment&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;pipe&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;role&lt;/span&gt;       &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; aws_iam_role.pipe.name
  &lt;span class=&quot;token property&quot;&gt;policy_arn&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; aws_iam_policy.pipe.arn
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;resource &lt;span class=&quot;token type variable&quot;&gt;&quot;aws_pipes_pipe&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;this&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;name&lt;/span&gt;     &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;trigger-&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;local&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token type variable&quot;&gt;project&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;-sfn&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;role_arn&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; aws_iam_role.pipe.arn
  &lt;span class=&quot;token property&quot;&gt;source&lt;/span&gt;   &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; aws_sqs_queue.this.arn
  &lt;span class=&quot;token property&quot;&gt;target&lt;/span&gt;   &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; aws_sfn_state_machine.this.arn

  &lt;span class=&quot;token keyword&quot;&gt;source_parameters&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;sqs_queue_parameters&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;batch_size&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;maximum_batching_window_in_seconds&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;target_parameters&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;step_function_state_machine_parameters&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;invocation_type&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;FIRE_AND_FORGET&quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Let&apos;s break down this code into pieces. We created the:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;S3 bucket to upload the profile pictures and store the small and big sizes.&lt;/li&gt;
&lt;li&gt;SQS Queue that will receive the S3 events when new profile pictures get uploaded. We also make sure that the Queue Policy allows the &lt;code class=&quot;language-text&quot;&gt;SQS:SendMessage&lt;/code&gt; action from the S3 bucket.&lt;/li&gt;
&lt;li&gt;DynamoDB table that contains metadata about the resized profile picture&lt;/li&gt;
&lt;li&gt;ECR repository and Lambda Function to run the resize profile picture code (more on that later). The &lt;code class=&quot;language-text&quot;&gt;kreuzwerker/docker&lt;/code&gt; provider was used to build and push the Lambda code from the IaC code. The Lambda Function was granted write permissions to the S3 bucket and CloudWatch (for logging).&lt;/li&gt;
&lt;li&gt;Step Function was created and used the JSON definition as a template (i.e. the JSON code contains placeholders that are substituted during IaC deployment: &lt;code class=&quot;language-text&quot;&gt;LAMBDA_NAME&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;DYNAMODB_TABLE_NAME&lt;/code&gt;). The reason why this was done is that it allowed us to reference resources created by IaC with IaC code reference instead of hardcoding them (ARNs, resource names...) in the JSON definition, which allows us to use a single JSON definition for all environments, instead of creating a hardcoded JSON definition per environment (Dev/Staging/Production). The Step Function was granted the needed permissions to interact with the S3 bucket, Lambda Function, and DynamoDB Table.&lt;/li&gt;
&lt;li&gt;EventBridge Pipe to trigger Step Functions from SQS Queue.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The code used some variables:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;hcl&quot;&gt;&lt;pre class=&quot;language-hcl&quot;&gt;&lt;code class=&quot;language-hcl&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// variables.tf&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;variable&lt;span class=&quot;token type variable&quot;&gt; &quot;aws_region&quot; &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;description&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;AWS region to deploy the resources&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;type&lt;/span&gt;        &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; string
  &lt;span class=&quot;token property&quot;&gt;default&lt;/span&gt;     &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;us-east-1&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;variable&lt;span class=&quot;token type variable&quot;&gt; &quot;lambda_architecture&quot; &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;description&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Instruction set architecture for your Lambda function. Valid values are &apos;x86_64&apos; &apos;arm64&apos;. Since we are also deploying the image via local deployemnt this should correspond to your computer architecture&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;type&lt;/span&gt;        &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; string
  &lt;span class=&quot;token property&quot;&gt;default&lt;/span&gt;     &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;arm64&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;variable&lt;span class=&quot;token type variable&quot;&gt; &quot;query_language&quot; &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;description&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Query Language to use in the Step Functions&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;type&lt;/span&gt;        &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; string
  &lt;span class=&quot;token property&quot;&gt;default&lt;/span&gt;     &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;jsonpath&quot;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;validation&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;condition&lt;/span&gt;     &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; contains(&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;jsonata&quot;&lt;/span&gt;, &lt;span class=&quot;token string&quot;&gt;&quot;jsonpath&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;, var.query_language)
    &lt;span class=&quot;token property&quot;&gt;error_message&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Valid values for var: test_variable are: &apos;jsonata&apos;, &apos;jsonpath&apos;&quot;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I also mentioned about the Lambda Code. Let&apos;s take a look.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;python&quot;&gt;&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; io
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; os

&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; boto3
&lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; PIL &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; Image

s3 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; boto3&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;client&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;s3&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; region_name&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;os&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;getenv&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;AWS_REGION&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;us-east-1&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;resize_image_s3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    bucket_name&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    input_key&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    output_key&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    size&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;int&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Downloading uploaded profile picture...&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    response &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; s3&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;get_object&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Bucket&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;bucket_name&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Key&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;input_key&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    image_data &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; response&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;Body&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;read&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-interpolation&quot;&gt;&lt;span class=&quot;token string&quot;&gt;f&quot;Resizing image to &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;size&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;size&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;...&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    image &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Image&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;io&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;BytesIO&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;image_data&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    image_resized &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; image&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;resize&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;size&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; size&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token builtin&quot;&gt;buffer&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; io&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;BytesIO&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    image_resized&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;save&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;buffer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;image&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token builtin&quot;&gt;buffer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;seek&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Uploading resized profile picture to S3...&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    s3&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;put_object&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        Bucket&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;bucket_name&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        Key&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;output_key&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        Body&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;buffer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        ContentType&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;response&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;ContentType&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;lambda_handler&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; context&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-interpolation&quot;&gt;&lt;span class=&quot;token string&quot;&gt;f&quot;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;event &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-interpolation&quot;&gt;&lt;span class=&quot;token string&quot;&gt;f&quot;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;context &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        bucket_name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; event&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;body&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;s3&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;bucket&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
        size &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; event&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;size&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
        uploaded_profile_picture_key &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; event&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;body&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;s3&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;object&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;key&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
        picture_name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; uploaded_profile_picture_key&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;split&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
        user_id &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; picture_name&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;split&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;.png&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
        resized_profile_picture_key &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-interpolation&quot;&gt;&lt;span class=&quot;token string&quot;&gt;f&quot;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;user_id&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;size&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;.png&quot;&lt;/span&gt;&lt;/span&gt;

        resize_image_s3&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
            bucket_name&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            uploaded_profile_picture_key&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            resized_profile_picture_key&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            size
        &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&quot;statusCode&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&quot;data&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token string&quot;&gt;&quot;message&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Image resized!&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;token string&quot;&gt;&quot;userId&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; user_id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;token string&quot;&gt;&quot;bucket&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; bucket_name&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;token string&quot;&gt;&quot;uploadedKey&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; uploaded_profile_picture_key&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;token string&quot;&gt;&quot;resizedKey&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; resized_profile_picture_key
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;except&lt;/span&gt; Exception &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;raise&lt;/span&gt; Exception&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-interpolation&quot;&gt;&lt;span class=&quot;token string&quot;&gt;f&quot;Something went wrong: &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;It is a simple code that resizes the image using PIL library.&lt;/p&gt;
&lt;h2&gt;Cya!&lt;/h2&gt;
&lt;p&gt;I hope you&apos;ve liked it and that this demo and explanation was helpful! Remember that reading this blog post is not enough, I highly encourage you to deploy the examples mentioned here and also create your own workflow!&lt;/p&gt;
&lt;p&gt;See you around! 👋&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Deploying a serverless, cheap, and scalable infrastructure using Terraform in AWS]]></title><description><![CDATA[In today's blog post, I would like to comment on costs, scalability, and my thoughts about an initial architecture for a new company/product…]]></description><link>https://felipetrindade.com/serverless-infra/</link><guid isPermaLink="false">https://felipetrindade.com/serverless-infra/</guid><pubDate>Wed, 04 Dec 2024 10:40:32 GMT</pubDate><content:encoded>&lt;p&gt;In today&apos;s blog post, I would like to comment on costs, scalability, and my thoughts about an initial architecture for a new company/product. All the code of this blog post will be available on &lt;a href=&quot;https://github.com/felipelaptrin/serverless-infra-blog/tree/main&quot;&gt;GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I&apos;ve worked with several different clients and it still shocks me how some care about technology more than the business. They focus so much on using new technologies, hyped tools, and writing their own solutions for problems that already exist that they simply ignore the economic and business parts of the organization.&lt;/p&gt;
&lt;p&gt;During my time working in new startups, I notice that:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Time is key&lt;/strong&gt;: The company is still trying to validate an idea or a product and needs to do that fast. You don&apos;t need to deliver a full/complete product, only the minimum viable product (MVP) to validate if what the company is trying to do is feasible.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Money is tight&lt;/strong&gt;: Money is not infinite, the company needs to spend the money in the correct place. Use it wisely and remember that (developer) time is money.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;You are not a MANG&lt;/strong&gt; It&apos;s so common to see entrepreneurs thinking that their idea is so extraordinary that they will be the next MANG (Meta, Apple, Netflix, Google). Well, I expect them to grow, but let&apos;s keep our feet on the ground. It&apos;s amazing to read MANG&apos;s tech blogs and see the solution they are using for their problems (i.e. tools, strategies...) but remember you are NOT a FANG. Their blog post means: that if you were us, facing the same problems that we are, maybe, and just maybe, the proposed solution here will work for you. Start small, grow constantly, and modify your infrastructure and software as you grow. There is a well-known sentence in the software engineering community that says &quot;Premature Optimization is the root of all evil&quot;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It&apos;s shocking the amount of time that I saw over complex architectures in startups that don&apos;t even have clients: Kubernetes, multi-regional deployment, extra costs involved in compliance (we not needed)... This only increases the overall costs of the startup and reduces the time to market of the product.&lt;/p&gt;
&lt;p&gt;Here, I will propose a simple, scalable and cheap infrastructure, ideal for most startups. This is 100% opiniated, and that&apos;s fine, it might not be viable in all scenarios but it&apos;s a good start for a good amount of startups. I have DevOps friends that do not like to use managed services at all, and hate serverless and Lambdas and that&apos;s completely ok, just make sure you are aware of the pros and cons of each approach.&lt;/p&gt;
&lt;h2&gt;Full Stack Architecture&lt;/h2&gt;
&lt;p&gt;A basic full-stack architecture is composed of at least three parts:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Backend&lt;/strong&gt;: A piece of software that will be responsible for handling the bussiness logic&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Database&lt;/strong&gt;: The backend needs to store information in a database&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Frontend&lt;/strong&gt;: Website that will display information for the users. It&apos;s always communicating with the backend to get data.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Once again, this is the basic infrastructure. We are missing several other parts (e.g. caching, load balancing, authentication/authorization, observability...) that are still relevant but out of the scope of this blog post.&lt;/p&gt;
&lt;p&gt;The classic (not talking about the old days) approach for deploying this infrastructure is:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Deploy the Database (SQL - such as PostgreSQL - or NoSQL - such as MongoDB)&lt;/li&gt;
&lt;li&gt;Deploy the API and expose it by an API Gateway/Load Balancer (e.g. Kong, NGINX)&lt;/li&gt;
&lt;li&gt;Deploy the frontend by exposing the assets via a web server (e.g. NGINX)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is a perfect approach, nothing is wrong but notice that this involves having a server running 100% of the time to respond to incoming requests (shutting down all the servers to reduce costs means downtime for clients) and you also will need to manage scalability (if a sudden spike of traffic comes, you need to spin up more servers).&lt;/p&gt;
&lt;p&gt;Once again, this might be a huge cost for a new startup that barely has clients. Here is a more cost-effective approach:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Use a serverless, pay for what you use, database: DynamoDB. Notice that this is a NoSQL database and might not fit the needs of your application. For new accounts, AWS offers a 12-month free tier for RDS small databases, this might be a good alternative.&lt;/li&gt;
&lt;li&gt;Deploy the API using Lambda Function and expose it using API Gateway, you only pay for the traffic, storage of Lambda code (ECR or S3 zip object), and Lambda execution. For Lambda, AWS offers a monthly always-free tier of 1 million requests and up to 400,000 GB-seconds of execution (which means 111 hours of execution in a month when using a 1GB X86 architecture Lambda). Amazon API Gateway has a 12-month free tier of 1 million requests per month, but even without the free tier, the cost is $1.00 per million requests.&lt;/li&gt;
&lt;li&gt;Deploy the frontend using Amazon S3 for storing the website assets and distributing it at edge locations using CloudFront. The always free tier of CloudFront is great, with 1TB of data transfer out and 10 million requests per month. S3 will be basically free since we expect to cache all the static content in CloudFront and the size of the website assets is small.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/eaf1163dec5bc6338ae3e8aba93b1313/34431/architecture.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 46.202531645569614%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAYAAAAywQxIAAAACXBIWXMAAAsTAAALEwEAmpwYAAABWUlEQVR42oVSi3KDIBDM/39gpml0zKNRIloSRV4C2wObpJO0U5wblvPYu9tjhaflvUfXdVBKZaykRF1U6M4c42WAmTR0Mpl2tWCy7B8lVs+EIQQ45zKOiwOnWqDmF1jrEMjpw2I3PBPwhK0yr4SpKm008XjMfkbM5xnGGEg5Yp7nHGeMxUAV3c6J0CTCYRjocqTsFpxzaK1w2DXg7EKtU2VHBqMtjLPo2Bl9yzOB6DsI3kCIK8U0uRszEWFLJCmLJK3KsoSbHcrtAcd9g9OpQVXsESh9iBGn/QHVtsi4ZTWaYwVGSTbr9wdhjPFFw1RtWo6qctSqcwH5IxnS/6SuJZ01xaXbnvx/avhzKInYKA1FmdflG8RV3ONE34J97JZDjHlA5j/C25SVstiUBXrx+SAUAuzMvuPiYyi/EaY3mHRNbTmaphokaawwjROmtJNJwnKY7j41qhz3BQx0vFy4eu2iAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;AWS Architecture showing CloudFront, DynamoDB, API Gateway, Lambda, Route53 and ECR&quot;
        title=&quot;&quot;
        src=&quot;/static/eaf1163dec5bc6338ae3e8aba93b1313/f058b/architecture.png&quot;
        srcset=&quot;/static/eaf1163dec5bc6338ae3e8aba93b1313/c26ae/architecture.png 158w,
/static/eaf1163dec5bc6338ae3e8aba93b1313/6bdcf/architecture.png 315w,
/static/eaf1163dec5bc6338ae3e8aba93b1313/f058b/architecture.png 630w,
/static/eaf1163dec5bc6338ae3e8aba93b1313/40601/architecture.png 945w,
/static/eaf1163dec5bc6338ae3e8aba93b1313/78612/architecture.png 1260w,
/static/eaf1163dec5bc6338ae3e8aba93b1313/34431/architecture.png 1539w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2&gt;Serverless&lt;/h2&gt;
&lt;p&gt;Serverless, by definition, is the lack of servers, which is contradictory because you must have CPU, Memory, and Disk in order to run any software. So, what does it mean? Well, it basically means that your application will run in a way that you won&apos;t even see the servers running your application; you won&apos;t need to manage the virtual servers running or code; and you won&apos;t need to worry about managing failures, restarts and scalability.&lt;/p&gt;
&lt;p&gt;Serverless was highly related to &quot;pay as you go&quot; and &quot;pay by what you use&quot;. Meaning that, if you don&apos;t use it, you won&apos;t pay for it. This is partially true since we have serverless products in AWS that will charge you even if you are not using them (e.g. RDS Serverless, Fargate). So that&apos;s why I rather stick with the &quot;you don&apos;t manage servers&quot; instead of thinking it&apos;s free if you don&apos;t use it.&lt;/p&gt;
&lt;p&gt;Cloud is to On-Premise as Virtual Servers is to Serverless. If you understand this sentence then you understand the serverless concept.&lt;/p&gt;
&lt;h2&gt;Frontend: S3 + CloudFront&lt;/h2&gt;
&lt;p&gt;I&apos;ve already talked about S3 and CloudFront to deploy a website in a previous blog post called &lt;a href=&quot;https://www.felipetrindade.com/static-website-s3-cloudfront/&quot;&gt;Deploying a static website on AWS using S3 and CloudFront&lt;/a&gt; but I will talk about it again. I highly recommend you read the &lt;code class=&quot;language-text&quot;&gt;CloudFront&lt;/code&gt; section and subsections. Be aware that this method of deploying a website only works if your Javascript code is CRS (Client Side Rendering), so you can&apos;t use frameworks that are SSR (e.g. &lt;a href=&quot;https://nextjs.org/&quot;&gt;NextJS&lt;/a&gt;), only CSR (e.g. &lt;a href=&quot;https://react.dev/&quot;&gt;React&lt;/a&gt;). Another super important thing to mention is that when using SSL/TLS certificates with CloudFront you must &lt;a href=&quot;https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/cnames-and-https-requirements.html&quot;&gt;issue the certificate in the us-east-1 region&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;There are two easy ways of deploying a website using S3:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.aws.amazon.com/AmazonS3/latest/userguide/EnableWebsiteHosting.html&quot;&gt;S3 with website hosting configuration enabled&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/DownloadDistS3AndCustomOrigins.html#concept_S3Origin&quot;&gt;CloudFront with S3 origin&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I could make a comparison between these methods but I really don&apos;t see a reason why you would prefer only S3 with website hosting configuration enabled. Why? Well, check the disadvantages of using this method:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It does not support HTTPS. This is a big NO, but let&apos;s keep going...&lt;/li&gt;
&lt;li&gt;Limited to a specific location, which may cause delays to users in different geo locations&lt;/li&gt;
&lt;li&gt;Bad SEO&lt;/li&gt;
&lt;li&gt;S3 bucket needs to be public&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Because of that I really don&apos;t think it&apos;s worth it to even use the S3 website hosting option when you can use the CloudFront with S3 origin as an alternative. With the latter you have all the benefits that are cons in S3 website hosting plus:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Advanced caching behavior&lt;/li&gt;
&lt;li&gt;Geographic restriction&lt;/li&gt;
&lt;li&gt;Custom headers (good for SEO, and security)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So the idea here is to create a CloudFront distribution and select the S3 bucket as origin. This bucket will contain the build of our frontend website. When a distribution gets created you will get a URL (something like &lt;code class=&quot;language-text&quot;&gt;d111111abcdef8.cloudfront.net&lt;/code&gt;) that is your endpoint for accessing your website. To simplify the way of accesing your website, you can create an A record (with alias) pointing to this distribution (e.g. &lt;code class=&quot;language-text&quot;&gt;app.mydomain.com&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;Let&apos;s take a step back and talk about how CloudFront accesses the origin (S3 bucket in our case). In a single CloudFront distribution, you can have several origins (e.g. ELB, API Gateway, S3 bucket) and the way you control which origin will be used is by controlling the behavior of the distribution and you can define access patterns based on the &lt;a href=&quot;https://docs.aws.amazon.com/cloudfront/latest/APIReference/API_CacheBehavior.html&quot;&gt;URL path&lt;/a&gt; to decide what origin will be used. You can also specify a default behavior, which, in our use case it&apos;s enough, since there is only a single origin in our distribution.&lt;/p&gt;
&lt;p&gt;When controlling the behavior we can specify other things, such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Protocol policy&lt;/strong&gt;: HTTP and HTTPS; redirect HTTP to HTTPS; only HTTPS.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Allowed HTTP methods&lt;/strong&gt;: GET, HEAD; GET, HEAD, OPTIONS; GET, HEAD, OPTIONS, PUT, POST, PATCH, DELETE.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Caching Policy&lt;/strong&gt; You can use a custom or a managed caching policy (that defines TTL, Headers, Cookies, and Queries string)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Function Association&lt;/strong&gt;: CloudFront Functions or Lambda@Edge functions to run based on the response/request. It&apos;s useful if you would like to run operations such as URL redirects/rewrites, manipulate the header, access the HTTP body request... I&apos;ve used Lambda@Edge in a previous &lt;a href=&quot;https://www.felipetrindade.com/static-website-s3-cloudfront/&quot;&gt;blog post&lt;/a&gt; to perform URL rewrite!&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Backend: API Gateway + Lambda&lt;/h2&gt;
&lt;p&gt;Lambda offers a way of running a code for a short (up to 15 minutes) duration and you only pay for the storage of the function (in S3 as a ZIP file or in ECR as a Docker Image) and the duration of the Lambda execution. This becomes very interesting when you are working with API, which usually takes a sub-second response time. By default, the &lt;a href=&quot;https://repost.aws/knowledge-center/lambda-concurrency-limit-increase&quot;&gt;default concurrency Lambda limit&lt;/a&gt; is 1000, i.e. you can have up to 1000 Lambdas running at the same time in your account, but you can increase this values since it&apos;s a soft-limit.&lt;/p&gt;
&lt;p&gt;API Gateway offers a secure HTTP endpoint for invoking your Lambda function, while also managing high call volumes through traffic throttling and automating the validation and authorization of API requests. The idea is to proxy all requests to the Lambda. An API Gateway can be one of the following types: HTTP, REST, and WebSocket. The first two are suitable for your API use case and the REST API is basically an HTTP with batteries included (API keys, per-client throttling, AWS WAF integration, caching...). REST API Gateway can also be deployed privately and at the edge, while HTTP is only available publicly in a regional deployment. The REST API comes with more features but it comes with a price, at the first tier of requests (~300million per month - which is likely to be your use-case) it costs $3.5/million of requests, while the HTTP costs a fraction of it: $1/million of requests. To summarize, HTTP API Gateway is cheaper and has lower latency, while REST API Gateway offers advanced features but it&apos;s more expensive.&lt;/p&gt;
&lt;p&gt;One of the great advantages of using Lambdas is the famous &quot;cold start&quot; that can indeed make your application slower. Cold start varies from under 100ms to over 1s but typically it&apos;s under 1s, it depends on several things, such as the number of dependencies, the amount of RAM allocated to the function, the programming language used...&lt;/p&gt;
&lt;p&gt;You would need to adapt your code to be &quot;Lambda-compliant&quot; which is something that varies based on the programming language used, so the best thing to do is to directly check the &lt;a href=&quot;https://docs.aws.amazon.com/lambda/latest/dg/welcome.html&quot;&gt;documentation&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Remember that our API Gateway will receive requests from a browser, so we need to be aware of &lt;a href=&quot;https://docs.aws.amazon.com/apigateway/latest/developerguide/how-to-cors.html&quot;&gt;CORS&lt;/a&gt;, which means that your code needs to return some headers for the OPTIONS method (more on that in the code).&lt;/p&gt;
&lt;h2&gt;Database: DynamoDB&lt;/h2&gt;
&lt;p&gt;DynamoDB is a blazing-fast NoSQL key-value database proprietary service of AWS. It&apos;s completely serverless, you don&apos;t need to manage anything, you use the service as it is and only pay for storage and transactions (read and writes) in the table. When I say blazing fast I really mean it, it offers a single-digit millisecond latency but it&apos;s an expensive service, especially if you do not design your table correctly.&lt;/p&gt;
&lt;p&gt;A table in DynamoDB is schemaless, but you need to define a primary key (hash key) that must be unique in the entire table. The primary key can be a single key (partition key) or a composite key (partition key and sort key). The design of a DynamoDB table is completely off this discussion but to make you curious, it&apos;s a common strategy to denormalize data, single there are no joins in DynamoDB, so it&apos;s a completely different way of design when compared to common SQL tables. Because you pay for reading/writing in the table, a well-designed is so important, since you definitely don&apos;t want to avoid scan operation.&lt;/p&gt;
&lt;h2&gt;Demo Time&lt;/h2&gt;
&lt;h3&gt;Infrastructure as Code&lt;/h3&gt;
&lt;p&gt;Let&apos;s create the entire infrastructure of this code using Terraform! I decided to maximize the amount of community modules when creating the resources to make the code easier to read. If you rather check some &quot;pure&quot; Terraform code you can check &lt;a href=&quot;https://github.com/felipelaptrin/serverless-infra&quot;&gt;this repository&lt;/a&gt; that has a similar infrastructure to the one we are creating right now.&lt;/p&gt;
&lt;p&gt;Let&apos;s define the providers (AWS) that we will use in your terraform code. Notice that I&apos;ve defined a specific AWS provider to the &lt;code class=&quot;language-text&quot;&gt;us-east-1&lt;/code&gt; region (do you rember the TLS/SSL certificates when using CloudFront?).&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;hcl&quot;&gt;&lt;pre class=&quot;language-hcl&quot;&gt;&lt;code class=&quot;language-hcl&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# providers.tf&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;provider&lt;span class=&quot;token type variable&quot;&gt; &quot;aws&quot; &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;region&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; var.aws_region

  &lt;span class=&quot;token keyword&quot;&gt;default_tags&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;tags&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;Project&lt;/span&gt;    &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Serverless Infra&quot;&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;Repository&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https://github.com/felipelaptrin/serverless-infra-blog&quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;provider&lt;span class=&quot;token type variable&quot;&gt; &quot;aws&quot; &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;alias&lt;/span&gt;  &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;us_east_1&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;region&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;us-east-1&quot;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;default_tags&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;tags&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;Project&lt;/span&gt;    &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Serverless Infra&quot;&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;Repository&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https://github.com/felipelaptrin/serverless-infra-blog&quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Below, I declared all the variables that will be used, notice that only two variables are needed: &lt;code class=&quot;language-text&quot;&gt;domain&lt;/code&gt; (your domain) and &lt;code class=&quot;language-text&quot;&gt;frontend_bucket_name&lt;/code&gt; (the bucket name that will contain the frontend assets).&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;hcl&quot;&gt;&lt;pre class=&quot;language-hcl&quot;&gt;&lt;code class=&quot;language-hcl&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# variables.tf&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;########################################&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;##### GENERAL&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;########################################&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;variable&lt;span class=&quot;token type variable&quot;&gt; &quot;aws_region&quot; &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;type&lt;/span&gt;        &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; string
  &lt;span class=&quot;token property&quot;&gt;description&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;AWS region to deploy the infrastructure&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;default&lt;/span&gt;     &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;us-east-1&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;variable&lt;span class=&quot;token type variable&quot;&gt; &quot;domain&quot; &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;type&lt;/span&gt;        &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; string
  &lt;span class=&quot;token property&quot;&gt;description&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;The domain of your project&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;variable&lt;span class=&quot;token type variable&quot;&gt; &quot;logs_retention&quot; &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;type&lt;/span&gt;        &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; number
  &lt;span class=&quot;token property&quot;&gt;description&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;CloudWatch Group log retention&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;default&lt;/span&gt;     &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;90&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;########################################&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;##### NETWORKING&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;########################################&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;variable&lt;span class=&quot;token type variable&quot;&gt; &quot;vpc_deploy&quot; &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;type&lt;/span&gt;        &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; bool
  &lt;span class=&quot;token property&quot;&gt;description&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Controls the deployment of the VPC resources (VPC, Subnets, Internet Gateway, Route Table...). If you already have a VPC deployed, set this variable to false and set &apos;vpc_id&apos; variable.&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;default&lt;/span&gt;     &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;variable&lt;span class=&quot;token type variable&quot;&gt; &quot;vpc_id&quot; &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;type&lt;/span&gt;        &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; string
  &lt;span class=&quot;token property&quot;&gt;description&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;VPC ID of the already deployed VPC in your account. To use this, set vpc_deploy to false.&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;default&lt;/span&gt;     &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;variable&lt;span class=&quot;token type variable&quot;&gt; &quot;vpc_name&quot; &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;type&lt;/span&gt;        &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; string
  &lt;span class=&quot;token property&quot;&gt;description&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Name of the VPC to deploy&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;default&lt;/span&gt;     &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;PoC&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;variable&lt;span class=&quot;token type variable&quot;&gt; &quot;vpc_cidr&quot; &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;type&lt;/span&gt;        &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; string
  &lt;span class=&quot;token property&quot;&gt;description&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;CIDR of the VPC to create. Please use a /16 mask for high compatibility with this module.&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;default&lt;/span&gt;     &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;10.50.0.0/16&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;########################################&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;##### BACKEND&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;########################################&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;variable&lt;span class=&quot;token type variable&quot;&gt; &quot;backend_subdomain&quot; &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;type&lt;/span&gt;        &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; string
  &lt;span class=&quot;token property&quot;&gt;description&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Subdomain where the API Gateway will be exposed, i.e. https://{backend_subdomain}/{domain}&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;default&lt;/span&gt;     &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;api&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;variable&lt;span class=&quot;token type variable&quot;&gt; &quot;lambda_name&quot; &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;type&lt;/span&gt;        &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; string
  &lt;span class=&quot;token property&quot;&gt;description&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Name of the Lambda Function&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;default&lt;/span&gt;     &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;backend-api&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;variable&lt;span class=&quot;token type variable&quot;&gt; &quot;lambda_memory&quot; &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;type&lt;/span&gt;        &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; number
  &lt;span class=&quot;token property&quot;&gt;description&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Amount of memory that should be used in the Lambda&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;default&lt;/span&gt;     &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;256&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;variable&lt;span class=&quot;token type variable&quot;&gt; &quot;lambda_architecture&quot; &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;type&lt;/span&gt;        &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; string
  &lt;span class=&quot;token property&quot;&gt;description&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Architecture that the Lambda function will run&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;default&lt;/span&gt;     &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;arm64&quot;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;validation&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;condition&lt;/span&gt;     &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; contains(&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;x86_64&quot;&lt;/span&gt;, &lt;span class=&quot;token string&quot;&gt;&quot;arm64&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;, var.lambda_architecture)
    &lt;span class=&quot;token property&quot;&gt;error_message&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Valid values for var: test_variable are: &apos;x86_64&apos; and &apos;arm64&apos;.&quot;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;variable&lt;span class=&quot;token type variable&quot;&gt; &quot;lambda_timeout&quot; &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;type&lt;/span&gt;        &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; number
  &lt;span class=&quot;token property&quot;&gt;description&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Timeout in seconds of the Lambda&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;default&lt;/span&gt;     &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;########################################&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;##### DATABASE&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;########################################&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;variable&lt;span class=&quot;token type variable&quot;&gt; &quot;table_name&quot; &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;type&lt;/span&gt;        &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; string
  &lt;span class=&quot;token property&quot;&gt;description&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Name of the DynamoDB table&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;default&lt;/span&gt;     &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;table&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;variable&lt;span class=&quot;token type variable&quot;&gt; &quot;table_attributes&quot; &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;type&lt;/span&gt;        &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; list(map(string))
  &lt;span class=&quot;token property&quot;&gt;description&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Attributes of the DynamoDB table&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;UserId&quot;&lt;/span&gt;,
      &lt;span class=&quot;token property&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;S&quot;&lt;/span&gt;,
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;,
  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;variable&lt;span class=&quot;token type variable&quot;&gt; &quot;table_hash_key&quot; &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;type&lt;/span&gt;        &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; string
  &lt;span class=&quot;token property&quot;&gt;description&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Hash key of the DynamodDB table&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;default&lt;/span&gt;     &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;UserId&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;########################################&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;##### FRONTEND&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;########################################&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;variable&lt;span class=&quot;token type variable&quot;&gt; &quot;frontend_subdomain&quot; &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;type&lt;/span&gt;        &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; string
  &lt;span class=&quot;token property&quot;&gt;description&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Subdomain that the Website will be exposed, i.e. https://{frontend_subdomain}/{domain}&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;default&lt;/span&gt;     &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;app&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;variable&lt;span class=&quot;token type variable&quot;&gt; &quot;frontend_bucket_name&quot; &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;type&lt;/span&gt;        &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; string
  &lt;span class=&quot;token property&quot;&gt;description&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Name of the S3 bucket that will contains the frontend website&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now, let&apos;s check some &quot;helper&quot; files (data sources and locals).&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;hcl&quot;&gt;&lt;pre class=&quot;language-hcl&quot;&gt;&lt;code class=&quot;language-hcl&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# locals.tf&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;locals&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;vpc_id&lt;/span&gt;          &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; var.vpc_deploy &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt; ? module.vpc&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;.vpc_id : var.vpc_id
  &lt;span class=&quot;token property&quot;&gt;vpc_ip&lt;/span&gt;          &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; cidrhost(var.vpc_cidr, &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;)
  &lt;span class=&quot;token property&quot;&gt;all_subnets&lt;/span&gt;     &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;for i in range(&lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;) : cidrsubnet(var.vpc_cidr, &lt;span class=&quot;token number&quot;&gt;8&lt;/span&gt;, i + &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;)&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;public_subnets&lt;/span&gt;  &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; slice(local.all_subnets, &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;, &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;)
  &lt;span class=&quot;token property&quot;&gt;private_subnets&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; slice(local.all_subnets, &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;, &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;)
  &lt;span class=&quot;token property&quot;&gt;account_id&lt;/span&gt;      &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; data.aws_caller_identity.current.account_id
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;hcl&quot;&gt;&lt;pre class=&quot;language-hcl&quot;&gt;&lt;code class=&quot;language-hcl&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# data.tf&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;data &lt;span class=&quot;token type variable&quot;&gt;&quot;aws_caller_identity&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;current&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;data &lt;span class=&quot;token type variable&quot;&gt;&quot;aws_route53_zone&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;this&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; var.domain
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And finally, let&apos;s check the main code.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;hcl&quot;&gt;&lt;pre class=&quot;language-hcl&quot;&gt;&lt;code class=&quot;language-hcl&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# main.tf&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;########################################&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;##### NETWORKING&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;########################################&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;module&lt;span class=&quot;token type variable&quot;&gt; &quot;vpc&quot; &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;count&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; var.vpc_deploy &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt; ? &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; : &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;

  &lt;span class=&quot;token property&quot;&gt;source&lt;/span&gt;  &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;terraform-aws-modules/vpc/aws&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;version&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;5.15.0&quot;&lt;/span&gt;

  &lt;span class=&quot;token property&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; var.vpc_name
  &lt;span class=&quot;token property&quot;&gt;cidr&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; var.vpc_cidr

  &lt;span class=&quot;token property&quot;&gt;azs&lt;/span&gt;             &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token type variable&quot;&gt;aws_region&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;a&quot;&lt;/span&gt;, &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token type variable&quot;&gt;aws_region&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;b&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;private_subnets&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; local.private_subnets
  &lt;span class=&quot;token property&quot;&gt;public_subnets&lt;/span&gt;  &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; local.public_subnets
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;########################################&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;##### DATABASE&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;########################################&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;module&lt;span class=&quot;token type variable&quot;&gt; &quot;dynamodb-table&quot; &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;source&lt;/span&gt;  &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;terraform-aws-modules/dynamodb-table/aws&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;version&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;4.2.0&quot;&lt;/span&gt;

  &lt;span class=&quot;token property&quot;&gt;name&lt;/span&gt;         &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; var.table_name
  &lt;span class=&quot;token property&quot;&gt;attributes&lt;/span&gt;   &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; var.table_attributes
  &lt;span class=&quot;token property&quot;&gt;hash_key&lt;/span&gt;     &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; var.table_hash_key
  &lt;span class=&quot;token property&quot;&gt;billing_mode&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;PAY_PER_REQUEST&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;########################################&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;##### BACKEND&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;########################################&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;module&lt;span class=&quot;token type variable&quot;&gt; &quot;ecr&quot; &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;source&lt;/span&gt;  &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;terraform-aws-modules/ecr/aws&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;version&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;2.3.0&quot;&lt;/span&gt;

  &lt;span class=&quot;token property&quot;&gt;repository_name&lt;/span&gt;                 &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;backend-api&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;repository_image_tag_mutability&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;MUTABLE&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;repository_image_scan_on_push&lt;/span&gt;   &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;create_lifecycle_policy&lt;/span&gt;         &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;


&lt;span class=&quot;token keyword&quot;&gt;module&lt;span class=&quot;token type variable&quot;&gt; &quot;lambda_function&quot; &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;source&lt;/span&gt;  &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;terraform-aws-modules/lambda/aws&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;version&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;7.14.0&quot;&lt;/span&gt;

  &lt;span class=&quot;token property&quot;&gt;function_name&lt;/span&gt;                     &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; var.lambda_name
  &lt;span class=&quot;token property&quot;&gt;description&lt;/span&gt;                       &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Lambda Function based API serving as backend for the app&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;create_package&lt;/span&gt;                    &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;architectures&lt;/span&gt;                     &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;var.lambda_architecture&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;image_uri&lt;/span&gt;                         &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;module&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token type variable&quot;&gt;ecr&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;repository_url&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;:latest&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;package_type&lt;/span&gt;                      &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Image&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;cloudwatch_logs_retention_in_days&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; var.logs_retention

  &lt;span class=&quot;token property&quot;&gt;attach_network_policy&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;vpc_subnet_ids&lt;/span&gt;        &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; module.vpc&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;.private_subnets

  &lt;span class=&quot;token property&quot;&gt;memory_size&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; var.lambda_memory
  &lt;span class=&quot;token property&quot;&gt;timeout&lt;/span&gt;     &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; var.lambda_timeout
  &lt;span class=&quot;token property&quot;&gt;environment_variables&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;TABLE_NAME&lt;/span&gt;        &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; var.table_name
    &lt;span class=&quot;token property&quot;&gt;FRONTEND_ENDPOINT&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https://&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token type variable&quot;&gt;frontend_subdomain&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;.&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token type variable&quot;&gt;domain&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token property&quot;&gt;create_current_version_allowed_triggers&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;allowed_triggers&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;ApiGateway&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;service&lt;/span&gt;    &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;apigateway&quot;&lt;/span&gt;,
      &lt;span class=&quot;token property&quot;&gt;source_arn&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;arn:aws:execute-api:&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token type variable&quot;&gt;aws_region&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;:&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;local&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token type variable&quot;&gt;account_id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;:&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;module&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token type variable&quot;&gt;api_gateway&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;api_id&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;/*/*&quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token property&quot;&gt;attach_policy_json&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;policy_json&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; jsonencode(&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;Version&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;2012-10-17&quot;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;Statement&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;Action&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
          &lt;span class=&quot;token string&quot;&gt;&quot;dynamodb:Batch*&quot;&lt;/span&gt;,
          &lt;span class=&quot;token string&quot;&gt;&quot;dynamodb:GetItem&quot;&lt;/span&gt;,
          &lt;span class=&quot;token string&quot;&gt;&quot;dynamodb:PutItem&quot;&lt;/span&gt;,
          &lt;span class=&quot;token string&quot;&gt;&quot;dynamodb:UpdateItem&quot;&lt;/span&gt;,
          &lt;span class=&quot;token string&quot;&gt;&quot;dynamodb:Query&quot;&lt;/span&gt;,
          &lt;span class=&quot;token string&quot;&gt;&quot;dynamodb:Scan&quot;&lt;/span&gt;,
        &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;Effect&lt;/span&gt;   &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Allow&quot;&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;Resource&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;module&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token type variable&quot;&gt;dynamodb&lt;/span&gt;-table&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dynamodb_table_arn&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;,
    &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;)
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;module&lt;span class=&quot;token type variable&quot;&gt; &quot;api_gateway&quot; &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;source&lt;/span&gt;  &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;terraform-aws-modules/apigateway-v2/aws&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;version&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;5.2.0&quot;&lt;/span&gt;

  &lt;span class=&quot;token property&quot;&gt;name&lt;/span&gt;             &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;backend-gateway&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;description&lt;/span&gt;      &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;HTTP API Gateway for the Lambda-based API&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;protocol_type&lt;/span&gt;    &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;HTTP&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;domain_name&lt;/span&gt;      &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token type variable&quot;&gt;backend_subdomain&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;.&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token type variable&quot;&gt;domain&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;hosted_zone_name&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; var.domain
  &lt;span class=&quot;token property&quot;&gt;subdomains&lt;/span&gt;       &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;var.backend_subdomain&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;

  &lt;span class=&quot;token property&quot;&gt;cors_configuration&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;allow_headers&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;*&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;allow_methods&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;*&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;allow_origins&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;https://&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token type variable&quot;&gt;frontend_subdomain&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;.&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token type variable&quot;&gt;domain&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token property&quot;&gt;routes&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;$default&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;integration&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;uri&lt;/span&gt;                    &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;arn:aws:lambda:&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token type variable&quot;&gt;aws_region&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;:&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;local&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token type variable&quot;&gt;account_id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;:function:&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token type variable&quot;&gt;lambda_name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// module.lambda_function.lambda_function_arn&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;payload_format_version&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;2.0&quot;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;########################################&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;##### FRONTEND&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;########################################&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;module&lt;span class=&quot;token type variable&quot;&gt; &quot;s3_bucket&quot; &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;source&lt;/span&gt;  &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;terraform-aws-modules/s3-bucket/aws&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;version&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;4.2.2&quot;&lt;/span&gt;

  &lt;span class=&quot;token property&quot;&gt;bucket&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; var.frontend_bucket_name

  &lt;span class=&quot;token property&quot;&gt;attach_policy&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;policy&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; jsonencode(&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;Version&quot;&lt;/span&gt; : &lt;span class=&quot;token string&quot;&gt;&quot;2008-10-17&quot;&lt;/span&gt;,
    &lt;span class=&quot;token property&quot;&gt;&quot;Id&quot;&lt;/span&gt; : &lt;span class=&quot;token string&quot;&gt;&quot;PolicyForCloudFrontPrivateContent&quot;&lt;/span&gt;,
    &lt;span class=&quot;token property&quot;&gt;&quot;Statement&quot;&lt;/span&gt; : &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;Sid&quot;&lt;/span&gt; : &lt;span class=&quot;token string&quot;&gt;&quot;AllowCloudFrontServicePrincipal&quot;&lt;/span&gt;,
        &lt;span class=&quot;token property&quot;&gt;&quot;Effect&quot;&lt;/span&gt; : &lt;span class=&quot;token string&quot;&gt;&quot;Allow&quot;&lt;/span&gt;,
        &lt;span class=&quot;token property&quot;&gt;&quot;Principal&quot;&lt;/span&gt; : &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;token property&quot;&gt;&quot;Service&quot;&lt;/span&gt; : &lt;span class=&quot;token string&quot;&gt;&quot;cloudfront.amazonaws.com&quot;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;,
        &lt;span class=&quot;token property&quot;&gt;&quot;Action&quot;&lt;/span&gt; : &lt;span class=&quot;token string&quot;&gt;&quot;s3:GetObject&quot;&lt;/span&gt;,
        &lt;span class=&quot;token property&quot;&gt;&quot;Resource&quot;&lt;/span&gt; : &lt;span class=&quot;token string&quot;&gt;&quot;arn:aws:s3:::&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token type variable&quot;&gt;frontend_bucket_name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;/*&quot;&lt;/span&gt;,
        &lt;span class=&quot;token property&quot;&gt;&quot;Condition&quot;&lt;/span&gt; : &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;token property&quot;&gt;&quot;StringEquals&quot;&lt;/span&gt; : &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;&quot;AWS:SourceArn&quot;&lt;/span&gt; : &lt;span class=&quot;token string&quot;&gt;&quot;arn:aws:cloudfront::&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;local&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token type variable&quot;&gt;account_id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;:distribution/&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;module&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token type variable&quot;&gt;cdn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;cloudfront_distribution_id&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;
          &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;)

  &lt;span class=&quot;token property&quot;&gt;providers&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;aws&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; aws.us_east_1
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;module&lt;span class=&quot;token type variable&quot;&gt; &quot;acm&quot; &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;source&lt;/span&gt;  &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;terraform-aws-modules/acm/aws&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;version&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;5.1.1&quot;&lt;/span&gt;

  &lt;span class=&quot;token property&quot;&gt;domain_name&lt;/span&gt;       &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; var.domain
  &lt;span class=&quot;token property&quot;&gt;validation_method&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;DNS&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;zone_id&lt;/span&gt;           &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; data.aws_route53_zone.this.id

  &lt;span class=&quot;token property&quot;&gt;subject_alternative_names&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token type variable&quot;&gt;frontend_subdomain&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;.&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token type variable&quot;&gt;domain&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;,
  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;wait_for_validation&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;

  &lt;span class=&quot;token property&quot;&gt;providers&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;aws&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; aws.us_east_1
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;module&lt;span class=&quot;token type variable&quot;&gt; &quot;cdn&quot; &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;source&lt;/span&gt;  &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;terraform-aws-modules/cloudfront/aws&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;version&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;3.4.1&quot;&lt;/span&gt;

  &lt;span class=&quot;token property&quot;&gt;aliases&lt;/span&gt;             &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token type variable&quot;&gt;frontend_subdomain&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;.&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token type variable&quot;&gt;domain&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;comment&lt;/span&gt;             &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;CDN of Frontend&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;price_class&lt;/span&gt;         &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;PriceClass_All&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;is_ipv6_enabled&lt;/span&gt;     &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;default_root_object&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;index.html&quot;&lt;/span&gt;

  &lt;span class=&quot;token property&quot;&gt;create_origin_access_control&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;origin_access_control&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;s3_bucket_frontend&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;description&lt;/span&gt;      &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Frontend assets bucket&quot;&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;origin_type&lt;/span&gt;      &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;s3&quot;&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;signing_behavior&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;always&quot;&lt;/span&gt;,
      &lt;span class=&quot;token property&quot;&gt;signing_protocol&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;sigv4&quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;origin&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;s3&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;domain_name&lt;/span&gt;           &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token type variable&quot;&gt;frontend_bucket_name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;.s3.us-east-1.amazonaws.com&quot;&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;origin_access_control&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;s3_bucket_frontend&quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token property&quot;&gt;default_cache_behavior&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;target_origin_id&lt;/span&gt;       &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;s3&quot;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;viewer_protocol_policy&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;redirect-to-https&quot;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;allowed_methods&lt;/span&gt;        &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;DELETE&quot;&lt;/span&gt;, &lt;span class=&quot;token string&quot;&gt;&quot;GET&quot;&lt;/span&gt;, &lt;span class=&quot;token string&quot;&gt;&quot;HEAD&quot;&lt;/span&gt;, &lt;span class=&quot;token string&quot;&gt;&quot;OPTIONS&quot;&lt;/span&gt;, &lt;span class=&quot;token string&quot;&gt;&quot;PATCH&quot;&lt;/span&gt;, &lt;span class=&quot;token string&quot;&gt;&quot;POST&quot;&lt;/span&gt;, &lt;span class=&quot;token string&quot;&gt;&quot;PUT&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;cached_methods&lt;/span&gt;         &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;GET&quot;&lt;/span&gt;, &lt;span class=&quot;token string&quot;&gt;&quot;HEAD&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;cache_policy_id&lt;/span&gt;        &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;658327ea-f89d-4fab-a63d-7e88639e58f6&quot;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# CachingOptimized - Recommended for S3&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;use_forwarded_values&lt;/span&gt;   &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;compress&lt;/span&gt;               &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token property&quot;&gt;viewer_certificate&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;acm_certificate_arn&lt;/span&gt;      &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; module.acm.acm_certificate_arn
    &lt;span class=&quot;token property&quot;&gt;ssl_support_method&lt;/span&gt;       &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;sni-only&quot;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;minimum_protocol_version&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;TLSv1.2_2021&quot;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;module&lt;span class=&quot;token type variable&quot;&gt; &quot;records&quot; &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;source&lt;/span&gt;  &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;terraform-aws-modules/route53/aws//modules/records&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;version&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;4.1.0&quot;&lt;/span&gt;

  &lt;span class=&quot;token property&quot;&gt;zone_name&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; data.aws_route53_zone.this.name

  &lt;span class=&quot;token property&quot;&gt;records&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token type variable&quot;&gt;frontend_subdomain&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;A&quot;&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;alias&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;name&lt;/span&gt;    &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; module.cdn.cloudfront_distribution_domain_name
        &lt;span class=&quot;token property&quot;&gt;zone_id&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; module.cdn.cloudfront_distribution_hosted_zone_id
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;To recap, the following was created:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;VPC (for the entire project)&lt;/li&gt;
&lt;li&gt;CloudFront distribution (S3 bucket as origin)&lt;/li&gt;
&lt;li&gt;S3 bucket (frontend assets)&lt;/li&gt;
&lt;li&gt;API Gateway (Lambda as Target)&lt;/li&gt;
&lt;li&gt;ECR repository (store backend Docker image)&lt;/li&gt;
&lt;li&gt;Lambda (serve as Backend)&lt;/li&gt;
&lt;li&gt;ACM Certificates (for HTTPs in the API Gateway and CloudFront)&lt;/li&gt;
&lt;li&gt;Route53 records (for frontend and backend endpoints)&lt;/li&gt;
&lt;li&gt;DynamoDB table&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you try to run this for the first time it will fail. Why? Because the code will attempt to create an ECR repository and use the &lt;code class=&quot;language-text&quot;&gt;latest&lt;/code&gt; image in this repository to provision the Lambda. This is impossible to happen. What needs to be done is: create ECR repository -&gt; push docker image to repository -&gt; deploy Lambda function.&lt;/p&gt;
&lt;p&gt;A simple option to make this deployement work is: apply terraform -&gt; fail -&gt; push image to ECR repo -&gt; apply terraform again.&lt;/p&gt;
&lt;h3&gt;Backend Code Example (Golang)&lt;/h3&gt;
&lt;p&gt;I will show a quick Golang code that when receives a POST request adds records in the DynamoDB table and OPTIONS for the CORS.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;go&quot;&gt;&lt;pre class=&quot;language-go&quot;&gt;&lt;code class=&quot;language-go&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;package&lt;/span&gt; main

&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;token string&quot;&gt;&quot;context&quot;&lt;/span&gt;
  &lt;span class=&quot;token string&quot;&gt;&quot;encoding/json&quot;&lt;/span&gt;
  &lt;span class=&quot;token string&quot;&gt;&quot;fmt&quot;&lt;/span&gt;
  &lt;span class=&quot;token string&quot;&gt;&quot;log&quot;&lt;/span&gt;
  &lt;span class=&quot;token string&quot;&gt;&quot;os&quot;&lt;/span&gt;
  &lt;span class=&quot;token string&quot;&gt;&quot;time&quot;&lt;/span&gt;

  &lt;span class=&quot;token string&quot;&gt;&quot;github.com/aws/aws-lambda-go/events&quot;&lt;/span&gt;
  &lt;span class=&quot;token string&quot;&gt;&quot;github.com/aws/aws-lambda-go/lambda&quot;&lt;/span&gt;
  &lt;span class=&quot;token string&quot;&gt;&quot;github.com/aws/aws-sdk-go-v2/aws&quot;&lt;/span&gt;
  &lt;span class=&quot;token string&quot;&gt;&quot;github.com/aws/aws-sdk-go-v2/config&quot;&lt;/span&gt;
  &lt;span class=&quot;token string&quot;&gt;&quot;github.com/aws/aws-sdk-go-v2/service/dynamodb&quot;&lt;/span&gt;
  &lt;span class=&quot;token string&quot;&gt;&quot;github.com/aws/aws-sdk-go-v2/service/dynamodb/types&quot;&lt;/span&gt;
  &lt;span class=&quot;token string&quot;&gt;&quot;github.com/aws/aws-sdk-go-v2/service/sts&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; User &lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  UserId    &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;`json:&quot;userId&quot;`&lt;/span&gt;
  Name      &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;`json:&quot;name&quot;`&lt;/span&gt;
  Email     &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;`json:&quot;email&quot;`&lt;/span&gt;
  CreatedAt &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;`json:&quot;createdAt&quot;`&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getEnv&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; fallback &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  value&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; exists &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; os&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;LookupEnv&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;exists &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; fallback
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; value
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; dynamoClient &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;dynamodb&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Client
&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; tableName &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getEnv&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;TABLE_NAME&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;table&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  cfg&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; err &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; config&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;LoadDefaultConfig&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;TODO&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; config&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;WithRegion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getEnv&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;AWS_REGION&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;us-east-1&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; err &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    log&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Fatalf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Failed to load AWS config: %v&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; err&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  dynamoClient &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; dynamodb&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;NewFromConfig&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;cfg&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  stsClient &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; sts&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;NewFromConfig&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;cfg&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  identity&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; err &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; stsClient&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;GetCallerIdentity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;TODO&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;sts&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;GetCallerIdentityInput&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; err &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    log&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Printf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Failed to get caller identity: %v&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; err&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    log&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Printf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Lambda is running as: %s&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;identity&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Arn&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;handler&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;request events&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;APIGatewayV2HTTPRequest&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;events&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;APIGatewayProxyResponse&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;RequestContext&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;HTTP&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Method &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;OPTIONS&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; events&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;APIGatewayProxyResponse&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      Headers&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&quot;Access-Control-Allow-Headers&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Content-Type&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&quot;Access-Control-Allow-Origin&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;  os&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Getenv&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;FRONTEND_ENDPOINT&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&quot;Access-Control-Allow-Methods&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;OPTIONS,POST,GET&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      StatusCode&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;nil&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;RequestContext&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;HTTP&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Method &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;POST&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;createUser&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; events&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;APIGatewayProxyResponse&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    Body&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;       &lt;span class=&quot;token string&quot;&gt;&quot;Method not supported&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    StatusCode&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;405&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;nil&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;createUser&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;request events&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;APIGatewayV2HTTPRequest&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;events&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;APIGatewayProxyResponse&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; user User

  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; err &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; json&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Unmarshal&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;byte&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Body&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;user&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; err &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; events&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;APIGatewayProxyResponse&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      Body&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;       fmt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Sprintf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Invalid request body: %v&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; err&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      StatusCode&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;400&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;nil&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;UserId &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; events&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;APIGatewayProxyResponse&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      Body&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;       &lt;span class=&quot;token string&quot;&gt;&quot;UserId must be provided&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      StatusCode&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;400&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;nil&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  getItemOutput&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; err &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; dynamoClient&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;GetItem&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;TODO&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;dynamodb&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;GetItemInput&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    TableName&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; aws&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;tableName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    Key&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;types&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;AttributeValue&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token string&quot;&gt;&quot;UserId&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;types&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;AttributeValueMemberS&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;Value&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;UserId&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; err &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; events&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;APIGatewayProxyResponse&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      Body&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;       fmt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Sprintf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Error checking for existing user: %v&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; err&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      StatusCode&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;500&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;nil&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; getItemOutput&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Item &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; events&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;APIGatewayProxyResponse&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      Body&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;       &lt;span class=&quot;token string&quot;&gt;&quot;User already exists&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      StatusCode&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;400&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;nil&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;CreatedAt &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; time&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Now&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;UTC&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;time&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;RFC3339&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  item &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;types&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;AttributeValue&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;UserId&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;    &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;types&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;AttributeValueMemberS&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;Value&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;UserId&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;Name&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;      &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;types&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;AttributeValueMemberS&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;Value&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Name&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;Email&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;     &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;types&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;AttributeValueMemberS&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;Value&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Email&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;CreatedAt&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;types&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;AttributeValueMemberS&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;Value&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;CreatedAt&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token boolean&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; err &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; dynamoClient&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;PutItem&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;TODO&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;dynamodb&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;PutItemInput&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    TableName&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; aws&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;tableName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    Item&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;      item&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; err &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; events&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;APIGatewayProxyResponse&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      Body&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;       fmt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Sprintf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Failed to save user: %v&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; err&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      StatusCode&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;500&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;nil&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  responseBody&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; json&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Marshal&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;message&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;User created successfully&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;userId&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;  user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;UserId&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; events&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;APIGatewayProxyResponse&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    Body&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;       &lt;span class=&quot;token function&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;responseBody&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    StatusCode&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;201&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;nil&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  lambda&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Start&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;handler&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;Cya!&lt;/h2&gt;
&lt;p&gt;Well, I hope you liked this blog post. See you around! 👋&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Email, protocols, and Amazon SES]]></title><description><![CDATA[The goal of today's post is to comment more about the Amazon SES service, email infrastructure, protocols, and domain. The entire source…]]></description><link>https://felipetrindade.com/ses/</link><guid isPermaLink="false">https://felipetrindade.com/ses/</guid><pubDate>Fri, 08 Nov 2024 12:55:32 GMT</pubDate><content:encoded>&lt;p&gt;The goal of today&apos;s post is to comment more about the Amazon SES service, email infrastructure, protocols, and domain. The entire source code of this demo is available in my &lt;a href=&quot;https://github.com/felipelaptrin/email-ses&quot;&gt;GitHub repository&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;How does email work?&lt;/h2&gt;
&lt;p&gt;Before we dive deep into Amazon SES, we need to take a step back and understand how email works, its concepts, and the protocols involved in the process.&lt;/p&gt;
&lt;p&gt;Well, you know what an email is: an electronic message, a &quot;digital letter.&quot; But how am I able to send a message from my personal email (e.g., &lt;code class=&quot;language-text&quot;&gt;myemail@something.com&lt;/code&gt;) to yours (e.g., &lt;code class=&quot;language-text&quot;&gt;youremail@otherthing.com&lt;/code&gt;)? Let&apos;s deep-dive.&lt;/p&gt;
&lt;p&gt;When you create an email account in an email provider (e.g., Gmail, Outlook, Yahoo), you are now relying on the provider&apos;s infrastructure to handle email operations, such as receiving, sending, filtering/detecting spam, etc. Under the hood, they use several protocols to make sure everything works fine for you.&lt;/p&gt;
&lt;h3&gt;Protocol for sending emails&lt;/h3&gt;
&lt;p&gt;The standard protocol for sending mail is known as SMTP (Simple Mail Transfer Protocol). It is a network protocol that defines how servers should exchange information to send mail to another server. It creates a standard in the way the emails should navigate the internet from the sender to the recipient. Notice that SMTP is NOT a protocol that specifies how the mail should be received, this belongs to other protocols that will be covered in the next section.&lt;/p&gt;
&lt;p&gt;The SMTP protocol was specified in several different RFC (publications about the base of the internet), such as &lt;a href=&quot;https://datatracker.ietf.org/doc/html/rfc772&quot;&gt;RFC 772&lt;/a&gt;, &lt;a href=&quot;https://datatracker.ietf.org/doc/html/rfc780&quot;&gt;RFC 780&lt;/a&gt;, &lt;a href=&quot;https://datatracker.ietf.org/doc/html/rfc788&quot;&gt;RFC 788&lt;/a&gt; and &lt;a href=&quot;https://datatracker.ietf.org/doc/html/rfc821&quot;&gt;RFC 821&lt;/a&gt;. As time passed, bugs were found, errors were fixed more secure strategies were created and new RFCs were presented to enhance SMPT, such as &lt;a href=&quot;https://datatracker.ietf.org/doc/html/rfc5321&quot;&gt;RFC 5321&lt;/a&gt;. Could you imagine emails being sent without encryption (over TLS/SSL) these days? SMTP protocol was created more than 10 years before the invention of SSL, so that&apos;s why updates in these standards are so important.&lt;/p&gt;
&lt;p&gt;Now that we have the protocol in place we need software that implements this protocol, i.e. we need an SMTP server. There are plenty of open-source SMTP servers out there, and the most open-source famous one is &lt;a href=&quot;https://www.postfix.org/&quot;&gt;Postfix&lt;/a&gt;. As you probably know, ports are just numbers and you can basically run any service in any port (e.g. SSH by default works on port 22 but you can run in any other port) but there are standards that are always good to know and follow if possible. There are four &lt;a href=&quot;https://www.cloudflare.com/learning/email-security/smtp-port-25-587/#:~:text=Today%2C%20SMTP%20should%20instead%20use,should%20be%20used%20if%20possible.&quot;&gt;SMTP ports&lt;/a&gt; that you should know about:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;25&lt;/code&gt;: This is the default SMTP port and it&apos;s often blocked by your ISP to prevent spam.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;587&lt;/code&gt;: This is the standard SMTP port when used with TLS encryption (SMTPS).&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;465&lt;/code&gt;: This was the original port intended to be used with SMTPS but it&apos;s considered deprecated, although some providers may still offer this option.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;2525&lt;/code&gt;: In case the ports above are blocked (by ISP or firewall rules) it is commonly used this port.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When you open your Gmail email, for example, you are using the default Gmail&apos;s SMTP server (&lt;a href=&quot;https://developers.google.com/gmail/imap/imap-smtp&quot;&gt;smpt.gmail.com&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;Notice that for interacting with the SMTP server you need an SMTP client. When using Gmail, for example, everything is abstracted in the browser and you don&apos;t even realize these technicities. But you can also use SMTP programmatically, e.g. &lt;a href=&quot;https://docs.python.org/3/library/smtplib.html&quot;&gt;smtplib for Python&lt;/a&gt;, &lt;a href=&quot;https://linux.die.net/man/8/sendmail.sendmail&quot;&gt;sendmail&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Protocol for receiving emails&lt;/h3&gt;
&lt;p&gt;There are two main protocols for receiving mails: POP3 (Post Office Protocol version 3) and IMAP (Internet Message Access Protocol). I have already used the IMAP with Python in my &lt;a href=&quot;https://www.felipetrindade.com/email-forwarder/&quot;&gt;configuring an email forward to enhance your phone security in case of a robbery&lt;/a&gt; blog post.&lt;/p&gt;
&lt;p&gt;Similarly to the SMTP, POP3 and IMAP were proposed in several different RFCs that I will not go into detail. Both protocols have the same goal: retrieve mail, but they do that using different ways and approaches. The main difference between these protocols is that IMAP reads the emails from the email server without deleting them, while POP3 downloads and deletes the emails from the server, because of that, POP3 downloads the emails from the server allowing the emails to be accessible offline, while in IMAP it doesn&apos;t.&lt;/p&gt;
&lt;p&gt;With the protocol established we need, once again, software to implement it, following all the rules and conventions. By default, these servers use the following ports:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;143&lt;/code&gt;: IMAP.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;993&lt;/code&gt;: IMAP over SSL.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;110&lt;/code&gt;: POP3.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;995&lt;/code&gt;: POP3 over SSL.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There are several open-source software that implement these protocols, such as &lt;a href=&quot;https://github.com/nodemailer/wildduck&quot;&gt;Wildduck&lt;/a&gt; and &lt;a href=&quot;https://www.dovecot.org/&quot;&gt;Dovecot&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Domain&lt;/h3&gt;
&lt;p&gt;When you send someone an email (e.g. &lt;code class=&quot;language-text&quot;&gt;youremail@otherthing.com&lt;/code&gt;) how does it know &quot;how to get&quot; to your inbox? Domain resolution. Computers do not understand names, only IPs, that&apos;s why DNS exists. &lt;code class=&quot;language-text&quot;&gt;otherthing.com&lt;/code&gt; is a domain and the nameservers will tell you the IP of the email servers. If you have a domain and would like to setup an email you would need to configure some records in your DNS. Some records are important to be set in your domain such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;Email record&lt;/code&gt;: A &lt;code class=&quot;language-text&quot;&gt;MX&lt;/code&gt; record that will specify the mail server(s).&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;SPF&lt;/code&gt;: A &lt;code class=&quot;language-text&quot;&gt;TXT&lt;/code&gt; record used to specify what servers are authorized to send emails on behalf of your domain (the mail provider that will send the emails). It helps receiving servers verify that emails claiming to come from your domain are sent by legitimate sources.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;DKIM&lt;/code&gt;: A &lt;code class=&quot;language-text&quot;&gt;TXT&lt;/code&gt; record that contains the public key. The emails from your domain will be signed by your private key and people that receive the email can verify if it was signed by you by checking it using the public key. This is usually generated by the email provider and you only need to add to your DNS.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;DMARC&lt;/code&gt;: A &lt;code class=&quot;language-text&quot;&gt;TXT&lt;/code&gt; record that specifies what should email servers do (nothing/quarantine/discard) if they receive an email that failed SPF/DKIM checks.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Some of these records were set in my &lt;a href=&quot;https://www.felipetrindade.com/email-provider-for-domain/&quot;&gt;configuring a free email provider for your domain using AWS Route53 and Zoho&lt;/a&gt; blog post.&lt;/p&gt;
&lt;h3&gt;Missing pieces&lt;/h3&gt;
&lt;p&gt;The real email infrastructure with all components of a real provider (e.g. Gmail) is really big and complex but there are so many more parts that are not relevant for this post, such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Email Storage&lt;/strong&gt;: Store all the mail, and attachments somewhere that should be accessible by the user.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Anti-Spam and Anti-Virus filters&lt;/strong&gt;: Smart detection of phishing, false, and spam mail.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Web interface&lt;/strong&gt;: Access your inbox via browser.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;SES&lt;/h2&gt;
&lt;p&gt;Amazon SES (Simple Email Service) is a service responsible for sending emails using its interface/API or using SMTP. You may probably be thinking: &quot;Ok... can&apos;t I simply set up my own SMTP server and send emails using it?&quot;. Indeed you can, but there are a few catches: you would need to make sure your server is scalable and secure; would need to manage retries and assure high deliverability; would need to establish IP addresses with a good reputation (otherwise your emails will always arrive in the receipt spam folder); would need to set up feedback mechanism (bounced and undelivered emails). That&apos;s why services like Amazon SES, SendGrid, Mailchimp, and Mailgun exist, for you to focus only on your application.&lt;/p&gt;
&lt;p&gt;Be aware that SES by default puts your environment in a &lt;code class=&quot;language-text&quot;&gt;sandbox&lt;/code&gt; mode, where you can only send emails to emails verified by SES. This is ok for testing but for production, this makes no sense, so you should open a ticket to AWS informing that you would like to move out from the &lt;code class=&quot;language-text&quot;&gt;sandbox&lt;/code&gt; mode, specifying why and how you going to send email.&lt;/p&gt;
&lt;h3&gt;Identity&lt;/h3&gt;
&lt;p&gt;There are two ways of proving that you own the email (validate identity) you would like to send the emails from:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Domain validation&lt;/strong&gt;: To prove that you own the domain you will need to set up DKIM records. You can either choose &lt;code class=&quot;language-text&quot;&gt;Easy DKIM&lt;/code&gt; (AWS will generate a public-private key for you to use and will ask you to add the public key in your DNS) or &lt;code class=&quot;language-text&quot;&gt;BYODKIM&lt;/code&gt; (you provide the private key instead of relying on AWS to create one for you). And later you can add SPF and DMARC records to reduce the chances of your email going to the spam folder. It&apos;s very common for companies to use an email provider (e.g. Outlook, Google Workspace) with the domain (e.g. &lt;code class=&quot;language-text&quot;&gt;example.com&lt;/code&gt;) and create a subdomain for sending an email (e.g. &lt;code class=&quot;language-text&quot;&gt;mail.example.com&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;marketing.example.com&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Email address validation&lt;/strong&gt;: To prove that you own that email address, AWS will send a mail to the provided email and you should confirm it by clicking the confirmation link provided. By confirming that you own the email address, you are allowing Amazon SES to send emails using your email address in the &quot;From&quot; field.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There are a few differences in choosing one over another:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Set up complexity&lt;/strong&gt;: Email address identity is way faster to set up when compared to domain identity.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Deliverability&lt;/strong&gt;: Domain identity is always used to achieve optimal deliverability because the receipt can always validate if the email using DNS records. You can also send emails from any subdomain or email address of the domain identity, while when using email address identity you can only send from the specified email.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Sending emails using SES&lt;/h3&gt;
&lt;p&gt;When using Amazon SES there are two ways to send emails:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;API&lt;/strong&gt;: Amazon SES exposes a public API that you can use in three ways: making direct &lt;a href=&quot;https://docs.aws.amazon.com/ses/latest/APIReference/Welcome.html&quot;&gt;HTTPS requests&lt;/a&gt;, using AWS SDK or using &lt;a href=&quot;https://docs.aws.amazon.com/cli/latest/reference/ses/&quot;&gt;AWS CLI&lt;/a&gt;. The first one (HTTPS request) is less commonly used because you will need to handle authentication, &lt;a href=&quot;https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_sigv.html&quot;&gt;signing&lt;/a&gt; of requests and manually construct the request. The second one (AWS SDK) is very common, especially if your application is already running inside AWS (Lambda, ECS, EC2) and it&apos;s pretty easy to use with your favorite programming language (e.g. &lt;a href=&quot;https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/ses.html&quot;&gt;Python&lt;/a&gt;, &lt;a href=&quot;https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/javascript_ses_code_examples.html&quot;&gt;Javascript&lt;/a&gt;). The later (AWS CLI) is commonly used in scripts and testing. In all these ways you need to make sure that the IAM User/IAM Role that is sending the requests has the correct IAM permission.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SMTP interface&lt;/strong&gt;: The SMTP protocol can be used to send emails using SMTP protocol. You will need to specify the &lt;a href=&quot;https://docs.aws.amazon.com/general/latest/gr/ses.html&quot;&gt;SMTP server endpoint&lt;/a&gt;, the &lt;a href=&quot;https://docs.aws.amazon.com/ses/latest/dg/smtp-connect.html&quot;&gt;SMTP port&lt;/a&gt; and &lt;a href=&quot;https://docs.aws.amazon.com/ses/latest/dg/smtp-credentials.html&quot;&gt;SMTP user and password&lt;/a&gt; (create an IAM user that has permission to use Amazon SES and issue SMTP credentials).&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Demo time!&lt;/h2&gt;
&lt;p&gt;For this demo, I will use AWS CDK to create the needed infrastructure. You can also run this demo with me, you just need to adapt the inputs (e.g. your domain name). The only thing that I expect from you is that your AWS region is &lt;a href=&quot;https://docs.aws.amazon.com/cdk/v2/guide/bootstrapping.html&quot;&gt;CDK bootstrapped&lt;/a&gt; and the domain in Route53 is already configured (I will not configure this in this code because this domain is already being used in other personal demos). If you do not own a domain I highly recommend you buy a cheap one just for testing (you can get a cheap domain for $2/year or even less than that).&lt;/p&gt;
&lt;p&gt;I will use Amazon SES in a region that it&apos;s still in Sandbox mode, i.e. it can only send emails to identities verified in Amazon SES. So because of that, I will create two identities, an email address identity (personal email) and a domain identity (using the domain in Route53).&lt;/p&gt;
&lt;h3&gt;CDK Code&lt;/h3&gt;
&lt;p&gt;This will be a simple code that will use a single stack to create two email identities, the first will be validated using the domain, while the other is a personal email that will be validated using email address validation. Let&apos;s check the main (entry point) of our CDK project.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// src/main.ts&lt;/span&gt;
#&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;usr&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;bin&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;env ts&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;node
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;source-map-support/register&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; cdk &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;aws-cdk-lib&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; SesStack &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;./stack/ses&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; devProps &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;./config&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; app &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;cdk&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;App&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SesStack&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;app&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;SesStack&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; devProps&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Before checking the values of the stack let&apos;s first take a look at the stack definition.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// src/stack/ses.ts&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; Stack&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; StackProps&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Tags &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;aws-cdk-lib&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; Construct &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;constructs&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  DkimIdentity&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  EasyDkimSigningKeyLength&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  EmailIdentity&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  Identity&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  MailFromBehaviorOnMxFailure&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;aws-cdk-lib/aws-ses&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; HostedZone&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; TxtRecord &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;aws-cdk-lib/aws-route53&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SesStackProps&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;StackProps&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  domain&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  email&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SesStack&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Stack&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;scope&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Construct&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; id&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; props&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; SesStackProps&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;scope&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; props&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setEmailIdentity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;email&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setDomainIdentity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;domain&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    Tags&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;CreatedBy&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;CDK&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;setEmailIdentity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;email&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;EmailIdentity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;EmailIdentity&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
 identity&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Identity&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;email&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;email&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;setDomainIdentity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;domain&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; hostedZone &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; HostedZone&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fromLookup&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Zone&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; domainName&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; domain &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;EmailIdentity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;DomainIdentity&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
 identity&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Identity&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;publicHostedZone&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;hostedZone&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
 dkimIdentity&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; DkimIdentity&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;easyDkim&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;EasyDkimSigningKeyLength&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;RSA_2048_BIT&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
 mailFromBehaviorOnMxFailure&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; MailFromBehaviorOnMxFailure&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;USE_DEFAULT_VALUE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
 dkimSigning&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TxtRecord&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;DmarkRecord&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
 recordName&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;_dmarc&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
 values&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;v=DMARC1;p=quarantine&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
 zone&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; hostedZone&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TxtRecord&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;SpfRecord&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
 recordName&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
 values&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;v=spf1 include:amazonses.com -all&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
 zone&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; hostedZone&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And finally, let&apos;s check the values for this stack.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// src/config/dev.config.ts&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; SesStackProps &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;../stack/ses&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; devProps&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; SesStackProps &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
 domain&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;demosfelipetrindade.top&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
 email&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;yourpersonalemail@gmail.com&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
 env&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
 account&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;937168356724&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
 region&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;us-east-1&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;There you go! Deploying this stack is super easy and you only need to run &lt;code class=&quot;language-text&quot;&gt;cdk deploy&lt;/code&gt; with the appropriate IAM permissions to the desired account. Of course you will need to change these values to fit your use case.&lt;/p&gt;
&lt;p&gt;A few important points to mention:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Notice that in the code we didn&apos;t have to configure the DKIM record. Why? Actually, because my domain is being managed by Route53 I&apos;ve used the &lt;a href=&quot;https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_ses.Identity.html#static-publicwbrhostedwbrzonehostedzone&quot;&gt;publicHostedZone&lt;/a&gt; method of CDK that already setup the &lt;code class=&quot;language-text&quot;&gt;DKIM&lt;/code&gt; in the hosted zone. If you used the &lt;a href=&quot;https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_ses.Identity.html#static-domaindomain&quot;&gt;domain&lt;/a&gt; method the Amazon SES would provide you with the DKIM records for you to add in your DNS. Indeed, after deployment you will see the DNS records there:&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/74b9b7491d17a349778b8ca540c12fb8/6d74e/dkim-records.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 13.924050632911392%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAADCAYAAACTWi8uAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAn0lEQVR42j2NWQ6CQBAFuQwgwzALg6yyCQSCMUrU+x/l2YD4Uale8rotX8fQSYlTYOB4Cg5TsNnuA/tHEKbwRPQ3E2cwGW/Zw1ZWdZifb8yP125ivC/U7/Vt+Ww1p8dZ3SMnqm5CmFzoSESHDXRcbMgohZUWNa7DhLYf0RB1tzKgIa+ztqcd7U2SIysbMoUpyKWB60u4TIAFGr4I4XGFL3fwY5jousrBAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Screenshot of Route53 records showing three DKIM records&quot;
        title=&quot;&quot;
        src=&quot;/static/74b9b7491d17a349778b8ca540c12fb8/f058b/dkim-records.png&quot;
        srcset=&quot;/static/74b9b7491d17a349778b8ca540c12fb8/c26ae/dkim-records.png 158w,
/static/74b9b7491d17a349778b8ca540c12fb8/6bdcf/dkim-records.png 315w,
/static/74b9b7491d17a349778b8ca540c12fb8/f058b/dkim-records.png 630w,
/static/74b9b7491d17a349778b8ca540c12fb8/40601/dkim-records.png 945w,
/static/74b9b7491d17a349778b8ca540c12fb8/6d74e/dkim-records.png 1085w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You will need to confirm the personal email address identity in your inbox. It will be similar to the following.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/28c7444708d70958730dc0c152814731/eafa0/email-address-confirmation.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 60.75949367088608%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAAsTAAALEwEAmpwYAAAB80lEQVR42p1SyXLTQBD1n8IphFAVkipuNneuHLlw4cIHgYlDApIsL7NIMyPJsjb70d2Ww3JEVa96e9U96n4T0KeUQhRFSJIExmiEEFAUBay1FBvkeS7+GVmWwTknefa999jv94LJ4XAQUpqmyMi6okbfDyOhwTAM+Pfruk7qTdOIz+j7Htxrwg5P4In8slBUqHY7dJTnxkw6UpPjHw05x4O4zmBfeESaMCEED602sPS7WisYQvCOhuRSKwjn2JNlhOBoLR5VVY6ocOQXckObeaRrg9XWYs1QtIKNoZzGamOxXFuxHDOPOclKY6NylPWAuun/fuGHj5/x/OINrm7f4tXtDFc3U7y8mYl/eT3DxfVU8IJw+XoqvGfEf/f+ExbrIxLdwBYDqv34wpSmf7mLsHhY4tv3hBDjjv2HBPNFjPl9gq+E+f25TjHlHxMNV/JhWrRtezoKN6zrmvYRUJJUyvI3eDdlNcZU55h51Vjj/VprRHZaG7m0NNSUSOKItPgTy2VMfoyYwLrUow6zjGGlAVuXZ8hFg47kZiQezg3zUCPduieslZOJSm1F2FprGNLo1nhsdKA6W4elqvBj2yFSLdkW2o8NM7+DdXsUdLGwG+hy/ZPyGbwStpwvpH4CczkOu178pht3yDrzLjvthtCT8v/3+wU/4IA1GOT4mwAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Screenshot of the confirmation email address sent by Amazon SES&quot;
        title=&quot;&quot;
        src=&quot;/static/28c7444708d70958730dc0c152814731/f058b/email-address-confirmation.png&quot;
        srcset=&quot;/static/28c7444708d70958730dc0c152814731/c26ae/email-address-confirmation.png 158w,
/static/28c7444708d70958730dc0c152814731/6bdcf/email-address-confirmation.png 315w,
/static/28c7444708d70958730dc0c152814731/f058b/email-address-confirmation.png 630w,
/static/28c7444708d70958730dc0c152814731/eafa0/email-address-confirmation.png 939w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3&gt;Let&apos;s test!&lt;/h3&gt;
&lt;p&gt;We can easily test by sending a test email using AWS CLI or SMTP. Why not check how it works using both approaches?&lt;/p&gt;
&lt;h4&gt;Sending emails using Amazon SES API&lt;/h4&gt;
&lt;p&gt;Let&apos;s use a simple bash script for sending these emails:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token shebang important&quot;&gt;#!/bin/bash&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# PARAMETERS&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;SENDER&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;noreply@demosfelipetrindade.top&quot;&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;RECIPIENT&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;yourpersonalemail@gmail.com&quot;&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;SUBJECT&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Test Email from Amazon SES&quot;&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;BODY_HTML&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&amp;lt;html&gt;&amp;lt;body&gt;&amp;lt;h1&gt;Test Email&amp;lt;/h1&gt;&amp;lt;p&gt;This is an &amp;lt;strong&gt;Amazon SES&amp;lt;/strong&gt; test email!&amp;lt;/p&gt;&amp;lt;/body&gt;&amp;lt;/html&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;AWS_REGION&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;us-east-1&quot;&lt;/span&gt;

Amazon SES send-email &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
 &lt;span class=&quot;token parameter variable&quot;&gt;--region&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$AWS_REGION&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;token parameter variable&quot;&gt;--from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$SENDER&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
 &lt;span class=&quot;token parameter variable&quot;&gt;--destination&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;ToAddresses=&lt;span class=&quot;token variable&quot;&gt;$RECIPIENT&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;token parameter variable&quot;&gt;--message&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Subject={Data=&lt;span class=&quot;token variable&quot;&gt;$SUBJECT&lt;/span&gt;,Charset=utf-8},Body={Html={Data=&lt;span class=&quot;token variable&quot;&gt;$BODY_HTML&lt;/span&gt;,Charset=utf-8}}&quot;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$?&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-eq&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt;
    &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Email sent successfully!&quot;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt;
    &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Failed to send email.&quot;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;fi&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Notice that because I validated the entire domain I can send email using any email address within that domain, in this case, I&apos;m using &lt;code class=&quot;language-text&quot;&gt;noreply&lt;/code&gt;. After executing this script the following email will show up in your email inbox.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/fc0c022980ced6d26b2e4ef3010f2e92/078fe/noreply-email.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 27.21518987341772%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAFCAYAAABFA8wzAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAeUlEQVR42qWQSwqAMAxEe/9beQe37UoXRewnLkq/o8leUQxMQiC8DKOmOWPbI0LwIKJrBnjvUUrBXbXWkFIS5ZzlltV7h6oN2J3Duiyw1j6C3pTi5i6gMQZaa3HHDn4Bxxhil1Vrlf0XkHPg7DgTOg5EipInP/gKPAEd2Ymsar0FngAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Screenshot of the email inbox showing the test email received&quot;
        title=&quot;&quot;
        src=&quot;/static/fc0c022980ced6d26b2e4ef3010f2e92/f058b/noreply-email.png&quot;
        srcset=&quot;/static/fc0c022980ced6d26b2e4ef3010f2e92/c26ae/noreply-email.png 158w,
/static/fc0c022980ced6d26b2e4ef3010f2e92/6bdcf/noreply-email.png 315w,
/static/fc0c022980ced6d26b2e4ef3010f2e92/f058b/noreply-email.png 630w,
/static/fc0c022980ced6d26b2e4ef3010f2e92/078fe/noreply-email.png 934w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;We can check the details of this email to see extra information.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 464px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 51.26582278481012%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAAAsTAAALEwEAmpwYAAABO0lEQVR42o2SiW6DQAxE8//fF+W+7wPCcicgmPAsbRW1aVNL1hq0O57xuNc0jYiyLHW/3+08n886Ho86HA5W7/d7q/m32+3sO4oie9e2rV6j5wGrqjKw2+2mwWCg4XCo6XSq0Wik2WymyWRi4JfLRdfrVXEc6118AcIuTVPrDMB8Ptd6vdZyudRisVAQBPJ3/woDhHYYhkqSxBIgZHEiE6bb7daaEv7Nr4BZlpmMPM+N5Wq1slmRyKQJDRnJR4Z1XSsIA5MKoHPOZMLOMwWwKApx91+SnYs60LBjWnSZmjxYMjvc5SRhCTCNOVH2w2V+PB6VktipzJ2KThYgsNtsNlbTgBqHqfv9vm3BeDy2Eb11uW0qqY5Nul8ZVgW3cd2vEA1Op5ONghXD/VeWve+rgJNcRB6nTxph3Cdjngd3/ZKCah0UAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Screenshot of the details of the email received&quot;
        title=&quot;&quot;
        src=&quot;/static/457970940a1f95dccbcf999f72f5fdb7/69096/noreply-details.png&quot;
        srcset=&quot;/static/457970940a1f95dccbcf999f72f5fdb7/c26ae/noreply-details.png 158w,
/static/457970940a1f95dccbcf999f72f5fdb7/6bdcf/noreply-details.png 315w,
/static/457970940a1f95dccbcf999f72f5fdb7/69096/noreply-details.png 464w&quot;
        sizes=&quot;(max-width: 464px) 100vw, 464px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;. Notice the &lt;code class=&quot;language-text&quot;&gt;mailed-by&lt;/code&gt; field! The value is &lt;code class=&quot;language-text&quot;&gt;amazonses.com&lt;/code&gt;. Is it possible to change this? Yes it is tottaly possible. This is called the &lt;a href=&quot;https://docs.aws.amazon.com/ses/latest/dg/mail-from.html&quot;&gt;EMAIL FROM&lt;/a&gt; field. By default if not specified AWS uses Amazon SES domain. This is out of the scope of this demo but it&apos;s good to know.&lt;/p&gt;
&lt;h4&gt;Sending emails using SMTP&lt;/h4&gt;
&lt;p&gt;Sending email using SMTP requires more work at first. I&apos;ll create using the AWS Console an IAM User with SMTP credentials. Ideally you should save this in Secrets Manager for your application to use (remember that at the moment it is not recommended to create SMTP credentials using ephemeral/temporary AWS credentials).&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/2969df5039fbf70520be77ae6ae462b4/0f67e/smtp-01.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 55.69620253164557%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAAsTAAALEwEAmpwYAAACJklEQVR42lVSWXLaQBDVGRwwYG0IEEISsjaEFrSB2GPjIk4qqVQ+U6nKAXKHXCE/+chFX3qGuEw+XvVM98yb129a6GomOoaLnqrDtH0YlodWR+a4aYk8dkUNtz0VN20Rb24ltLsKhyjK6MsyFEmCKksQJQWC7c5Qb45YlA3SfIlyuUXd7DFPS77OikvutbaDOjQhq0P8/NzB728t/Praxp/vbfw4tSEYlos4qxClBaKkwDwr+X4W50gWNSLah/MFkrzmpAxResllWYY8zxHHMSzThNrXINwpI5jTgLdqUMtjimPTxcQOKHo8DiYuxL4BiWyR+mPItJa1CUTV4LhTxujJOrrSCEJ/aKFqDlhUG2TlGmnRkJoViuUOeb1FudrDSypOOrqCpk8xMBwS4HPoBGaFoOkWZtRWQC1b5CeD7UXQbQ9OmCCk9pwghjH1YVKNxRmdff/pC/YPZ9TrA/wo4/mhMb0QZhUpK1eIixWyeo2YfJr6MYJ4gbRqiKCk/Zw/NnECnn84f8DmeEKx2nJkdI4TDsY26u0RPn0CU+SEMb9k3oecxCawvE+fYF0pPD69w/7xzIlPzx9xeHomKxzycGTyi4zkBYyMgT3CFLv/WmI5Vg9pGpr9Ax+35e4tmsMjzw2YQkbIPHvx7xoBETIrmIfXeSaAka0PJ5Q0szm1zM7oNB2Cohmv4/IffIzMewypDZ1m9brGzsc0lwkNve3RJ7KHSD2z7y85aFztynbNWQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Step 1 to create SMTP credentials using AWS Console&quot;
        title=&quot;&quot;
        src=&quot;/static/2969df5039fbf70520be77ae6ae462b4/f058b/smtp-01.png&quot;
        srcset=&quot;/static/2969df5039fbf70520be77ae6ae462b4/c26ae/smtp-01.png 158w,
/static/2969df5039fbf70520be77ae6ae462b4/6bdcf/smtp-01.png 315w,
/static/2969df5039fbf70520be77ae6ae462b4/f058b/smtp-01.png 630w,
/static/2969df5039fbf70520be77ae6ae462b4/0f67e/smtp-01.png 921w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/c4593ed6c701418dbb954541d9f3a7d0/cd536/smtp-02.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 74.0506329113924%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAPCAYAAADkmO9VAAAACXBIWXMAAAsTAAALEwEAmpwYAAACP0lEQVR42oWTWW/aUBCFbSAKadKkSc2WxNh43/CGbUKAQilZGvWhUtr//1NO5w4hqoTVPnyae689587MsaUbK8TACGB7Yxh2iE5/iOHIgWa4UHURPdxqNp93B9p/kdwghRMkuBlaULoqHyb5FFlxjygpUEyXCOMJlJ76lqSjd62jfzOqRTKcCJrpQ7N8PhCJXpRjvnpgytmaLswQxCX8cUGxgBvmGNkRdOpuj9irIw/S3+qiAtGaSJrMlgiSkvFp740nHH2KYm1TZwInzBiXihDFSUJgz15wnN9hsXkm0RWK+zWScg6XRFyq8B0hzhcWTJiWsPwYUt1gdZtMork6YUrJObWY8t6mBJFkeTGtE14bTsiYbsQGHggqvSHScobt9x/IqjkLemTKvoo9QUpVRtnuwnHGzy36Ug4r7Ksw8wXix9+onl5RPf8iXpFtf8K5f2LcN7TJmlhhmK0wiJdQ/fxQsEczVOM76PMXTjZnj7AJY/oArfz2jk6Mqi2jFRt0x0vc+pNDQTYlq7DavqBabKjtJZtieikMl2bmJrROYHhihik5P2Eiyqk1ZedyhcXXB3L4C6aLNSqKuxmKuaU8K8vfYe9jsDOoRlCD7Qbo9rrotE/QOT/D+WUb7fYRZLmJRqNRiyzL9KcNagQJy/Hx6eICsiShf/IBvbMTXCrHOPvYQuuoAYnO6/jc6de37PghFEXhl05bLZw2mzhuN5hmU/6HYG2FQ5hOgJHpUAvXuKKXLunmK2XAiCRxXse1auAPzL+imF5XaVcAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Step 2 to create SMTP credentials using AWS Console&quot;
        title=&quot;&quot;
        src=&quot;/static/c4593ed6c701418dbb954541d9f3a7d0/f058b/smtp-02.png&quot;
        srcset=&quot;/static/c4593ed6c701418dbb954541d9f3a7d0/c26ae/smtp-02.png 158w,
/static/c4593ed6c701418dbb954541d9f3a7d0/6bdcf/smtp-02.png 315w,
/static/c4593ed6c701418dbb954541d9f3a7d0/f058b/smtp-02.png 630w,
/static/c4593ed6c701418dbb954541d9f3a7d0/40601/smtp-02.png 945w,
/static/c4593ed6c701418dbb954541d9f3a7d0/cd536/smtp-02.png 1114w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/1b549f964d978bfa7b89097dd365886c/afd0b/smtp-03.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 44.93670886075949%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAYAAAAywQxIAAAACXBIWXMAAAsTAAALEwEAmpwYAAABaklEQVR42nWS13LCMBBF/Q2hFxe5Wy5YxgUMBMJkmIT8SWaS/3+7WQlSmOCHM1qVvXu9Xm3KQoxZBOZx2H6M4cSEwQKFaYfQLR+WE9LeV3e/GFcoHhs/55pMnFPSaGKhN5xjNLWQL9fESq1pXqNa7VQ8Nz1MdQczw71Bnk/m9kXQjxYI4hwBz8mNh/5IV86Yy8lZRA8dKsIwnrF/Qn+RhZSgdCQfywoy7pP9eFFhtT2ibg/wuYATZnCpsBNkndhBitHMgiZVByQyuPZBromoUW+fIJoNyvUjRL1BmBaIsmUn8l4a024bfRFMiwbr/TOqdg9RteB51SkULwoFz0S3YE4ih9MZ9e6IvNlSQglOfIsEiSBHAn6yBOMVbMKKSvrkO4ISOUJumIBRX0wvVmNjuRG8KFOk1BKeNyiLFG9bB28bF+fWga6b9wVVT+lvK679lagRoRHjWQEvLtEKF5+vD/h46eH91AMzdXwBQtMCya86sa4AAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Step 3 to create SMTP credentials using AWS Console&quot;
        title=&quot;&quot;
        src=&quot;/static/1b549f964d978bfa7b89097dd365886c/f058b/smtp-03.png&quot;
        srcset=&quot;/static/1b549f964d978bfa7b89097dd365886c/c26ae/smtp-03.png 158w,
/static/1b549f964d978bfa7b89097dd365886c/6bdcf/smtp-03.png 315w,
/static/1b549f964d978bfa7b89097dd365886c/f058b/smtp-03.png 630w,
/static/1b549f964d978bfa7b89097dd365886c/40601/smtp-03.png 945w,
/static/1b549f964d978bfa7b89097dd365886c/afd0b/smtp-03.png 1153w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Now that we have the SMTP user and SMTP password let&apos;s use a simple Python code to send emails.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;python&quot;&gt;&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; smtplib
&lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; email&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;mime&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;multipart &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; MIMEMultipart
&lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; email&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;mime&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;text &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; MIMEText

&lt;span class=&quot;token comment&quot;&gt;# Parameters&lt;/span&gt;
smtp_server &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;email-smtp.us-east-1.amazonaws.com&quot;&lt;/span&gt;
smtp_port &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;587&lt;/span&gt;
smtp_username &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;YOUR_SMTP_USERNAME&quot;&lt;/span&gt;
smtp_password &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;YOUT_SMTP_PASSWORD&quot;&lt;/span&gt;
sender &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;noreply@demosfelipetrindade.top&quot;&lt;/span&gt;
recipient &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;yourpersonalemail@gmail.com&quot;&lt;/span&gt;
subject &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Test Email from SMTP&quot;&lt;/span&gt;
message &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token triple-quoted-string string&quot;&gt;&quot;&quot;&quot;
Test SMTP Email
This is an Amazon SES SMTP test email!
&quot;&quot;&quot;&lt;/span&gt;
body_html &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-interpolation&quot;&gt;&lt;span class=&quot;token string&quot;&gt;f&quot;&amp;lt;html&gt;&amp;lt;body&gt;&amp;lt;h1&gt;Test SMTP Email&amp;lt;/h1&gt;&amp;lt;p&gt;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;message&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;lt;/p&gt;&amp;lt;/body&gt;&amp;lt;/html&gt;&quot;&lt;/span&gt;&lt;/span&gt;

msg &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; MIMEMultipart&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;alternative&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
msg&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Subject&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; subject
msg&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;From&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; sender
msg&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;To&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; recipient

part1 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; MIMEText&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;message&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;plain&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
part2 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; MIMEText&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;body_html&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;html&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

msg&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;attach&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;part1&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
msg&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;attach&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;part2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;with&lt;/span&gt; smtplib&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;SMTP&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;smtp_server&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; smtp_port&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; server&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
 server&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;starttls&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
 server&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;login&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;smtp_username&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; smtp_password&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
 server&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sendmail&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;sender&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; recipient&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; msg&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;as_string&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Email sent successfully!&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;except&lt;/span&gt; Exception &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-interpolation&quot;&gt;&lt;span class=&quot;token string&quot;&gt;f&quot;Failed to send email: &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;After code execution, you should see the following in your inbox:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/974690ac8df17994eebbddbef60667fd/966a0/noreply-smtp-email.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 31.645569620253163%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAGCAYAAADDl76dAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAqElEQVR42p1QSQ7DIAzk/+/qH3pMlISEQyoIIFLWKbaUW3tILQ2LZY9nLEoDjDGY5xlSSqzryqBcKQXfotaKlBJyzlxD//M8GeLxTFC7hT00vPdwzsFayw2/gohCCEwQe907Rs4Rsahd4b6/MAwDxnHEsizYtg3/hqBDa41pmqCUYjKyfKmlybcJSeq1D7oJsdsg2621+4QpJ5jD8F5IGb2ddyh90F3LHwjf1hQ6pl5CAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Screenshot of an email that was sent using Amazon SES SMTP&quot;
        title=&quot;&quot;
        src=&quot;/static/974690ac8df17994eebbddbef60667fd/f058b/noreply-smtp-email.png&quot;
        srcset=&quot;/static/974690ac8df17994eebbddbef60667fd/c26ae/noreply-smtp-email.png 158w,
/static/974690ac8df17994eebbddbef60667fd/6bdcf/noreply-smtp-email.png 315w,
/static/974690ac8df17994eebbddbef60667fd/f058b/noreply-smtp-email.png 630w,
/static/974690ac8df17994eebbddbef60667fd/966a0/noreply-smtp-email.png 944w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2&gt;Cya!&lt;/h2&gt;
&lt;p&gt;I hope you&apos;ve like it and that this demo and explanation was helpful!&lt;/p&gt;
&lt;p&gt;See you around! 👋&lt;/p&gt;</content:encoded></item><item><title><![CDATA[RSS as a tool to stay up-to-date with tools, news, and posts]]></title><description><![CDATA[We all know that the technology world evolves FAST. The amount of new tools created weekly, updates, bug fixes, new features, tech blog…]]></description><link>https://felipetrindade.com/rss/</link><guid isPermaLink="false">https://felipetrindade.com/rss/</guid><pubDate>Mon, 21 Oct 2024 12:05:32 GMT</pubDate><content:encoded>&lt;p&gt;We all know that the technology world evolves FAST. The amount of new tools created weekly, updates, bug fixes, new features, tech blog posts... It&apos;s impossible to manually keep track of all these things without an automatic way. That&apos;s where we are going to use RSS in our favor! Maybe the word &quot;RSS&quot; is not familiar to you but I bet you have already seen its icon.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 153px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/02da15cc7f6183b61a46a0358bdb6042/3bda9/rss.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 100%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAACXBIWXMAAAsSAAALEgHS3X78AAAC4UlEQVR42p1VW0gVURS9XxGFPSjfmtGtfiqCTCKSqOgjoohA8KfPgqDoK+yBetUyKlGztDAzS4QI0aAwCguxJ9IDjBIqKsKgokDNnHNm7sys9jl7nBlLyRo49wz7nrvO2muvvW9EHF0Afx1bBFG+kN6jGBP/hxXhFwI4Mh+iKAkilgpRNo+Ao3zBfwEqsJNLYV7Kg6zfBFGxnIDTYBTOgSjJ8JhHJwmoUiRWsmEL9GObcIe/wvnwEFZnOeTpXIjCRIjSTE+OyQJe8ADV47rqg1/NEdhPWyCqcmAUJf5VhohOpTRDM7FftMN51w134CPg2CFw2ka+w2zbC1GcTKCLJywca6hBM2EcmA7j8GydmtLTftIMVwx5wI7e4t3VXLiJARlMnloF63YZrKu7IGtWwziYAGP/FMi69bDfdnn6WgzaVUEFmztu+lpDozgF5sXtgYTmMJzXnTAv5zMwpRl/VM9fOnG9ma27CfRPTccBdH3dNKmeJvJlFoxDMxF/UBdcSk6Q1Tle9aO/pUxGllUrdcr28ytwhz57bLgwdl8H/ZDMThc7b+76oPGeRrLU2NSDTiEWojiJtSGTx+/VsGY2pxh/fJ5YJkCe3QDIYUYUg+SONdoloywjfv7aj2mcHoEaBVNhXdvHLFWFaZdNeRSfpn3ps7xVwh3lsYwEYKlU4Z1w+p/BftUBWbtWFySsm/3yOsVmwGzOD2LkAE3E66LA2Gdy4RoDwcG+mzCU3ypXwP3xhQvx85vWWhnbHejn2OAnkmgZS0bShVpvq9/Luh7EVB1Q7Wb3tvkXmS07yPyzYL+/7wVGIM9tpCGSrlkGGtINdm8r30qgVtsefUgBWneO+4DWjQLqKNKxt913gmzcph0QAlTzMEsv5UdZu47bq5znpKzKhknDQy1Zmc2dRdXVsYbNECeW8AgcU+XRpZo/lh4aVexTEUvhNTp8lVViyRzzwMYH9P8GwvEox/SK/hkLDYpfCpuiSZh2zGMAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;RSS icon&quot;
        title=&quot;&quot;
        src=&quot;/static/02da15cc7f6183b61a46a0358bdb6042/3bda9/rss.png&quot;
        srcset=&quot;/static/02da15cc7f6183b61a46a0358bdb6042/3bda9/rss.png 153w&quot;
        sizes=&quot;(max-width: 153px) 100vw, 153px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2&gt;RSS&lt;/h2&gt;
&lt;p&gt;RSS (it can mean several things, such as Really Simple Syndication or Rich Site Summary) is one of the elders of the internet, created in 1999. We are talking about a time when internet speed was around &lt;a href=&quot;https://newsroom.cisco.com/c/r/newsroom/en/us/a/y2010/m06/annual-cisco-visual-networking-index-forecast-projects-global-ip-traffic-to-increase-more-than-fourfold-by-2014.html&quot;&gt;127 kilobits per second&lt;/a&gt;. Downloading a 3MB audio took three minutes back then! You can imagine how slow it was to surf the web back then...&lt;/p&gt;
&lt;p&gt;Today our internet speed is way faster &lt;a href=&quot;https://www.statista.com/chart/31075/global-average-internet-download-speed/&quot;&gt;(at least 1000x)&lt;/a&gt; so doing &quot;ordinary&quot; things, such as navigating the internet, uploading attachments in emails, watching videos, and other activities are way faster than it was more than two decades ago.&lt;/p&gt;
&lt;p&gt;With that being said, can you imagine the pain of opening a website only to check if a new post/news was published in the feed? The idea is that the website could publish posts/news in a different type of feed called RSS. This special feed could be interpreted by a feed reader that would allow the user to know when something new was published and the data transfer would also be significantly smaller. Today RSS is more used to save time, and as you know time is valuable. Notice that RSS is not magical, it relies on the website to implement the RSS feed so it can be consumed by the feed reader (also known as feed aggregator). Today, some fancy feed readers can automatically scrape a website and turn it into an &quot;RSS feed&quot; (but it might be a payed feature in our feed reader).&lt;/p&gt;
&lt;p&gt;Something very common in tech blogs is to, instead of implementing an RSS feed, create a newsletter, where you get the new blog posts via e-mail. Ideally, though, both should be presented!&lt;/p&gt;
&lt;p&gt;The RSS feed is just an XML formatted plain text, as you can see in my &lt;a href=&quot;https://felipetrindade.com/rss.xml&quot;&gt;website&apos;s RSS&lt;/a&gt;. As you can see, this is pretty difficult for us to read but very easy for a machine to understand. So every time this XML changes with a new section, the feed reader knows that the website has a new post.&lt;/p&gt;
&lt;h2&gt;Feed readers&lt;/h2&gt;
&lt;p&gt;There are several feed readers out there but in general, they can be divided into two categories: desktop and web apps. Desktop readers, such as &lt;a href=&quot;https://github.com/martinrotter/rssguard&quot;&gt;RSS Guard&lt;/a&gt; or &lt;a href=&quot;https://github.com/yang991178/fluent-reader&quot;&gt;Fluent Reader&lt;/a&gt; are &quot;off-line&quot; readers, which means you install them on your computer and needs your computer to access the feed reader. A more handy solution (because you simply need to access it using any web browser) is web feed readers such as &lt;a href=&quot;https://feedly.com/&quot;&gt;Feedly&lt;/a&gt; or &lt;a href=&quot;https://www.inoreader.com/&quot;&gt;Inoreader&lt;/a&gt;. Choose the option that fits your needs the most. One important thing to mention about web feed readers is that they are usually paid (but offer an always free version with limited features).&lt;/p&gt;
&lt;p&gt;This is not an ad or recommendation: I use Feedly and for now, it works fine for my use case. It does not support some features in the free plan but I tolerate. But you should really use any tool that fits your needs.&lt;/p&gt;
&lt;h2&gt;RSS feeds suggestion&lt;/h2&gt;
&lt;p&gt;Currently, I divide my RSS feed into two categories:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Tech-blogs&lt;/strong&gt;: Mostly tech companies blogs presenting solutions, problems, and tools. Some of the tech blogs I follow are &lt;a href=&quot;https://aws.amazon.com/blogs/architecture/&quot;&gt;AWS Architecture Blog&lt;/a&gt;, &lt;a href=&quot;https://engineering.fb.com/&quot;&gt;Engineering at Meta&lt;/a&gt;, &lt;a href=&quot;https://www.linkedin.com/blog/engineering&quot;&gt;LinkedIn Blog&lt;/a&gt;, &lt;a href=&quot;https://slack.engineering/&quot;&gt;Slack Engineering&lt;/a&gt;, &lt;a href=&quot;https://engineering.atspotify.com/&quot;&gt;Spotify Engineering&lt;/a&gt;. I also recommend personal tech blogs, such as &lt;a href=&quot;https://samwho.dev/&quot;&gt;SamWho&lt;/a&gt; and &lt;a href=&quot;https://www.felipetrindade.com/&quot;&gt;mine&lt;/a&gt; (of course!).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tools releases&lt;/strong&gt;: This helps me stay tuned about releases of the main tools I use. Some of the tools I follow are &lt;a href=&quot;https://github.com/opentofu/opentofu/releases&quot;&gt;Opentofu&lt;/a&gt;, &lt;a href=&quot;https://github.com/gruntwork-io/terragrunt/releases&quot;&gt;Terragrunt&lt;/a&gt;, &lt;a href=&quot;https://github.com/aws/aws-cdk/releases&quot;&gt;CDK&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Some important things to mention:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Today is very common for tech people to publish things in blogs like &lt;a href=&quot;https://medium.com/&quot;&gt;Medium&lt;/a&gt; and &lt;a href=&quot;https://dev.to/&quot;&gt;Dev.To&lt;/a&gt;. I still think RSS is relevant and does not substitute them, use both in your favor!&lt;/li&gt;
&lt;li&gt;The &quot;tools release&quot; DOES NOT replace &lt;a href=&quot;https://github.com/dependabot&quot;&gt;Dependabot&lt;/a&gt; at all. You should still use it in your projects to update the versions of your dependencies.&lt;/li&gt;
&lt;li&gt;It&apos;s common for tools to offer release notes (e.g. &lt;a href=&quot;https://fastapi.tiangolo.com/release-notes/&quot;&gt;Fastapi&lt;/a&gt;) and upgrading notes (e.g. &lt;a href=&quot;https://argo-cd.readthedocs.io/en/latest/operator-manual/upgrading/overview/&quot;&gt;ArgoCD Upgrading guide&lt;/a&gt;). You should still read them, and treat the RSS as just a &quot;preview&quot;.&lt;/li&gt;
&lt;li&gt;Some GitHub repositories have INTENSE release cycles, with beta, dev, and test versions being deployed frequently, this will spam your RSS feed a bit.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Cya!&lt;/h2&gt;
&lt;p&gt;This was a not-so-common blog post from my side. Less technical but still important to comment on. This might be extremely helpful if you struggle to stay up-to-date with tools and tech articles.&lt;/p&gt;
&lt;p&gt;See you around! 👋&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Multi-account deployment using AWS CDK]]></title><description><![CDATA[AWS CDK is a great tool to deploy infrastructure in AWS using your favorite programming language but deploying to multiple accounts using…]]></description><link>https://felipetrindade.com/cdk-multi-account/</link><guid isPermaLink="false">https://felipetrindade.com/cdk-multi-account/</guid><pubDate>Mon, 30 Sep 2024 16:40:30 GMT</pubDate><content:encoded>&lt;p&gt;AWS CDK is a great tool to deploy infrastructure in AWS using your favorite programming language but deploying to multiple accounts using AWS CodePipeline can be tricky. In this blog post, we will cover the idea behind it and we are going to implement this solution using AWS CDK and AWS CodePipeline. All the code used in this blog post is available in my &lt;a href=&quot;https://github.com/felipelaptrin/cdk-multi-account&quot;&gt;GitHub repository&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Multi Account Architecture&lt;/h2&gt;
&lt;p&gt;The goal of this blog post is to use AWS best practices and separate AWS accounts based on their functionality and deploy infrastructure from a single AWS account to several AWS accounts. To summarize, the below architecture will be created in this blog post:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/c13e48048d52bebe6167cef257614442/fbf08/cdk-multi-account.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 55.06329113924051%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAAsTAAALEwEAmpwYAAAB0UlEQVR42pVSTW/UMBDd/8ulf4MD/AD2wIEDEhK/oBwQrXpZVaggkGjTlt2y2cRO7CT+iO04eUy8SO2iqoiRRnHG9pv3nmdhrUWMEc45eO+R5zm0VhiGAd55BD9nQAiH6R+pz7VF0zRgjMEYgzlWqxWyLEvraZrwPzGfX0ghUFdVYjRH27ZQWmMKEebrLSKxHY0D4rRvME7wvUerejjbp/8DwFlyLST6vk8spRTQSsH2GuIix/blFrY1ME0H/fwDTFaikQX4uyPY4oYAE9Q9YNt2uNvuEpjWBpvbH+DFr3RE5R0uX/8krwY4alguP0Hf1eSXRnf6CqEtMcW/JM9GPgxjLHq3r0UC8sEhhgED1ewQUuNxiAgkdSRbEMenAR9GJK9kLWGJeU+N+I4hu7yCJ+8U2WTnh5wS0r8BJ2LVnV9BbTmgHZwyELxGT2B+w6G+rWGrBrABnWwhaS+SiscB/zxcWNeQR+9hhUIpcrz9+AysvE57enmGZnmC+fbZlzc4Pn9BJPC05CDI/OPvGH3EMHrc7E7R62bv9ecN5TqteZNhyy8IcMSiKAqIeRbr+jBpNjkrUSoBXjLUJUdbGfpWqAoGJmuwpqY1jVFFklmLinH8BkqATNhskrvIAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Multi Account deployment with AWS CDK&quot;
        title=&quot;&quot;
        src=&quot;/static/c13e48048d52bebe6167cef257614442/f058b/cdk-multi-account.png&quot;
        srcset=&quot;/static/c13e48048d52bebe6167cef257614442/c26ae/cdk-multi-account.png 158w,
/static/c13e48048d52bebe6167cef257614442/6bdcf/cdk-multi-account.png 315w,
/static/c13e48048d52bebe6167cef257614442/f058b/cdk-multi-account.png 630w,
/static/c13e48048d52bebe6167cef257614442/40601/cdk-multi-account.png 945w,
/static/c13e48048d52bebe6167cef257614442/fbf08/cdk-multi-account.png 962w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Let&apos;s comment about it:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;GitHub Actions&lt;/strong&gt;: GitHub Actions will be used only for CI purposes: check the linting and run tests of our AWS CDK code.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;AWS Accounts&lt;/strong&gt;: We will use two AWS accounts for workloads (development and production) and one account that will contain the CD pipeline to deploy to the workload accounts and resources shared by every account (shared assets). The workload accounts usually contain services like RDS, EC2, EKS, Lambda, S3, and so on, while the shared assets accounts will contain the Pipelines for deploying to the workload account and common resources to the workload accounts (e.g. images in ECR to allow artifact promotion, common scripts in S3 buckets...).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;CodePipeline&lt;/strong&gt;: It will deploy services in the desired AWS account based on the AWS CDK code created.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;AWS Connector for GitHub&lt;/strong&gt;: A CodeStar connection will be created between GitHub repository and AWS, this will install a GitHub App that will trigger a CodePipeline execution based on commits pushed to the GitHub repository.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;CloudFormation&lt;/strong&gt;: AWS CDK is an abstraction over CloudFormation, in the end, all the AWS CDK code will be transformed (synth) into CloudFormation stacks.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Notice that the ECR and S3 created are just for exemplifying in the code how to create resources in these accounts.&lt;/p&gt;
&lt;h2&gt;AWS CDK&lt;/h2&gt;
&lt;p&gt;This is not an AWS CDK tutorial but I need to comment some important things before diving into the code. CDK, as mentioned, relies on the CloudFormation service, so the more you know about CloudFormation the better will be to understand how it works, although it&apos;s not necessary.&lt;/p&gt;
&lt;p&gt;For CDK to work you need some resources in your account (such as IAM Roles that CloudFormation will use to deploy/lookup resources and publish files). The step of creating these resources before start to work on the CDK code is called &lt;code class=&quot;language-text&quot;&gt;bootstrap&lt;/code&gt;. This is a manual step that will provision a CloudFormation Stack (called CDKToolkit) in the region you are bootstrapping.&lt;/p&gt;
&lt;p&gt;You can use any programming language supported by AWS CDK my recommendation is Typescript, which is the default programming language used to write AWS CDK code, and this is the programming language that I will use in this demo.&lt;/p&gt;
&lt;p&gt;In our code we will create 4 stacks:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Pipeline stack&lt;/strong&gt;: This will be responsible for creating the CodePipeline resources to cross-account deploy to all environments (shared-assets and workload). This will be deployed in the shared-assets account.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Shared Assets stack&lt;/strong&gt;: Resources deployed in the shared assets account.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Workload (Development) stack&lt;/strong&gt;: Resources deployed in the development account.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Workload (Production) stack&lt;/strong&gt;: Resources deployed in the production account.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Notice that this is just one possible way of deploying things, in reality, you could have divided this in several different ways, multiple stacks per account, and so on. The idea here is to present only the solution and the idea that makes this deployment possible.&lt;/p&gt;
&lt;p&gt;Most of the time when creating CDK resources we will use &lt;a href=&quot;https://docs.aws.amazon.com/cdk/v2/guide/constructs.html&quot;&gt;L2 constructs&lt;/a&gt; that can be found in the API Reference &lt;a href=&quot;https://docs.aws.amazon.com/cdk/api/v2/docs/aws-construct-library.html&quot;&gt;documentation&lt;/a&gt;. What makes AWS CDK deployment easy is the existence of the &lt;a href=&quot;https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.pipelines-readme.html&quot;&gt;pipelines&lt;/a&gt; module (do not confuse with the &lt;a href=&quot;https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_codepipeline-readme.html&quot;&gt;CodePipeline&lt;/a&gt; module) that is an abstraction over CI/CD AWS Services to create a deployment pipeline for your own AWS CDK (notice, this means that AWS CDK will be responsible for creating its own CD deployment pipeline!). We will see it in action.&lt;/p&gt;
&lt;h2&gt;Hands-on!&lt;/h2&gt;
&lt;p&gt;Let&apos;s discuss all tools used in this project (the latter will be CDK and the code of this demo).&lt;/p&gt;
&lt;h3&gt;Devbox&lt;/h3&gt;
&lt;p&gt;I always use &lt;a href=&quot;https://www.jetify.com/devbox&quot;&gt;Devbox&lt;/a&gt; in my projects. I HATE when I on-board a project that it&apos;s not clear the dependencies (and their version) that I need to have installed to contribute to the project and make it work locally. That&apos;s why I love Devbox: an abstraction of Nix Packages that installs locally (in a folder called &lt;code class=&quot;language-text&quot;&gt;.devbox&lt;/code&gt;) all the dependencies of the project in an explicit way. I&apos;ve used the following devbox file (&lt;code class=&quot;language-text&quot;&gt;devbox.json&lt;/code&gt;):&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;$schema&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https://raw.githubusercontent.com/jetify-com/devbox/0.12.0/.schema/devbox.schema.json&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;packages&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;nodejs@22.8.0&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;awscli2@2.17.42&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;yarn@1.22.22&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;go-task@3.38.0&quot;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;shell&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;init_hook&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
      &lt;span class=&quot;token string&quot;&gt;&quot;echo &apos;Please use Taskfile scripts.&apos;&quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;As you can see, I&apos;m using NodeJS, AWS CLI, Yarn, and Task. The first three dependencies are obvious why we are using, but you might wondering about Task...&lt;/p&gt;
&lt;h3&gt;Task&lt;/h3&gt;
&lt;p&gt;Task is a modern approach to Makefiles written in Go. It supports a declarative YAML manifest (called &lt;code class=&quot;language-text&quot;&gt;Taskfile&lt;/code&gt;) which you specify the scripts and commands that you would like to use. I&apos;m using Task here to make it easier the &lt;a href=&quot;https://docs.aws.amazon.com/cdk/v2/guide/bootstrapping.html&quot;&gt;CDK bootstrap&lt;/a&gt; process. Indeed, this will only be done once, but I highly recommend this to be documented or even scripted to make it repeatable in case it&apos;s needed in the future. What we want here is to bootstrap all the three AWS accounts (shared assets, development, and production) but there is one thing: we need to allow the shared assets account to assume a role with Administrator access to be assumed in the workload accounts. To simplify all of this, the following Taskfile was created:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;3&apos;&lt;/span&gt;

&lt;span class=&quot;token key atrule&quot;&gt;anchors&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;check_aws_credentials&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token important&quot;&gt;&amp;amp;check_aws_credentials&lt;/span&gt;
     &lt;span class=&quot;token key atrule&quot;&gt;sh&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;aws sts get-caller-identity&apos;&lt;/span&gt;
     &lt;span class=&quot;token key atrule&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;AWS credentials are not valid or are expired!&quot;&lt;/span&gt;

&lt;span class=&quot;token key atrule&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;AWS_REGION&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; us&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;east&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# Modify this!&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;AWS_SHARED_ACCOUNT&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;730335516527&quot;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# Modify this!&lt;/span&gt;

&lt;span class=&quot;token key atrule&quot;&gt;tasks&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;desc&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Install all code dependencies using YARN
    &lt;span class=&quot;token key atrule&quot;&gt;silent&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean important&quot;&gt;true&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;cmds&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; yarn
  &lt;span class=&quot;token key atrule&quot;&gt;bootstrap-shared-assets&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;desc&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Bootstrap the shared account (contains shared assets and CI/CD pipeline). Make sure AWS credentials are set.
    &lt;span class=&quot;token key atrule&quot;&gt;silent&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean important&quot;&gt;true&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;preconditions&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token important&quot;&gt;*check_aws_credentials&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;cmds&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; echo &quot;Bootstrapping the &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;.AWS_SHARED_ACCOUNT&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; account in the &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;.AWS_REGION&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; region&lt;span class=&quot;token punctuation&quot;&gt;...&lt;/span&gt;&quot;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; yarn cdk bootstrap aws&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;//&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;.AWS_SHARED_ACCOUNT&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;/&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;.AWS_REGION&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;bootstrap-workload&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;desc&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Bootstrap an workload account to allow deployments from the shared/ci&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;cd AWS account.  Make sure AWS credentials are set.
    &lt;span class=&quot;token key atrule&quot;&gt;silent&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean important&quot;&gt;true&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;preconditions&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token important&quot;&gt;*check_aws_credentials&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;vars&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;AWS_ACCOUNT&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;sh&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; aws sts get&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;caller&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;identity &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;query Account &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;output text
    &lt;span class=&quot;token key atrule&quot;&gt;cmds&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; echo &quot;Bootstrapping the &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;.AWS_ACCOUNT&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; account in the &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;.AWS_REGION&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; region&lt;span class=&quot;token punctuation&quot;&gt;...&lt;/span&gt;&quot;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token scalar string&quot;&gt;
 yarn cdk bootstrap aws://{{.AWS_ACCOUNT}}/{{.AWS_REGION}} \
 --trust {{.AWS_SHARED_ACCOUNT}} --trust-for-lookup {{.AWS_SHARED_ACCOUNT}} \
 --cloudformation-execution-policies &quot;arn:aws:iam::aws:policy/AdministratorAccess&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;When running &lt;code class=&quot;language-text&quot;&gt;task --list&lt;/code&gt; we have the following output (that summarizes well the tasks we have defined in the Taskfile).&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/6d010fb4cbe63cbbb2a701a058cee80d/c23ad/tasks.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 6.962025316455696%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAABCAYAAADeko4lAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAS0lEQVR42mPQDwj/r+SX8F/ZI+6/ukvaf/OQgv/64a7/jb39/+vZB/7XtvP9r2TmBsbK5hAMYbuDsZyh438jM7//2pYx/0VMQv4DAHHlIjJI4nsAAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Output of the &amp;quot;task --list&amp;quot; command. It shows all the available tasks for the project with their name and a description.&quot;
        title=&quot;&quot;
        src=&quot;/static/6d010fb4cbe63cbbb2a701a058cee80d/f058b/tasks.png&quot;
        srcset=&quot;/static/6d010fb4cbe63cbbb2a701a058cee80d/c26ae/tasks.png 158w,
/static/6d010fb4cbe63cbbb2a701a058cee80d/6bdcf/tasks.png 315w,
/static/6d010fb4cbe63cbbb2a701a058cee80d/f058b/tasks.png 630w,
/static/6d010fb4cbe63cbbb2a701a058cee80d/40601/tasks.png 945w,
/static/6d010fb4cbe63cbbb2a701a058cee80d/c23ad/tasks.png 1193w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;So, with the AWS Accounts in place, we need to perform:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Export the credentials of the Shared Assets account and run &lt;code class=&quot;language-text&quot;&gt;task bootstrap-shared-assets&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Export the credentials of the Development account and run &lt;code class=&quot;language-text&quot;&gt;task bootstrap-workload&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Export the credentials of the Production account and run &lt;code class=&quot;language-text&quot;&gt;task bootstrap-workload&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I encourage you to check the Taskfile code to notice that the &lt;code class=&quot;language-text&quot;&gt;cdk bootstrap&lt;/code&gt; command used in the workload account have extra parameters that allow the cross-account access!&lt;/p&gt;
&lt;h3&gt;AWS CodeStar connection&lt;/h3&gt;
&lt;p&gt;Follow the &lt;a href=&quot;https://docs.aws.amazon.com/dtconsole/latest/userguide/connections-create-github.html&quot;&gt;AWS Documentation&lt;/a&gt;. This must be performed in the CI-CD/Shared account. You must be the owner of the GitHub repository/organization.&lt;/p&gt;
&lt;p&gt;This is a step that must be performed via the console, even the CLI option provided by the documentation mentions that you still need to go to the console to activate the connection, so I think it&apos;s easier to simply create everything manually.&lt;/p&gt;
&lt;h3&gt;CDK&lt;/h3&gt;
&lt;p&gt;I&apos;m going to divide the source code into three parts:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Pipeline: Pipeline configuration and stack to create the CodePipeline that will deploy CDK code.&lt;/li&gt;
&lt;li&gt;Stacks: Creates resources&lt;/li&gt;
&lt;li&gt;Configuration: Values used to configure the stacks.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let&apos;s check our code.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// main.ts&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;source-map-support/register&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; cdk &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;aws-cdk-lib&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; CodePipelineStack &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;./pipeline/codepipeline&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; pipelineProps &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;./config/pipeline&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; app &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;cdk&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;App&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CodePipelineStack&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;app&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;MultiAccountStack&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; pipelineProps&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The entry point of our project defines a stack called &lt;code class=&quot;language-text&quot;&gt;MultiAccountStack&lt;/code&gt;. This stack is the codepipeline stack that will deploy CDK and will also deploy the other stack in the corresponding accounts.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// pipeline/codepipeline.ts&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; Stack&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; StackProps&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Stage&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; StageProps&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Tags &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;aws-cdk-lib&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; CodePipeline&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; CodePipelineSource&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; ManualApprovalStep&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; ShellStep &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;aws-cdk-lib/pipelines&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; Construct &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;constructs&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; SharedAssetsProps&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; SharedAssetsStack &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;../stack/shared-assets&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; WorkflowStack&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; WorkloadProps &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;../stack/workload&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; developmentProps &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;../config/development&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; productionProps &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;../config/production&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; sharedAssetsProps &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;../config/shared-assets&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PipelineProps&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;StackProps&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  gitBranch&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  githubOwner&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  githubRepository&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  codeStarConnectionArn&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SharedAssetsPipelineStage&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Stage&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;scope&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Construct&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; id&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; sharedAssetsProps&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; SharedAssetsProps&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; props&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; StageProps&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;scope&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; props&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SharedAssetsStack&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;StageStack&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; sharedAssetsProps&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;WorkloadPipelineStage&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Stage&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;scope&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Construct&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; id&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; workloadProps&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; WorkloadProps&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; props&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; StageProps&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;scope&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; props&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;WorkflowStack&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;WorkflowStack&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; workloadProps&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CodePipelineStack&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Stack&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;scope&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Construct&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; id&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; props&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; PipelineProps&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;scope&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; props&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; branch &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;gitBranch&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; githubRepository &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;githubOwner&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;githubRepository&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; githubConnection &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; CodePipelineSource&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;connection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;githubRepository&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; branch&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
 connectionArn&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;codeStarConnectionArn&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; pipeline &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CodePipeline&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;PipelineStack&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
 pipelineName&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;MultiAccountPipeline&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
 synth&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ShellStep&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Synth&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
 input&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; githubConnection&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
 commands&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;yarn&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;yarn run cdk synth&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
 primaryOutputDirectory&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;cdk.out&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
 selfMutation&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
 crossAccountKeys&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    pipeline&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addStage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SharedAssetsPipelineStage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;SharedAssetsStage&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; sharedAssetsProps&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    pipeline&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addStage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;WorkloadPipelineStage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;DevelopmentStage&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; developmentProps&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    pipeline&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addStage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;WorkloadPipelineStage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;ProductionStage&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; productionProps&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
 pre&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ManualApprovalStep&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Release to production&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    Tags&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;CreatedBy&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;CDK&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;In the end, this code defines a class (&lt;code class=&quot;language-text&quot;&gt;CodePipelineStack&lt;/code&gt;) that extends a stack and defines a pipeline with stages to deploy to shared assets account (using shared-assets stack), development account, and production account (using workload stack).&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// stack/shared-assets.ts&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; RemovalPolicy&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Stack&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; StackProps&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Tags &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;aws-cdk-lib&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; Repository&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; TagMutability &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;aws-cdk-lib/aws-ecr&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; Construct &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;constructs&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; AccountPrincipal&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; CompositePrincipal &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;aws-cdk-lib/aws-iam&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; AwsAccount &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;../config/types&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SharedAssetsProps&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;StackProps&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  ecrRepository&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SharedAssetsStack&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Stack&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;scope&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Construct&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; id&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; props&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; SharedAssetsProps&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;scope&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; props&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; allAwsAccounts &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Object&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;values&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;AwsAccount&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;accountId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AccountPrincipal&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;accountId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; repositoryName &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ecrRepository&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; repository &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Repository&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; repositoryName&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
 repositoryName&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; repositoryName&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
 removalPolicy&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; RemovalPolicy&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;DESTROY&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
 imageTagMutability&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; TagMutability&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;IMMUTABLE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
 imageScanOnPush&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; principals &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CompositePrincipal&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;allAwsAccounts&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      repository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;grantPullPush&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;principals&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    Tags&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;CreatedBy&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;CDK&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This is a &quot;dummy&quot; stack that creates ECR repositories.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// stack/workload.ts&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; RemovalPolicy&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Stack&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; StackProps&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Tags &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;aws-cdk-lib&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; Construct &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;constructs&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; BlockPublicAccess&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Bucket &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;aws-cdk-lib/aws-s3&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; Environment &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;../config/types&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;WorkloadProps&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;StackProps&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  environment&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Environment&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;WorkflowStack&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Stack&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;scope&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Construct&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; id&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; props&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; WorkloadProps&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;scope&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; props&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Bucket&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;environment&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;-bucket-ingestion&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
 enforceSSL&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
 blockPublicAccess&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; BlockPublicAccess&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;BLOCK_ALL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
 removalPolicy&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; RemovalPolicy&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;DESTROY&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    Tags&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;CreatedBy&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;CDK&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This is a &quot;dummy&quot; stack that creates an S3 bucket.&lt;/p&gt;
&lt;p&gt;It&apos;s important to understand that in the &lt;code class=&quot;language-text&quot;&gt;pipeline/codepipeline.ts&lt;/code&gt; file, the configs are being passed to the stack, controlling where (environment) the stack will be deployed. Let&apos;s check, for example, the development config values.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// config/development.ts&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; WorkloadProps &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;../stack/workload&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; AwsAccount &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;./types&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; developmentProps&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; WorkloadProps &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
 environment&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;development&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
 env&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
 account&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; AwsAccount&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Development&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
 region&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;us-east-2&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Basically, it add values to the props of the stack. The same will happen to ALL the other environments: we are defining our infrastructure via code.&lt;/p&gt;
&lt;p&gt;Commenting and pasting all the code here doesn&apos;t make sense, since at this point the idea of the code, how it works, and the CDK deployment in multi-account was already explained. I highly encourage you to check the &lt;a href=&quot;https://github.com/felipelaptrin/cdk-multi-account&quot;&gt;source code&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;The chicken and the egg problem&lt;/h3&gt;
&lt;p&gt;Notice one thing, we are defining the stack that creates the Codepipeline to create the code. But it won&apos;t deploy the CDK code if the stack does not exist. We are facing the chicken and egg problem. To solve this, we should deploy (only this time) manually the codepipeline stack.&lt;/p&gt;
&lt;p&gt;After this is done, all the pushes of the code in the main branch will trigger Codepipeline execution and the CDK code will be deployed. We can easily do that by exporting the credentials to the shared-assets account and running &lt;code class=&quot;language-text&quot;&gt;yarn cdk deploy MultiAccountStack&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Cya&lt;/h2&gt;
&lt;p&gt;Hope you enjoyed this blog post! See you in the next post! 👋&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Using IaC to configure private resources]]></title><description><![CDATA[The demo GitHub repository with all the code for this project can be accessed here. The goal of this blog post is to present two ways of…]]></description><link>https://felipetrindade.com/iac-private-resources/</link><guid isPermaLink="false">https://felipetrindade.com/iac-private-resources/</guid><pubDate>Sun, 18 Aug 2024 13:44:32 GMT</pubDate><content:encoded>&lt;p&gt;The demo GitHub repository with all the code for this project can be accessed &lt;a href=&quot;https://github.com/felipelaptrin/iac-private-resources/&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The goal of this blog post is to present two ways of configuring private resources. First, let&apos;s define what I mean by private resources. Private resources are resources that are not accessible directly via the internet, i.e. are resources that are contained in a private network. When I say &quot;resources&quot; I&apos;m talking about configurations for applications/services (that are typically a Terraform resource). A good example is a DB/User in a database (which is typically deployed in a private network and can&apos;t be accessed directly), but it could have been a configuration for &lt;a href=&quot;https://registry.terraform.io/providers/goharbor/harbor/latest&quot;&gt;Harbor&lt;/a&gt;, &lt;a href=&quot;https://registry.terraform.io/providers/mrparkers/keycloak/latest&quot;&gt;Keycloak&lt;/a&gt; or any other service that can be configured. For this demo we want to create a DB in a database service.&lt;/p&gt;
&lt;p&gt;Notice that I&apos;m not talking about resources that are deployed privately but can be configured because the API is public. For example, the creation of these services (e.g. Amazon RDS) is in general not a problem because they are being created in a cloud provider (e.g. AWS) and the API for creating these resources is public (but requires authentication). Configuring all &quot;cloud-related&quot; topics for these services is not a problem (e.g. replication, backup, instance size...) but the internal configuration of this service (e.g. database creation, db user creation) requires you to connect to these services and do the configuration yourself.&lt;/p&gt;
&lt;p&gt;It is important to have these services in a private network because it&apos;s a security risk to expose them publicly so it&apos;s not worth the risk of having it exposed to easy access versus the risk that you will face in case of an attack.&lt;/p&gt;
&lt;p&gt;In this blog post, I will cover two common approaches to this problem:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;SSH Proxy&lt;/strong&gt;: Using a bastion host as to SSH-proxy the connection to the private resource&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Self Host&lt;/strong&gt;: Instead of using the bastion as a proxy, the entire CI/CD to configure these resources can be deployed inside the private network&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For this project, I&apos;ll be using &lt;a href=&quot;https://www.vultr.com/&quot;&gt;Vultr&lt;/a&gt; Cloud and &lt;a href=&quot;https://opentofu.org/&quot;&gt;Opentofu&lt;/a&gt; (the open-source version of Terraform). I wouldn&apos;t skip this in case you are looking for an AWS approach, what matters here are the concepts, that you can easily replicate to AWS scenario.&lt;/p&gt;
&lt;h2&gt;SSH-proxy&lt;/h2&gt;
&lt;p&gt;This is a well-known solution when users need to connect to private resources. The idea is to deploy a virtual machine publicly so only the developers have access and connect to this instance using SSH. Once the connection is established, the developers can access all the resources deployed inside the internal network (of course, if the firewall allows it). Ideally, you should also restrict the IPs that can connect to the bastion, but one bad thing is that usually, internet service providers (ISP) give you a dynamic IP. You can have a plan that gives you a static IP but it&apos;s usually way more expensive.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/c8ce25758f58670fd1696c612ac8ecd5/58213/ssh-proxy.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 40.50632911392405%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAYAAAD5nd/tAAAACXBIWXMAAAsTAAALEwEAmpwYAAABVklEQVR42n1Sy04CMRStv6bfYOI/uGFpTNy5dQlrY+YL2Ji4kJUxUaIhSCBCkMwAA8MA0+m8Okzn2BZGRhO5Sdub9p5zem5LcCByPekZ9rANe9SDPXcwnU5g2zaCIEAURXIN9ep5HkguAcXY4vd5iRYrswW+NpFtBJI40mRljK5ZrUAUq+M4eiNNU2RC6Hwcx3hj7AfAfArKU9y3+3jpDlBICpHDYykUynUXIPV6HYZhaFLLsmCNLSwXa1Q/HnH1fIPPkCOUolNzhIdWB0dnFZycXyIIpU3m4akzwWltgHdT3pouQZrNJhqNhrawVRRate/O8PrVQSxzkWXwpZ3ezMXxdRUXtVsIbTPHwGao3HUxnIeScA2imsl21sqRJFyKRL/6owi47J8SKCLbpOARRS4yeJSClJt6KKgs/vc37PC+7+9f+VBhUcw5lzdPEMsH+zvUmSL8BgtkYIvHjUEvAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;The image illustrates a secure architecture where an SSH proxy connects GitHub to a database within a private network via a Bastion host.&quot;
        title=&quot;&quot;
        src=&quot;/static/c8ce25758f58670fd1696c612ac8ecd5/f058b/ssh-proxy.png&quot;
        srcset=&quot;/static/c8ce25758f58670fd1696c612ac8ecd5/c26ae/ssh-proxy.png 158w,
/static/c8ce25758f58670fd1696c612ac8ecd5/6bdcf/ssh-proxy.png 315w,
/static/c8ce25758f58670fd1696c612ac8ecd5/f058b/ssh-proxy.png 630w,
/static/c8ce25758f58670fd1696c612ac8ecd5/58213/ssh-proxy.png 902w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;This is usually a cheap approach because you typically deploy a virtual machine that is small since this machine won&apos;t run workloads of application and will serve only as a network proxy for your internal network.&lt;/p&gt;
&lt;p&gt;Let&apos;s check how can we create a private network with a bastion host and database.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;hcl&quot;&gt;&lt;pre class=&quot;language-hcl&quot;&gt;&lt;code class=&quot;language-hcl&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# variables.tf&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;variable&lt;span class=&quot;token type variable&quot;&gt; &quot;public_ssh_key&quot; &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;description&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Public SSH key to attach to the authorized_keys of the bastion host&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;type&lt;/span&gt;        &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; string
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;A single variable will be used here: the public SSH key to attach to the &lt;code class=&quot;language-text&quot;&gt;authorized_keys&lt;/code&gt; file of the bastion host.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;hcl&quot;&gt;&lt;pre class=&quot;language-hcl&quot;&gt;&lt;code class=&quot;language-hcl&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# main.tf&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;locals&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;region&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;mia&quot;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# Miami&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;label&lt;/span&gt;  &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;ssh-proxy&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;############################&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;###### Networking&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;############################&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;resource &lt;span class=&quot;token type variable&quot;&gt;&quot;vultr_vpc&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;this&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;region&lt;/span&gt;         &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; local.region
  &lt;span class=&quot;token property&quot;&gt;v4_subnet&lt;/span&gt;      &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;10.0.0.0&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;v4_subnet_mask&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;24&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;resource &lt;span class=&quot;token type variable&quot;&gt;&quot;vultr_firewall_group&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;bastion&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;description&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Bastion Firewall&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;resource &lt;span class=&quot;token type variable&quot;&gt;&quot;vultr_firewall_rule&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;bastion&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;firewall_group_id&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; vultr_firewall_group.bastion.id
  &lt;span class=&quot;token property&quot;&gt;protocol&lt;/span&gt;          &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;tcp&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;ip_type&lt;/span&gt;           &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;v4&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;subnet&lt;/span&gt;            &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;0.0.0.0&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;subnet_size&lt;/span&gt;       &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;port&lt;/span&gt;              &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;22&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;notes&lt;/span&gt;             &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Allow all SSH from anywhere&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;############################&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;###### Virual Machine&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;############################&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;resource &lt;span class=&quot;token type variable&quot;&gt;&quot;vultr_ssh_key&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;bastion&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;name&lt;/span&gt;    &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;bastion&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;ssh_key&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; var.public_ssh_key
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;resource &lt;span class=&quot;token type variable&quot;&gt;&quot;vultr_instance&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;bastion&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;region&lt;/span&gt;              &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; local.region
  &lt;span class=&quot;token property&quot;&gt;vpc_ids&lt;/span&gt;             &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;vultr_vpc.this.id&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;plan&lt;/span&gt;                &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;vc2-1c-1gb&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;os_id&lt;/span&gt;               &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; data.vultr_os.ubuntu.id
  &lt;span class=&quot;token property&quot;&gt;backups&lt;/span&gt;             &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;disabled&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;label&lt;/span&gt;               &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; local.label
  &lt;span class=&quot;token property&quot;&gt;firewall_group_id&lt;/span&gt;   &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; vultr_firewall_group.bastion.id
  &lt;span class=&quot;token property&quot;&gt;ssh_key_ids&lt;/span&gt;         &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;vultr_ssh_key.bastion.id&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;enable_ipv6&lt;/span&gt;         &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;disable_public_ipv4&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;ddos_protection&lt;/span&gt;     &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;activation_email&lt;/span&gt;    &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;############################&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;###### Database&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;############################&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;resource &lt;span class=&quot;token type variable&quot;&gt;&quot;vultr_database&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;this&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;region&lt;/span&gt;                  &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; local.region
  &lt;span class=&quot;token property&quot;&gt;vpc_id&lt;/span&gt;                  &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; vultr_vpc.this.id
  &lt;span class=&quot;token property&quot;&gt;plan&lt;/span&gt;                    &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;vultr-dbaas-hobbyist-cc-1-25-1&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;database_engine&lt;/span&gt;         &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;pg&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;database_engine_version&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;15&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;label&lt;/span&gt;                   &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; local.label
  &lt;span class=&quot;token property&quot;&gt;trusted_ips&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;vultr_instance&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;bastion&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;internal_ip&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;/32&quot;&lt;/span&gt;,
  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;resource &lt;span class=&quot;token type variable&quot;&gt;&quot;postgresql_database&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;this&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;demodb&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;hcl&quot;&gt;&lt;pre class=&quot;language-hcl&quot;&gt;&lt;code class=&quot;language-hcl&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# versions.tf&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;terraform&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;required_providers&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;vultr&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;source&lt;/span&gt;  &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;vultr/vultr&quot;&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;version&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;2.21.0&quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;postgresql&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;source&lt;/span&gt;  &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;cyrilgdn/postgresql&quot;&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;version&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;1.22.0&quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;provider&lt;span class=&quot;token type variable&quot;&gt; &quot;vultr&quot; &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;module&lt;span class=&quot;token type variable&quot;&gt; &quot;db_tunnel&quot; &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;source&lt;/span&gt;  &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;flaupretre/tunnel/ssh&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;version&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;2.2.1&quot;&lt;/span&gt;

  &lt;span class=&quot;token property&quot;&gt;target_host&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; vultr_database.this.host
  &lt;span class=&quot;token property&quot;&gt;target_port&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; vultr_database.this.port

  &lt;span class=&quot;token property&quot;&gt;gateway_host&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; vultr_instance.bastion.main_ip
  &lt;span class=&quot;token property&quot;&gt;gateway_user&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;root&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;provider&lt;span class=&quot;token type variable&quot;&gt; &quot;postgresql&quot; &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;host&lt;/span&gt;     &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; module.db_tunnel.host
  &lt;span class=&quot;token property&quot;&gt;port&lt;/span&gt;     &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; module.db_tunnel.port
  &lt;span class=&quot;token property&quot;&gt;username&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; vultr_database.this.user
  &lt;span class=&quot;token property&quot;&gt;password&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; vultr_database.this.password
  &lt;span class=&quot;token property&quot;&gt;database&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; vultr_database.this.dbname
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;hcl&quot;&gt;&lt;pre class=&quot;language-hcl&quot;&gt;&lt;code class=&quot;language-hcl&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# data.tf&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;data &lt;span class=&quot;token type variable&quot;&gt;&quot;vultr_os&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;ubuntu&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;filter&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;name&lt;/span&gt;   &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;name&quot;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;values&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Ubuntu 22.04 LTS x64&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Notice that for simplicity, I&apos;m allowing the bastion instance firewall to receive traffic from anywhere but this is not a best practice in terms of security, as commented before.&lt;/p&gt;
&lt;p&gt;Terraform does not have a built-in feature to SSH-proxy, that&apos;s why you need to implement it. There is a really good module called &lt;a href=&quot;https://registry.terraform.io/modules/flaupretre/tunnel/ssh/latest&quot;&gt;tunnel&lt;/a&gt; that I&apos;ve used to create this SSH-proxy tunnel and reference the database host as the &quot;localhost&quot; of the runner.&lt;/p&gt;
&lt;h2&gt;Self-Hosted&lt;/h2&gt;
&lt;p&gt;The self-hosted approach is a very interesting approach for configuring private resources. You are fully responsible for the security, availability, operating system, and software tools presented in the runner. For the demo, I will use GitHub Action self-hosted runner.&lt;/p&gt;
&lt;p&gt;There are two important considerations:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;This might be an expensive solution for smaller teams in terms of maintenance and servers. Developers are expensive, maybe it is not suitable for your team to &quot;spend&quot; developer time to configure, maintain and deploy self-hosted runners. Also, it&apos;s unlikely that there will be jobs running late at night, so paying for an infrastructure (remember that a server to run a CI/CD will usually require more CPU/Memory to run tests, build, and stress-test) that is not in use can be expensive for a small company. Of course, you can create automation to turn on and off the servers but, again, more developer work.&lt;/li&gt;
&lt;li&gt;Do not use self-hosted runners if your repository is &lt;a href=&quot;https://docs.github.com/en/actions/hosting-your-own-runners/managing-self-hosted-runners/about-self-hosted-runners#self-hosted-runner-security&quot;&gt;public&lt;/a&gt;. This is because if the repository is public anyone can fork your repository and create a pull request that executes code in a workflow, which can be treated as a security risk.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/6af7bfdafe74a8f1b48c29762798cf90/a2b88/self-hosted.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 39.87341772151899%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAYAAAD5nd/tAAAACXBIWXMAAAsTAAALEwEAmpwYAAABSklEQVR42o2Rz0rDQBDGc/OVfAavHj36AkLFdxCvvVQE8dpn8CbUQ5CebGxIbTG0SZtom+yfZLObfO6mGmNR8YNhZ2dnfjPMWtCqqgo/qdo+1v7rYoJgOkKwjBCGIYIgACEEjLHGkiSB9QnctQZq7vrcLBzItymkLJFnvIbtDmJiVhRF8H3/W7FRmOcY0rT2ValA0hSkkLh1nmE/TRpYWVYgTOocA0xh9ft99Ho9uK6LOI6xXK2QkRyXozucDc4xjDaI5nNM3DEGYw97R6fYP+5gvUmQ0QT3j3Mcdj08zBhkpid0HAe2bYNzrruVUErVnV/WMezZCKmeSkkJod/9hOLg4gqd7jXkR950SXBy48ALGXKugW1IW0IUoIw3OzX7MZKF0I2/8pUsUPAEpZLbHf72y+1IG/iXKKVb4H9kgEKIejWmcNdM3Ez4DuQFYSkflE6lAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;The image illustrates a secure setup where a GitHub Runner within a private network uses long polling to communicate with GitHub over the internet, accessing a database within the same network.&quot;
        title=&quot;&quot;
        src=&quot;/static/6af7bfdafe74a8f1b48c29762798cf90/f058b/self-hosted.png&quot;
        srcset=&quot;/static/6af7bfdafe74a8f1b48c29762798cf90/c26ae/self-hosted.png 158w,
/static/6af7bfdafe74a8f1b48c29762798cf90/6bdcf/self-hosted.png 315w,
/static/6af7bfdafe74a8f1b48c29762798cf90/f058b/self-hosted.png 630w,
/static/6af7bfdafe74a8f1b48c29762798cf90/a2b88/self-hosted.png 908w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;The code for provisioning this infrastructure is really similar to the one we&apos;ve used in the SSH-proxy.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;hcl&quot;&gt;&lt;pre class=&quot;language-hcl&quot;&gt;&lt;code class=&quot;language-hcl&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# variables.tf&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;variable&lt;span class=&quot;token type variable&quot;&gt; &quot;repo_name&quot; &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;description&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Github repository name including the owner (e.g. felipelaptrin/my-repo)&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;type&lt;/span&gt;        &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; string
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;variable&lt;span class=&quot;token type variable&quot;&gt; &quot;public_ssh_key&quot; &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;description&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Public SSH key to attach to the authorized_keys of the self-hosted runner host&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;type&lt;/span&gt;        &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; string
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;variable&lt;span class=&quot;token type variable&quot;&gt; &quot;github_actions_version&quot; &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;description&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Version that the self-hosted runner will use. You can check all version here: https://github.com/actions/runner/releases&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;type&lt;/span&gt;        &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; string
  &lt;span class=&quot;token property&quot;&gt;default&lt;/span&gt;     &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;v2.319.1&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Notice that in addition to the SSH key to connect to the self-hosted runner host (in this case only to debug if needed), there are two more variables: the GitHub runner version and the repository name that the self runner will be configured (needed to retrieve the runner token to connect to GitHub and register the runner).&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;hcl&quot;&gt;&lt;pre class=&quot;language-hcl&quot;&gt;&lt;code class=&quot;language-hcl&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# main.tf&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;locals&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;region&lt;/span&gt;                 &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;mia&quot;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# Miami&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;label&lt;/span&gt;                  &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;self-hosted&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;github_actions_version&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; split(&lt;span class=&quot;token string&quot;&gt;&quot;v&quot;&lt;/span&gt;, var.github_actions_version)&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;repo_name&lt;/span&gt;              &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; split(&lt;span class=&quot;token string&quot;&gt;&quot;/&quot;&lt;/span&gt;, var.repo_name)&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;############################&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;###### Networking&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;############################&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;resource &lt;span class=&quot;token type variable&quot;&gt;&quot;vultr_vpc&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;this&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;region&lt;/span&gt;         &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; local.region
  &lt;span class=&quot;token property&quot;&gt;v4_subnet&lt;/span&gt;      &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;10.0.0.0&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;v4_subnet_mask&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;24&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;resource &lt;span class=&quot;token type variable&quot;&gt;&quot;vultr_firewall_group&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;runner&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;description&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Github self-runner Firewall&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;resource &lt;span class=&quot;token type variable&quot;&gt;&quot;vultr_firewall_rule&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;runner&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;firewall_group_id&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; vultr_firewall_group.runner.id
  &lt;span class=&quot;token property&quot;&gt;protocol&lt;/span&gt;          &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;tcp&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;ip_type&lt;/span&gt;           &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;v4&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;subnet&lt;/span&gt;            &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;0.0.0.0&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;subnet_size&lt;/span&gt;       &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;port&lt;/span&gt;              &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;22&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;notes&lt;/span&gt;             &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Allow all SSH from anywhere&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;############################&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;###### Virual Machine&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;############################&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;resource &lt;span class=&quot;token type variable&quot;&gt;&quot;vultr_ssh_key&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;runner&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;name&lt;/span&gt;    &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;runner&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;ssh_key&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; var.public_ssh_key
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;resource &lt;span class=&quot;token type variable&quot;&gt;&quot;vultr_instance&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;runner&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;region&lt;/span&gt;              &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; local.region
  &lt;span class=&quot;token property&quot;&gt;vpc_ids&lt;/span&gt;             &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;vultr_vpc.this.id&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;plan&lt;/span&gt;                &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;vc2-1c-1gb&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;os_id&lt;/span&gt;               &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; data.vultr_os.ubuntu.id
  &lt;span class=&quot;token property&quot;&gt;backups&lt;/span&gt;             &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;disabled&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;label&lt;/span&gt;               &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; local.label
  &lt;span class=&quot;token property&quot;&gt;firewall_group_id&lt;/span&gt;   &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; vultr_firewall_group.runner.id
  &lt;span class=&quot;token property&quot;&gt;ssh_key_ids&lt;/span&gt;         &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;vultr_ssh_key.runner.id&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;enable_ipv6&lt;/span&gt;         &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;disable_public_ipv4&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;ddos_protection&lt;/span&gt;     &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;activation_email&lt;/span&gt;    &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;user_data&lt;/span&gt;           &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token heredoc string&quot;&gt;&amp;lt;&amp;lt;EOF
#!/bin/bash
echo &quot;ubuntu ALL=(ALL) NOPASSWD: ALL&quot; &gt;&gt; /etc/sudoers
cd /home/ubuntu &amp;amp;&amp;amp; mkdir actions-runner &amp;amp;&amp;amp; cd actions-runner
curl -o actions-runner-linux.tar.gz -L https://github.com/actions/runner/releases/download/${var.github_actions_version}/actions-runner-linux-x64-${local.github_actions_version}.tar.gz
tar xzf ./actions-runner-linux.tar.gz
sudo chown -R ubuntu /home/ubuntu/actions-runner
sudo -u ubuntu ./config.sh --name vultr --replace --url https://github.com/${var.repo_name} --token ${sensitive(data.github_actions_registration_token.this.token)}
sudo -u ubuntu ./run.sh
  EOF&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;lifecycle&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;ignore_changes&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
      user_data
     &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;############################&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;###### Database&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;############################&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;resource &lt;span class=&quot;token type variable&quot;&gt;&quot;vultr_database&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;this&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;region&lt;/span&gt;                  &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; local.region
  &lt;span class=&quot;token property&quot;&gt;vpc_id&lt;/span&gt;                  &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; vultr_vpc.this.id
  &lt;span class=&quot;token property&quot;&gt;plan&lt;/span&gt;                    &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;vultr-dbaas-hobbyist-cc-1-25-1&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;database_engine&lt;/span&gt;         &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;pg&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;database_engine_version&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;15&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;label&lt;/span&gt;                   &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; local.label
  &lt;span class=&quot;token property&quot;&gt;trusted_ips&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;vultr_instance&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;runner&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;internal_ip&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;/32&quot;&lt;/span&gt;,
  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;resource &lt;span class=&quot;token type variable&quot;&gt;&quot;postgresql_database&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;this&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;demodb&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;hcl&quot;&gt;&lt;pre class=&quot;language-hcl&quot;&gt;&lt;code class=&quot;language-hcl&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# versions.tf&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;terraform&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;required_providers&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;vultr&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;source&lt;/span&gt;  &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;vultr/vultr&quot;&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;version&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;2.21.0&quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;github&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;source&lt;/span&gt;  &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;integrations/github&quot;&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;version&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;6.2.3&quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;postgresql&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;source&lt;/span&gt;  &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;cyrilgdn/postgresql&quot;&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;version&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;1.22.0&quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;provider&lt;span class=&quot;token type variable&quot;&gt; &quot;vultr&quot; &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;provider&lt;span class=&quot;token type variable&quot;&gt; &quot;github&quot; &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;provider&lt;span class=&quot;token type variable&quot;&gt; &quot;postgresql&quot; &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;host&lt;/span&gt;     &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; vultr_database.this.host
  &lt;span class=&quot;token property&quot;&gt;port&lt;/span&gt;     &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; vultr_database.this.port
  &lt;span class=&quot;token property&quot;&gt;username&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; vultr_database.this.user
  &lt;span class=&quot;token property&quot;&gt;password&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; vultr_database.this.password
  &lt;span class=&quot;token property&quot;&gt;database&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; vultr_database.this.dbname
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;hcl&quot;&gt;&lt;pre class=&quot;language-hcl&quot;&gt;&lt;code class=&quot;language-hcl&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# data.tf&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;data &lt;span class=&quot;token type variable&quot;&gt;&quot;vultr_os&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;ubuntu&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;filter&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;name&lt;/span&gt;   &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;name&quot;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;values&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Ubuntu 22.04 LTS x64&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;data &lt;span class=&quot;token type variable&quot;&gt;&quot;github_actions_registration_token&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;this&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;repository&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; local.repo_name
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Notice that now we are directly connecting to the database: not proxy, just direct connection.&lt;/p&gt;
&lt;h2&gt;CI/CD using GitHub Actions&lt;/h2&gt;
&lt;p&gt;We don&apos;t want to apply things manually, let&apos;s use a pipeline workflow to deploy our infrastructure using GitHub Actions! The idea is simple:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;During pull request&lt;/strong&gt;: Plan the actions that will be performed and &lt;a href=&quot;https://stackoverflow.com/questions/67963719/should-terraform-lock-hcl-be-included-in-the-gitignore-file&quot;&gt;commit&lt;/a&gt; the &lt;a href=&quot;https://developer.hashicorp.com/terraform/language/files/dependency-lock&quot;&gt;dependency lock file&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;When pull request is merged&lt;/strong&gt;: Deploy the infrastructure.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Two jobs were created (one for ssh-proxy and the other one for self-hosted) and to not repeat code two composite actions (plan and deploy) were also created. Let&apos;s check the actions first&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# .github/actions/plan/action.yaml&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; plan
&lt;span class=&quot;token key atrule&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Plan IaC

&lt;span class=&quot;token key atrule&quot;&gt;inputs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;folder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Name of the folder that terraform will run
    &lt;span class=&quot;token key atrule&quot;&gt;required&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean important&quot;&gt;true&lt;/span&gt;

&lt;span class=&quot;token key atrule&quot;&gt;runs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;using&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;composite&quot;&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Install devbox
      &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; jetify&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;com/devbox&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;install&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;action@v0.11.0

    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Configure Git
      &lt;span class=&quot;token key atrule&quot;&gt;shell&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; bash
      &lt;span class=&quot;token key atrule&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token scalar string&quot;&gt;
        git config --global user.name &quot;github-actions[bot]&quot;
        git config --global user.email &quot;github-actions[bot]@users.noreply.github.com&quot;&lt;/span&gt;

    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; webfactory/ssh&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;agent@v0.9.0
      &lt;span class=&quot;token key atrule&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;ssh-private-key&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; env.SSH_PRIVATE_KEY &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Plan
      &lt;span class=&quot;token key atrule&quot;&gt;shell&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; bash
      &lt;span class=&quot;token key atrule&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;FOLDER&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; inputs.folder &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;VULTR_API_KEY&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; env.VULTR_API_KEY &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;AWS_ACCESS_KEY&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; env.AWS_ACCESS_KEY &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;AWS_SECRET_ACCESS_KEY&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; env.AWS_SECRET_ACCESS_KEY &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;GITHUB_TOKEN&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; env.PAT_GITHUB &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token scalar string&quot;&gt;
        devbox run tofu -chdir=$FOLDER init
        devbox run tofu -chdir=$FOLDER plan -var-file=variables.tfvars&lt;/span&gt;

    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Install GH CLI
      &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; dev&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;hanz&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;ops/install&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;gh&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;cli&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;action@v0.1.0
      &lt;span class=&quot;token key atrule&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;gh-cli-version&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 2.54.0

    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Commit .terraform.lock.hcl files
      &lt;span class=&quot;token key atrule&quot;&gt;working-directory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; inputs.folder &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; github.ref &lt;span class=&quot;token tag&quot;&gt;!=&lt;/span&gt; &apos;refs/heads/main&apos;
      &lt;span class=&quot;token key atrule&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;GITHUB_TOKEN&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; env.GITHUB_TOKEN &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;shell&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; bash
      &lt;span class=&quot;token key atrule&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token scalar string&quot;&gt;
        if [ -n &quot;$(git status --porcelain)&quot; ]; then
          gh pr checkout ${{ github.event.pull_request.number }}
          git add .
          git commit -m &quot;Add .terraform.lock.hcl files&quot;
          git push
        else
          echo &quot;Nothing to commit.&quot;
        fi&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# .github/actions/apply/action.yaml&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; apply
&lt;span class=&quot;token key atrule&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Apply IaC

&lt;span class=&quot;token key atrule&quot;&gt;inputs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;folder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Name of the folder that opentofu will run
    &lt;span class=&quot;token key atrule&quot;&gt;required&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean important&quot;&gt;true&lt;/span&gt;

&lt;span class=&quot;token key atrule&quot;&gt;runs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;using&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;composite&quot;&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Install devbox
      &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; jetify&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;com/devbox&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;install&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;action@v0.11.0

    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; webfactory/ssh&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;agent@v0.9.0
      &lt;span class=&quot;token key atrule&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;ssh-private-key&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; env.SSH_PRIVATE_KEY &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Apply Opentofu
      &lt;span class=&quot;token key atrule&quot;&gt;shell&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; bash
      &lt;span class=&quot;token key atrule&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;FOLDER&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; inputs.folder &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;VULTR_API_KEY&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; env.VULTR_API_KEY &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;AWS_ACCESS_KEY&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; env.AWS_ACCESS_KEY &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;AWS_SECRET_ACCESS_KEY&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; env.AWS_SECRET_ACCESS_KEY &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;GITHUB_TOKEN&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; env.GITHUB_TOKEN &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token scalar string&quot;&gt;
        devbox run tofu -chdir=$FOLDER init
        devbox run tofu -chdir=$FOLDER apply -var-file=variables.tfvars -auto-approve&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Notice that several env vars are required to fully plan/run Opentofu:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;VULTR_API_KEY&lt;/code&gt;: &lt;a href=&quot;https://registry.terraform.io/providers/vultr/vultr/latest/docs&quot;&gt;Required&lt;/a&gt; to authenticate with Vultr Cloud. This API Key is safely stored in the GitHub Secrets of the repository.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;AWS_ACCESS_KEY&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;AWS_SECRET_ACCESS_KEY&lt;/code&gt;: I didn&apos;t paste the &lt;a href=&quot;https://github.com/felipelaptrin/iac-private-resources/blob/main/self-hosted/backend.tf&quot;&gt;backend.tf&lt;/a&gt; code because it&apos;s not relevant to the post, but I&apos;ve configured a Vultr Object Storage (similar to S3) to store the state of Opentofu and these environment variables are &lt;a href=&quot;https://docs.vultr.com/how-to-store-terraform-state-in-vultr-object-storage&quot;&gt;required&lt;/a&gt;. These keys are safely stored in the GitHub Secrets of the repository.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;GITHUB_TOKEN&lt;/code&gt;: Notice that in the plan action, I&apos;m using two different values for this environment value. The first one is &lt;a href=&quot;https://docs.github.com/en/actions/security-for-github-actions/security-guides/automatic-token-authentication&quot;&gt;automatically&lt;/a&gt; created by GitHub and it&apos;s being used to use GitHub Bot to commit the dependency lock files of Opentofu. The second value is my GitHub PAT token (Personal Access Token) that is being used to get the token to register the self-hosted runner (check the &lt;code class=&quot;language-text&quot;&gt;data.tf&lt;/code&gt; file of the self-hosted manifests). This PAT token is safely stored in the GitHub Secrets of the repository.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;SSH_PRIVATE_KEY&lt;/code&gt;: This is the private SSH key that can be used to connect to the bastion host and self-hosted runer. The latter is necessary only in case of debugging since all the self-runner configuration is being done in the user_data script.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now, let&apos;s check how the workflow looks like&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# .github/workflows/main.yaml&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Opentofu Actions&quot;&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;pull_request&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;branches&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; main

&lt;span class=&quot;token key atrule&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;AWS_ACCESS_KEY&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;${{ secrets.AWS_ACCESS_KEY }}&quot;&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;AWS_SECRET_ACCESS_KEY&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;${{ secrets.AWS_SECRET_ACCESS_KEY }}&quot;&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;VULTR_API_KEY&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;${{ secrets.VULTR_API_KEY }}&quot;&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;SSH_PRIVATE_KEY&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; secrets.SSH_PRIVATE_KEY &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token key atrule&quot;&gt;permissions&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;contents&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; write

&lt;span class=&quot;token key atrule&quot;&gt;jobs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;ssh-proxy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;runs-on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ubuntu&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;latest
    &lt;span class=&quot;token key atrule&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Checkout&quot;&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; actions/checkout@v4

      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Plan ssh&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;proxy
        &lt;span class=&quot;token key atrule&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; github.ref &lt;span class=&quot;token tag&quot;&gt;!=&lt;/span&gt; &apos;refs/heads/main&apos;
        &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ./.github/actions/plan
        &lt;span class=&quot;token key atrule&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;GITHUB_TOKEN&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; secrets.GITHUB_TOKEN &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;PAT_GITHUB&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; secrets.PAT_GITHUB &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;folder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ssh&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;proxy

      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Apply ssh&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;proxy
        &lt;span class=&quot;token key atrule&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; github.ref == &apos;refs/heads/main&apos;
        &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ./.github/actions/apply
        &lt;span class=&quot;token key atrule&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;GITHUB_TOKEN&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; secrets.PAT_GITHUB &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;folder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ssh&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;proxy

  &lt;span class=&quot;token key atrule&quot;&gt;self-hosted&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;runs-on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; self&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;hosted
    &lt;span class=&quot;token key atrule&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Checkout&quot;&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; actions/checkout@v4

      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Plan self&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;hosted
        &lt;span class=&quot;token key atrule&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; github.ref &lt;span class=&quot;token tag&quot;&gt;!=&lt;/span&gt; &apos;refs/heads/main&apos;
        &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ./.github/actions/plan
        &lt;span class=&quot;token key atrule&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;GITHUB_TOKEN&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; secrets.GITHUB_TOKEN &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;PAT_GITHUB&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; secrets.PAT_GITHUB &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;folder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; self&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;hosted

      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Apply self&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;hosted
        &lt;span class=&quot;token key atrule&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; github.ref == &apos;refs/heads/main&apos;
        &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ./.github/actions/apply
        &lt;span class=&quot;token key atrule&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;GITHUB_TOKEN&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; secrets.PAT_GITHUB &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;folder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; self&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;hosted&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Great!&lt;/p&gt;
&lt;p&gt;Wait, we are not over! Did you notice the chicken and the egg problem when using self-hosted? Well, we want to run a CI/CD pipeline to deploy our infrastructure. This infrastructure declares the runner that the CI/CD will run (self-hosted). When you commit this, GitHub Actions will try to run this on the self-hosted runners (that does not exist) and it&apos;s trying to create itself. Not good.&lt;/p&gt;
&lt;p&gt;How can we solve that? This is what we call a bootstrap process. We can do this in a two-step process:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Comment the resource &lt;code class=&quot;language-text&quot;&gt;postgresql_database.this&lt;/code&gt; (&lt;code class=&quot;language-text&quot;&gt;data.tf&lt;/code&gt; file) and modify the &lt;code class=&quot;language-text&quot;&gt;runs-on&lt;/code&gt; input of the &lt;code class=&quot;language-text&quot;&gt;self-hosted&lt;/code&gt; job to use &lt;code class=&quot;language-text&quot;&gt;ubuntu-latest&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Apply the infrastructure (via GitHub Actions managed runners). After done, uncomment the resource &lt;code class=&quot;language-text&quot;&gt;postgresql_database.this&lt;/code&gt; and add back &lt;code class=&quot;language-text&quot;&gt;self-hosted&lt;/code&gt; input.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That&apos;s one way of doing this. You can also use scrips or have a completely separate workflow only for setting the self-hosted runners to solve this problem.&lt;/p&gt;
&lt;h2&gt;Cya&lt;/h2&gt;
&lt;p&gt;Hope you enjoyed this blog post! See you in the next post! 👋&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Deploying public and private applications in Kubernetes]]></title><description><![CDATA[The goal of this blog post is to deploy a Kubernetes cluster with a public and a private application. This is a common requirement in…]]></description><link>https://felipetrindade.com/public-private-apps/</link><guid isPermaLink="false">https://felipetrindade.com/public-private-apps/</guid><pubDate>Mon, 12 Aug 2024 10:40:32 GMT</pubDate><content:encoded>&lt;p&gt;The goal of this blog post is to deploy a Kubernetes cluster with a public and a private application. This is a common requirement in several companies because usually their main service is exposed publicly but internal tools (e.g. Grafana, Airflow) shouldn&apos;t be exposed publicly, even if they require authentication, so are deployed internally/privately.&lt;/p&gt;
&lt;p&gt;Several tools will be used during the process. Cert Manager, Kubernetes Replicator, External DNS and NGINX Ingress will help us achieve this goal. This will be explained in detail during the blog post, so don&apos;t freak out!&lt;/p&gt;
&lt;p&gt;For this demo, I will use Vultr to provision the Kubernetes cluster. If you are using a different cloud provider (such as AWS) I would still read this post, since everything here is valid and I will comment exactly where things need to change if you are using a different cloud provider.&lt;/p&gt;
&lt;p&gt;All the manifests used here are in the &lt;a href=&quot;https://github.com/felipelaptrin/private-public-kubernetes-apps&quot;&gt;GitHub repository&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Ingress and NGINX Ingress Controller&lt;/h2&gt;
&lt;p&gt;Instead of exposing every Kubernetes application using a LoadBalancer service type, which would require the deployment of a single LoadBalancer per application in your cloud provider, a good practice is to reuse the LoadBalancer to expose multiple services and route these internally using an Ingress.&lt;/p&gt;
&lt;p&gt;An Ingress only works with an Ingress Controller, which is not deployed in Kubernetes by default. There are &lt;a href=&quot;https://kubernetes.io/docs/concepts/services-networking/ingress-controllers/#additional-controllers&quot;&gt;several controllers available&lt;/a&gt; out there. One of the most popular options available is the &lt;a href=&quot;https://github.com/kubernetes/ingress-nginx&quot;&gt;NGINX Ingress Controller&lt;/a&gt;, which is cloud-agnostic and open-source.&lt;/p&gt;
&lt;p&gt;The NGINX Ingress Controller will be responsible for requesting a Load Balancer (layer 4) to your cloud provider and will manage the routing to your services. So imagine the following scenario (for simplicity consider all apps are public):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Application A&lt;/strong&gt;: Configured in the ingress manifest to be accessible in the &lt;code class=&quot;language-text&quot;&gt;app-a.mydomain.com&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Application B&lt;/strong&gt;: Configured in the ingress manifest to be accessible in the &lt;code class=&quot;language-text&quot;&gt;app-b.mydomain.com&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you resolve these names (e.g. using &lt;code class=&quot;language-text&quot;&gt;dig&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;nslookup&lt;/code&gt;) you will see that they resolve to the same IP, because it&apos;s indeed a single Load Balancer exposed and all the routing is happening inside Kubernetes by the Ingress controller (in this case NGINX Ingress). This will be demoed, don&apos;t worry!&lt;/p&gt;
&lt;p&gt;It&apos;s also in the &lt;a href=&quot;https://kubernetes.io/docs/reference/kubernetes-api/service-resources/ingress-v1/#IngressSpec&quot;&gt;ingress manifest&lt;/a&gt; that we define if the application will be exposed securely (using a TLS certificate). If HTTPS is configured we need to specify the name of the Kubernetes secret that contains the certificates in the ingress manifest.&lt;/p&gt;
&lt;h2&gt;Let&apos;s Encrypt and Cert Manager&lt;/h2&gt;
&lt;p&gt;Now that we know that we can use NGINX Ingress Controller to expose our applications using a single Load Balancer and we can specify in the ingress manifest the certificates of the TLS the question now is how the TLS certificate gets generated.&lt;/p&gt;
&lt;p&gt;You can self-sign your certificate but who is going to trust you? By default browsers and operational systems come with a list of CA (Certificate Authorities) that are trustable to issue certificates (e.g. &lt;a href=&quot;https://ccadb.my.salesforce-sites.com/mozilla/IncludedCACertificateReport&quot;&gt;Firefox included certificates&lt;/a&gt;). If you self-sign this certificate you will need to add yourself as a valid CA in the computers of the users (which is something that is not practical, but that can be done in an on-premise environment). To summarize, a self-signed certificate is not practical even for on-premise/private environments is something tedious.&lt;/p&gt;
&lt;p&gt;Another option is to buy a certificate from one of these CA companies (e.g. &lt;a href=&quot;https://www.digicert.com/&quot;&gt;Digicert&lt;/a&gt; or &lt;a href=&quot;https://www.sectigo.com/&quot;&gt;Segtigo&lt;/a&gt; - this is not an advertisement, I&apos;m just giving examples and I honestly recommend certificates that can be issued for free by LetsEncrypt) but this is usually expensive.&lt;/p&gt;
&lt;p&gt;A really popular and free option is to issue certificates from a nonprofit CA company called &lt;a href=&quot;https://letsencrypt.org/&quot;&gt;Let&apos;s Encrypt&lt;/a&gt;. According to &lt;a href=&quot;https://w3techs.com/technologies/history_overview/ssl_certificate/all&quot;&gt;W3Techs&lt;/a&gt;, around 53% of the websites use a certificate issued by Let&apos;s Encrypt!&lt;/p&gt;
&lt;p&gt;For a certificate to be issued to your domain/website you will need to prove that you own the domain (this is called a &lt;code class=&quot;language-text&quot;&gt;challenge&lt;/code&gt;). Let&apos;s Encrypt uses ACME protocol to verify that you control the given domain. There are &lt;a href=&quot;https://letsencrypt.org/docs/client-options/&quot;&gt;several ACME clients&lt;/a&gt; that you can use to issue the certificate, for Kubernetes the most popular tool is &lt;a href=&quot;https://github.com/cert-manager/cert-manager&quot;&gt;Cert Manager&lt;/a&gt;. Let&apos;s Encrypt accepts the following &lt;a href=&quot;https://letsencrypt.org/docs/challenge-types/&quot;&gt;challenges&lt;/a&gt; from the ACME protocol:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;HTTP-01&lt;/strong&gt;: Let&apos;s Encrypt gives you a token and you must expose this token in a given route/path (&lt;code class=&quot;language-text&quot;&gt;/.well-known/acme-challenge/&amp;lt;TOKEN&gt;&lt;/code&gt;) in your website. Let&apos;s Encrypt will try to retrieve (several times from different servers) this token and check if this is the same token he sent to your ACME client. If it&apos;s the same then a new certificate will be issued, otherwise it will fail, meaning that you probably don&apos;t own/manage the domain you are trying to issue a certificate. Notice that your application must be public so Let&apos;s Encrypt can reach your server.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;DNS-01&lt;/strong&gt;: Instead of managing a route/path to expose the token in your application, DNS-01 challenge will verify that you own the domain by checking TXT records in your domain. Let&apos;s Encrypt gives you a TXT value to add to the domain and will check if the value was updated as expected, if so, it means you own the domain and the certificate will be issued. DNS-01 allows issuing certificates with wildcards, while HTTP-01 doesn&apos;t.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Some things that are important to mention:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The tokens/TXT values are not sensitive. They are only used to prove that you own the domain. Users who resolve the TXT record (DNS-01) or access the path containing the token (HTTP-01) can&apos;t do anything with this information&lt;/li&gt;
&lt;li&gt;The certificate received is public and the private key is created by the ACME client (in our case Cert-Manager) and will be stored in a Kubernetes secret (once the certificate is issued, the public certificate will also be added to the secret)&lt;/li&gt;
&lt;li&gt;The encryption and decryption part is done in the NGINX Ingress Controller pod&lt;/li&gt;
&lt;li&gt;The certificate is valid for &lt;a href=&quot;https://letsencrypt.org/docs/faq/#what-is-the-lifetime-for-let-s-encrypt-certificates-for-how-long-are-they-valid&quot;&gt;90 days&lt;/a&gt; and needs to be renewed before this time&lt;/li&gt;
&lt;li&gt;You can issue 5 certificates per week, this is a hard &lt;a href=&quot;https://letsencrypt.org/docs/rate-limits/&quot;&gt;rate limit&lt;/a&gt; and you can not change it, so if you are still testing your application you should use the &lt;a href=&quot;https://letsencrypt.org/docs/staging-environment/&quot;&gt;staging environment&lt;/a&gt; of Let&apos;s Encrypt&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Both solutions are possible to be used in production but DNS-01 is preferred over HTTP-01. First, because it works for public and private applications. Second, because you can issue the certificate before the application is deployed, so no period of missing the certificate. We are going to use DNS-01 challenge since we have private applications and because we are going to issue a wildcard certificate. We will need to provide credentials to Cert Manager so it can go to our DNS and add the TXT record.&lt;/p&gt;
&lt;h2&gt;Kubernetes Replicator&lt;/h2&gt;
&lt;p&gt;As commented, when you issue the certificate, Cert Manager will create a Kubernetes secret containing the private and public keys that will be used by the ingress. As we know, Kubernetes secrets are namespace-scoped so if you have multiple applications in different namespaces there are a few options when using DNS-01:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Issue one certificate per every application, i.e. one certificate for &lt;code class=&quot;language-text&quot;&gt;app-a.mydomain.com&lt;/code&gt;, another for &lt;code class=&quot;language-text&quot;&gt;app-b.mydomain.com&lt;/code&gt; and so on (this is exactly what you have to do if you use HTTP-01)&lt;/li&gt;
&lt;li&gt;Issue one wildcard certificate per every namespace, i.e. a certificate for &lt;code class=&quot;language-text&quot;&gt;*.mydomain.com&lt;/code&gt; in the &lt;code class=&quot;language-text&quot;&gt;apps&lt;/code&gt; namespace, &lt;code class=&quot;language-text&quot;&gt;*.mydomain.com&lt;/code&gt; in the &lt;code class=&quot;language-text&quot;&gt;monitoring&lt;/code&gt; namespace and so on.&lt;/li&gt;
&lt;li&gt;Issue a single wildcard certificate and replicate this secret in every namespace needed&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I&apos;ll use the third approach, especially because this will reduce the number of certificates issued, which can be critical for deployments in a big Kubernetes cluster. Since there is no built-in solution for Kubernetes to replicate a Kubernetes secret we need a tool for that. I will use &lt;a href=&quot;https://github.com/EmberStack/kubernetes-reflector&quot;&gt;Kubernetes Reflector&lt;/a&gt; but other tools can be used, such as &lt;a href=&quot;https://github.com/mittwald/kubernetes-replicator&quot;&gt;Kubernetes Replicator&lt;/a&gt;. The usage is as simple as installing the tool and adding annotations to the secret you want to replicate.&lt;/p&gt;
&lt;h2&gt;DNS records and External DNS&lt;/h2&gt;
&lt;p&gt;We commented that ingress allows us to expose our applications (internal-facing or public-facing) using a Load Balancer and routing using an ingress controller internal to the cluster. We need to add these names (e.g. &lt;code class=&quot;language-text&quot;&gt;app-a.mydomain.com&lt;/code&gt;) to our DNS so clients can resolve this name into an IP.&lt;/p&gt;
&lt;p&gt;We definitely don&apos;t want to manage this by hand, since this is tedious and prone to error. We can automate this task by using a well-known Kubernetes tool called &lt;a href=&quot;https://github.com/kubernetes-sigs/external-dns&quot;&gt;External DNS&lt;/a&gt;. External DNS will be responsible for adding the DNS records to the DNS automatically!&lt;/p&gt;
&lt;p&gt;The configuration is simple and it connects to several DNS providers, such as AWS, GCP, Azure, Linode, Digital Ocean, Vultr... We simply need to add permissions to connect and execute changes in the DNS and we are good to go! External DNS automatically monitors created ingresses.&lt;/p&gt;
&lt;h2&gt;Demo&lt;/h2&gt;
&lt;p&gt;Let&apos;s demo with a step-by-step approach. I will use Helm Chart with Kustomize to deploy the applications, since using GitOps (e.g. using ArgoCD) is out of the scope of this demo. For the infrastructure, I will use Terraform.&lt;/p&gt;
&lt;h3&gt;Creating the Kubernetes cluster&lt;/h3&gt;
&lt;p&gt;I&apos;m using Vultr to deploy my Kubernetes cluster, so let&apos;s use Vultr Terraform provider.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;hcl&quot;&gt;&lt;pre class=&quot;language-hcl&quot;&gt;&lt;code class=&quot;language-hcl&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# versions.tf&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;terraform&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;required_providers&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;vultr&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;source&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;vultr/vultr&quot;&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;version&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;2.21.0&quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;provider&lt;span class=&quot;token type variable&quot;&gt; &quot;vultr&quot; &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Creating a Kubernetes cluster is as easy as providing a single terraform resource&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;hcl&quot;&gt;&lt;pre class=&quot;language-hcl&quot;&gt;&lt;code class=&quot;language-hcl&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# main.tf&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;resource &lt;span class=&quot;token type variable&quot;&gt;&quot;vultr_kubernetes&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;this&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;region&lt;/span&gt;  &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;mia&quot;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# Miami&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;label&lt;/span&gt;   &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;terraform&quot;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;version&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;v1.30.0+1&quot;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;node_pools&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;node_quantity&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;plan&lt;/span&gt;          &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;vc2-2c-4gb&quot;&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;label&lt;/span&gt;         &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;vke-nodepool&quot;&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;auto_scaler&lt;/span&gt;   &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;output&lt;span class=&quot;token type variable&quot;&gt; &quot;cluster_id&quot; &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;description&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;ID of Kubernetes cluster&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;value&lt;/span&gt;       &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; vultr_kubernetes.this.id
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;To interact with Vultr API you need to set your environment variable to use the API key of your account (e.g. &lt;code class=&quot;language-text&quot;&gt;export VULTR_API_KEY=BRYAAFNWHYEFY2ZHGTQSDZCHEA&lt;/code&gt;). Make sure you have Terraform or Opentofu (I will use OpenTofu - which is the open-source version of Terraform) and run:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;tofu init
tofu apply&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You can use Vultr-CLI to get the kubeconfig (it will).&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;vultr-cli kubernetes config &lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;tofu output &lt;span class=&quot;token parameter variable&quot;&gt;-raw&lt;/span&gt; cluster_id&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;Installing Kubernetes Reflector&lt;/h3&gt;
&lt;p&gt;Let&apos;s use the Helm Chart to install the Kubernetes Reflector&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# namespace.yaml&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; v1
&lt;span class=&quot;token key atrule&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Namespace
&lt;span class=&quot;token key atrule&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; reflector
  &lt;span class=&quot;token key atrule&quot;&gt;labels&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;app.kubernetes.io/name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; reflector
&lt;span class=&quot;token punctuation&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# kustomization.yaml&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; kustomize.config.k8s.io/v1beta1
&lt;span class=&quot;token key atrule&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Kustomization
&lt;span class=&quot;token key atrule&quot;&gt;resources&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; namespace.yaml
&lt;span class=&quot;token key atrule&quot;&gt;helmCharts&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; reflector
    &lt;span class=&quot;token key atrule&quot;&gt;repo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; https&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;//emberstack.github.io/helm&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;charts
    &lt;span class=&quot;token key atrule&quot;&gt;releaseName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; reflector
    &lt;span class=&quot;token key atrule&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; reflector
    &lt;span class=&quot;token key atrule&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 7.1.288&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;Installing NGINX Ingress Controller&lt;/h3&gt;
&lt;p&gt;We will need two Load Balancers: one for the public apps and another one for the private apps, because of that we will deploy the NGINX Ingress Controller &lt;a href=&quot;https://kubernetes.github.io/ingress-nginx/user-guide/multiple-ingress/#multiple-ingress-controllers&quot;&gt;two times&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;NGINX pod will request to your Cloud Provider the Load Balancers so you will need to provide credentials to the pod to interact with the cloud. For Vultr this is as simple as providing the API token. For AWS you could provide the AWS Access Keys (this is not a best practice), &lt;a href=&quot;https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html&quot;&gt;IRSA&lt;/a&gt; or &lt;a href=&quot;https://docs.aws.amazon.com/eks/latest/userguide/pod-identities.html&quot;&gt;Pod Identity&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Notice that these credentials will also be used in the cert-manager (to create TXT records) and external-dns (to create records for the apps) so we could create these two secrets manually but let&apos;s create only a single secret and replicate using Kubernetes Reflector.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;kubectl create namespace cert&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;manager
kubectl create namespace ingress&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;nginx
kubectl create secret generic vultr&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;credentials &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;n ingress&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;nginx &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;from&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;literal=apiKey=$VULTR_API_KEY
kubectl label secret vultr&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;credentials &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;n ingress&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;nginx created&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;manually=true
kubectl annotate secret vultr&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;credentials &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;n ingress&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;nginx reflector.v1.k8s.emberstack.com/reflection&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;auto&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;namespaces=kube&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;system&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;cert&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;manager
kubectl annotate secret vultr&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;credentials &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;n ingress&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;nginx reflector.v1.k8s.emberstack.com/reflection&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;allowed=&quot;true&quot;
kubectl annotate secret vultr&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;credentials &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;n ingress&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;nginx reflector.v1.k8s.emberstack.com/reflection&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;auto&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;enabled=&quot;true&quot;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The Load Balancer creation can be configured using annotations that are specific to the cloud provider (e.g. &lt;a href=&quot;https://kubernetes-sigs.github.io/aws-load-balancer-controller/v2.2/guide/ingress/annotations/&quot;&gt;AWS&lt;/a&gt; and &lt;a href=&quot;https://github.com/vultr/vultr-cloud-controller-manager/blob/master/docs/load-balancers.md&quot;&gt;Vultr&lt;/a&gt;). Private Load Balancers require special &lt;a href=&quot;https://kubernetes.io/docs/concepts/services-networking/service/#internal-load-balancer&quot;&gt;annotation&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Let&apos;s check the manifests for deploying NGINX Ingress Controller:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# namespace.yaml&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; v1
&lt;span class=&quot;token key atrule&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Namespace
&lt;span class=&quot;token key atrule&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ingress&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;nginx
  &lt;span class=&quot;token key atrule&quot;&gt;labels&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;app.kubernetes.io/name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ingress&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;nginx
&lt;span class=&quot;token punctuation&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# values-private.yaml&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;controller&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;electionID&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ingress&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;controller&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;leader&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;private
  &lt;span class=&quot;token key atrule&quot;&gt;ingressClass&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; nginx&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;private
  &lt;span class=&quot;token key atrule&quot;&gt;ingressClassResource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; nginx&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;private
    &lt;span class=&quot;token key atrule&quot;&gt;controllerValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; k8s.io/private&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;ingress&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;nginx
  &lt;span class=&quot;token key atrule&quot;&gt;service&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;external&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;enabled&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean important&quot;&gt;false&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;internal&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;enabled&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean important&quot;&gt;true&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;annotations&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;service.beta.kubernetes.io/vultr-loadbalancer-vpc&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean important&quot;&gt;true&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;service.beta.kubernetes.io/vultr-loadbalancer-backend-protocol&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;http&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# values-public.yaml&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;controller&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;electionID&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ingress&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;controller&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;leader&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;public
  &lt;span class=&quot;token key atrule&quot;&gt;ingressClass&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; nginx&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;public
  &lt;span class=&quot;token key atrule&quot;&gt;ingressClassResource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; nginx&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;public
    &lt;span class=&quot;token key atrule&quot;&gt;controllerValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; k8s.io/public&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;ingress&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;nginx
&lt;span class=&quot;token punctuation&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# kustomization.yaml&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; kustomize.config.k8s.io/v1beta1
&lt;span class=&quot;token key atrule&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Kustomization
&lt;span class=&quot;token key atrule&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ingress&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;nginx
&lt;span class=&quot;token key atrule&quot;&gt;resources&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; namespace.yaml
&lt;span class=&quot;token key atrule&quot;&gt;helmCharts&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ingress&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;nginx
    &lt;span class=&quot;token key atrule&quot;&gt;repo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; https&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;//kubernetes.github.io/ingress&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;nginx
    &lt;span class=&quot;token key atrule&quot;&gt;releaseName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ingress&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;nginx&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;public
    &lt;span class=&quot;token key atrule&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ingress&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;nginx
    &lt;span class=&quot;token key atrule&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 4.11.1
    &lt;span class=&quot;token key atrule&quot;&gt;valuesFile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; values&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;public.yaml
    &lt;span class=&quot;token key atrule&quot;&gt;includeCRDs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean important&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ingress&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;nginx
    &lt;span class=&quot;token key atrule&quot;&gt;repo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; https&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;//kubernetes.github.io/ingress&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;nginx
    &lt;span class=&quot;token key atrule&quot;&gt;releaseName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ingress&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;nginx&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;private
    &lt;span class=&quot;token key atrule&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ingress&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;nginx
    &lt;span class=&quot;token key atrule&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 4.11.1
    &lt;span class=&quot;token key atrule&quot;&gt;valuesFile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; values&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;private.yaml
    &lt;span class=&quot;token key atrule&quot;&gt;includeCRDs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean important&quot;&gt;true&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;It might take a while for the Load Balancer to get created. You can run &lt;code class=&quot;language-text&quot;&gt;kubectl get service -n ingress-nginx&lt;/code&gt; and check if the Load Balancers services already have an IP assigned.&lt;/p&gt;
&lt;h3&gt;Installing Cert-Manager&lt;/h3&gt;
&lt;p&gt;Once again, let&apos;s use Helm Chart to install Cert Manager. One important thing to mention, when installing Cert Manager several CRDs are installed, such as &lt;code class=&quot;language-text&quot;&gt;ClusterIssuer&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;Certificate&lt;/code&gt;. We need to create a cluster issuer for Let&apos;s Encrypt and a wildcard certificate to be used in all namespaces.&lt;/p&gt;
&lt;p&gt;When creating the cluster issuer using DNS-01 we need to define the DNS provider that we are going to use. There are several options available and the &lt;a href=&quot;https://cert-manager.io/docs/reference/api-docs/#acme.cert-manager.io/v1.ACMEChallengeSolverDNS01&quot;&gt;configuration&lt;/a&gt; of each one is specific to the provider. For Vultr, we need to use the webhook method, so we are going to install the &lt;a href=&quot;https://github.com/vultr/cert-manager-webhook-vultr&quot;&gt;Vultr webhook for Cert Manager&lt;/a&gt;. This will already provide the cluster issuer for us, but in case you are curious to know how the cluster issuer manifest would look for AWS it is like the following:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; cert&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;manager.io/v1
&lt;span class=&quot;token key atrule&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ClusterIssuer
&lt;span class=&quot;token key atrule&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ca&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;issuer
  &lt;span class=&quot;token key atrule&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; cert&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;manager
&lt;span class=&quot;token key atrule&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;acme&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;email&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; youremail@domain.com
    &lt;span class=&quot;token key atrule&quot;&gt;server&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; https&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;//acme&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;v02.api.letsencrypt.org/directory &lt;span class=&quot;token comment&quot;&gt;# you can use staging URL for testing&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;privateKeySecretRef&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; issuer&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;key
    &lt;span class=&quot;token key atrule&quot;&gt;solvers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;dns01&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;route53&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;region&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; us&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;east&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;hostedZoneID&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Z101361A2CEA1ZXYAMN60&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;So now, let&apos;s install Cert Manager.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# namespace.yaml&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; v1
&lt;span class=&quot;token key atrule&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Namespace
&lt;span class=&quot;token key atrule&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; cert&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;manager
  &lt;span class=&quot;token key atrule&quot;&gt;labels&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;app.kubernetes.io/name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; cert&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;manager
&lt;span class=&quot;token punctuation&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# certificate.yaml&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; cert&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;manager.io/v1
&lt;span class=&quot;token key atrule&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Certificate
&lt;span class=&quot;token key atrule&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ca&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;tls
  &lt;span class=&quot;token key atrule&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; default
&lt;span class=&quot;token key atrule&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;issuerRef&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; vultr&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;letsencrypt&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;prod &lt;span class=&quot;token comment&quot;&gt;# created by vultr webhook&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ClusterIssuer
  &lt;span class=&quot;token key atrule&quot;&gt;duration&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 2160h &lt;span class=&quot;token comment&quot;&gt;# 90 days&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;renewBefore&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 240h &lt;span class=&quot;token comment&quot;&gt;# 10 days&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;privateKey&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;algorithm&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; RSA
    &lt;span class=&quot;token key atrule&quot;&gt;encoding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; PKCS1
    &lt;span class=&quot;token key atrule&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2048&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;dnsNames&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;*.demosfelipetrindade.top&quot;&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;secretName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ca&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;tsl
  &lt;span class=&quot;token key atrule&quot;&gt;secretTemplate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;annotations&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;reflector.v1.k8s.emberstack.com/reflection-allowed&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;true&quot;&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;reflector.v1.k8s.emberstack.com/reflection-auto-enabled&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;true&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# values.yaml&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;installCRDs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean important&quot;&gt;true&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# kustomization.yaml&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; kustomize.config.k8s.io/v1beta1
&lt;span class=&quot;token key atrule&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Kustomization
&lt;span class=&quot;token key atrule&quot;&gt;resources&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; namespace.yaml
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; certificate.yaml
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; vultr&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;webhook.yaml
&lt;span class=&quot;token key atrule&quot;&gt;helmCharts&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; cert&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;manager
    &lt;span class=&quot;token key atrule&quot;&gt;repo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; https&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;//charts.jetstack.io
    &lt;span class=&quot;token key atrule&quot;&gt;releaseName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; cert&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;manager
    &lt;span class=&quot;token key atrule&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; cert&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;manager
    &lt;span class=&quot;token key atrule&quot;&gt;valuesFile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; values.yaml
    &lt;span class=&quot;token key atrule&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1.15.2&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Notice that I didn&apos;t include here the values of the &lt;code class=&quot;language-text&quot;&gt;vultr-webhook.yaml&lt;/code&gt;. You can check in the GitHub repository but I rather not paste it here because it&apos;s really big (the deployed Helm Chart of Vultr Webhook is old so I&apos;ve used &lt;code class=&quot;language-text&quot;&gt;helm template&lt;/code&gt; to get the manifests from the source code).&lt;/p&gt;
&lt;p&gt;It will take a while for the certificate to become valid. You can check the flow of issuing a certificate in the &lt;a href=&quot;https://cert-manager.io/docs/usage/&quot;&gt;documentation&lt;/a&gt; but you basically:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You create the Certificate (&lt;code class=&quot;language-text&quot;&gt;kubectl get certificate&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;The Certificate creates a &lt;a href=&quot;https://cert-manager.io/docs/usage/certificaterequest/&quot;&gt;CertificateRequest&lt;/a&gt; (&lt;code class=&quot;language-text&quot;&gt;kubectl get CertificateRequest&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;An &lt;a href=&quot;https://cert-manager.io/docs/concepts/acme-orders-challenges/#orders&quot;&gt;Order&lt;/a&gt; gets created (&lt;code class=&quot;language-text&quot;&gt;kubectl get order&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;A &lt;a href=&quot;https://cert-manager.io/docs/usage/certificaterequest/&quot;&gt;Challenge&lt;/a&gt; gets created (&lt;code class=&quot;language-text&quot;&gt;kubectl get challenge&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;During the certificate is being issued you can run &lt;code class=&quot;language-text&quot;&gt;kubectl get&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;kubectl describe&lt;/code&gt; to get the state of the resources. If you open your DNS provider you will see that a TXT record will be created and after validation it will be removed.&lt;/p&gt;
&lt;h3&gt;Installing External-DNS&lt;/h3&gt;
&lt;p&gt;Differently than Cert-Manager, here we don&apos;t need to instantiate CRDs, but we need to set credentials to the cloud provider.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# values.yaml&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;provider&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; vultr
&lt;span class=&quot;token key atrule&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; VULTR_API_KEY
    &lt;span class=&quot;token key atrule&quot;&gt;valueFrom&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;secretKeyRef&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; apiKey
        &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; vultr&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;credentials &lt;span class=&quot;token comment&quot;&gt;# secret replicated&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# kustomization.yaml&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; kustomize.config.k8s.io/v1beta1
&lt;span class=&quot;token key atrule&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Kustomization
&lt;span class=&quot;token key atrule&quot;&gt;helmCharts&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; external&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;dns
    &lt;span class=&quot;token key atrule&quot;&gt;repo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; https&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;//kubernetes&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;sigs.github.io/external&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;dns/
    &lt;span class=&quot;token key atrule&quot;&gt;releaseName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; external&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;dns
    &lt;span class=&quot;token key atrule&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; kube&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;system
    &lt;span class=&quot;token key atrule&quot;&gt;valuesFile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; values.yaml
    &lt;span class=&quot;token key atrule&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1.14.5&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;Deploying a public application&lt;/h3&gt;
&lt;p&gt;Let&apos;s deploy a really simple application using Nginx as the base image and show the text &quot;Hello, from public-app&quot;. We are going to deploy a &lt;code class=&quot;language-text&quot;&gt;Service&lt;/code&gt; of kind &lt;code class=&quot;language-text&quot;&gt;ClusterIp&lt;/code&gt;, a &lt;code class=&quot;language-text&quot;&gt;Deployment&lt;/code&gt; and an &lt;code class=&quot;language-text&quot;&gt;Ingress&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# deployment.yaml&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; apps/v1
&lt;span class=&quot;token key atrule&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Deployment
&lt;span class=&quot;token key atrule&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;labels&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; public&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;app
  &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; public&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;app
&lt;span class=&quot;token key atrule&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;replicas&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;selector&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;matchLabels&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; public&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;app
  &lt;span class=&quot;token key atrule&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;labels&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; public&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;app
    &lt;span class=&quot;token key atrule&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;containers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; nginx
        &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; nginx
        &lt;span class=&quot;token key atrule&quot;&gt;command&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; sh
          &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;-c&quot;&lt;/span&gt;
          &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;echo &apos;Hello, from public-app&apos; &gt; /usr/share/nginx/html/index.html &amp;amp;&amp;amp; nginx -g &apos;daemon off;&apos;&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# service.yaml&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; v1
&lt;span class=&quot;token key atrule&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Service
&lt;span class=&quot;token key atrule&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;labels&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; public&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;app
  &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; public&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;app
&lt;span class=&quot;token key atrule&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;ports&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;80&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;protocol&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; TCP
    &lt;span class=&quot;token key atrule&quot;&gt;targetPort&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;80&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;selector&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; public&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;app
  &lt;span class=&quot;token key atrule&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ClusterIP
&lt;span class=&quot;token punctuation&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# ingress.yaml&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; networking.k8s.io/v1
&lt;span class=&quot;token key atrule&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Ingress
&lt;span class=&quot;token key atrule&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; public&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;app
  &lt;span class=&quot;token key atrule&quot;&gt;annotations&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;nginx.ingress.kubernetes.io/rewrite-target&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; /
    &lt;span class=&quot;token key atrule&quot;&gt;nginx.ingress.kubernetes.io/ssl-redirect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;true&quot;&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;ingressClassName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; nginx&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;public
  &lt;span class=&quot;token key atrule&quot;&gt;tls&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;secretName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ca&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;tsl
      &lt;span class=&quot;token key atrule&quot;&gt;hosts&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; public&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;app.demosfelipetrindade.top
  &lt;span class=&quot;token key atrule&quot;&gt;rules&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;host&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; public&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;app.demosfelipetrindade.top
    &lt;span class=&quot;token key atrule&quot;&gt;http&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;paths&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; /
        &lt;span class=&quot;token key atrule&quot;&gt;pathType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Prefix
        &lt;span class=&quot;token key atrule&quot;&gt;backend&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;service&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; public&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;app
            &lt;span class=&quot;token key atrule&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
              &lt;span class=&quot;token key atrule&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;80&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# kustomization.yaml&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; kustomize.config.k8s.io/v1beta1
&lt;span class=&quot;token key atrule&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Kustomization
&lt;span class=&quot;token key atrule&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; default
&lt;span class=&quot;token key atrule&quot;&gt;resources&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; deployment.yaml
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; service.yaml
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; ingress.yaml&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Notice that we are using the public ingress class created in the NGINX Ingress Controller installation (&lt;code class=&quot;language-text&quot;&gt;nginx-public&lt;/code&gt;).&lt;/p&gt;
&lt;h3&gt;Deploying a private application&lt;/h3&gt;
&lt;p&gt;The manifests for deploying the private application are almost identical to the the public one, the only change will be the name of the application being deployed, the message displayed and the ingress class name (&lt;code class=&quot;language-text&quot;&gt;nginx-private&lt;/code&gt;)&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# deployment.yaml&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; apps/v1
&lt;span class=&quot;token key atrule&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Deployment
&lt;span class=&quot;token key atrule&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;labels&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; private&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;app
  &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; private&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;app
&lt;span class=&quot;token key atrule&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;replicas&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;selector&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;matchLabels&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; private&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;app
  &lt;span class=&quot;token key atrule&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;labels&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; private&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;app
    &lt;span class=&quot;token key atrule&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;containers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; nginx
        &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; nginx
        &lt;span class=&quot;token key atrule&quot;&gt;command&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; sh
          &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;-c&quot;&lt;/span&gt;
          &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;echo &apos;Hello, from private-app&apos; &gt; /usr/share/nginx/html/index.html &amp;amp;&amp;amp; nginx -g &apos;daemon off;&apos;&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# service.yaml&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; v1
&lt;span class=&quot;token key atrule&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Service
&lt;span class=&quot;token key atrule&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;labels&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; private&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;app
  &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; private&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;app
&lt;span class=&quot;token key atrule&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;ports&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;80&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;protocol&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; TCP
    &lt;span class=&quot;token key atrule&quot;&gt;targetPort&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;80&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;selector&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; private&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;app
  &lt;span class=&quot;token key atrule&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ClusterIP
&lt;span class=&quot;token punctuation&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# ingress.yaml&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; networking.k8s.io/v1
&lt;span class=&quot;token key atrule&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Ingress
&lt;span class=&quot;token key atrule&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; private&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;app
  &lt;span class=&quot;token key atrule&quot;&gt;annotations&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;nginx.ingress.kubernetes.io/rewrite-target&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; /
    &lt;span class=&quot;token key atrule&quot;&gt;nginx.ingress.kubernetes.io/ssl-redirect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;true&quot;&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;ingressClassName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; nginx&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;private
  &lt;span class=&quot;token key atrule&quot;&gt;tls&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;secretName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ca&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;tsl
      &lt;span class=&quot;token key atrule&quot;&gt;hosts&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; private&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;app.demosfelipetrindade.top
  &lt;span class=&quot;token key atrule&quot;&gt;rules&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;host&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; private&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;app.demosfelipetrindade.top
    &lt;span class=&quot;token key atrule&quot;&gt;http&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;paths&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; /
        &lt;span class=&quot;token key atrule&quot;&gt;pathType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Prefix
        &lt;span class=&quot;token key atrule&quot;&gt;backend&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;service&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; private&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;app
            &lt;span class=&quot;token key atrule&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
              &lt;span class=&quot;token key atrule&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;80&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# kustomization.yaml&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; kustomize.config.k8s.io/v1beta1
&lt;span class=&quot;token key atrule&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Kustomization
&lt;span class=&quot;token key atrule&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; default
&lt;span class=&quot;token key atrule&quot;&gt;resources&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; deployment.yaml
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; service.yaml
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; ingress.yaml&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;Checking the applications&lt;/h3&gt;
&lt;p&gt;You can inspect the logs (&lt;code class=&quot;language-text&quot;&gt;kubectl logs&lt;/code&gt;) of the external-dns pod in the kube-system namespace and check that it will detect the new ingress and will create records in the DNS provider. Notice that it might take a while for the DNS to propage the changes and the applications to be accessible.&lt;/p&gt;
&lt;p&gt;Now access the public application URL (in my case &lt;code class=&quot;language-text&quot;&gt;https://public-app.demosfelipetrindade.top&lt;/code&gt;). Check the message displayed!&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/7ba90b06caf3938a5499e43c36f37aaa/f793b/public-app.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 15.822784810126581%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAADCAYAAACTWi8uAAAACXBIWXMAABYlAAAWJQFJUiTwAAAA00lEQVR42mNQtUn5r+we/Z/freq/SvSs/yoxQBw9+79G3Jz/ylEz/4sFTPwvYJP3n9s48T+XcdJ/LhMoBrJ5TJP+85olo2AGucjZ/+UiZv9Xipn3XzJsNhDP+i8WPOO/bNS8//LBff/V/Ov/64a1grF+eOt/w4g2IN323wBIK/nW/hd3Kf8v6VoBxwx7Tt74v/fUrf87jl75v/3Ilf+HL9z/f+Hu6//nbr/+f/7Wi/8Xbz39f/nO8/+X7z7/f+XuCyCG0S/+X7j17P/Z60/+n7uBwABbPJHvrUR1NgAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Screenshot of public-app.demosfelipetrindade.top showing that the access is correct and the message &amp;quot;Hello, from public-app&amp;quot; is displayed&quot;
        title=&quot;&quot;
        src=&quot;/static/7ba90b06caf3938a5499e43c36f37aaa/f058b/public-app.png&quot;
        srcset=&quot;/static/7ba90b06caf3938a5499e43c36f37aaa/c26ae/public-app.png 158w,
/static/7ba90b06caf3938a5499e43c36f37aaa/6bdcf/public-app.png 315w,
/static/7ba90b06caf3938a5499e43c36f37aaa/f058b/public-app.png 630w,
/static/7ba90b06caf3938a5499e43c36f37aaa/40601/public-app.png 945w,
/static/7ba90b06caf3938a5499e43c36f37aaa/78612/public-app.png 1260w,
/static/7ba90b06caf3938a5499e43c36f37aaa/f793b/public-app.png 1404w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Now let&apos;s check if we are able to access the private application URL (in my case &lt;code class=&quot;language-text&quot;&gt;https://private-app.demosfelipetrindade.top&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/7e00724d4aa7fb1061e9e70e1453a142/5ab15/private-app.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 43.67088607594937%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAYAAAAywQxIAAAACXBIWXMAABYlAAAWJQFJUiTwAAABNUlEQVR42o2Q207CQBCG+wZqYqICe2pLS2npiWKvDMQQNJ6I3mLiI6BGE3wBHkMe9Xd2bbwgS/Diy8zOzvzz7zrtpzX4YgP5/I3gZQM+X+P08h1n00+0ph84Hr/h8GKJox3ou5PJK9rjJQ4mX3BY9Yh2cY9W+QA1uoNfXSGobw0hoYbXYNkMPLfDCK+cISQ61RyO5AyqQXIO0cAZA+9QTYj9cAFBUWs40u1iG6F8+EGEIIxNbuuRygLVHakHtuDCRS9KkAwydJgwZ87Vb2xyIT3YZq2CkppdP8QgKw1pNkRWVCZP0sLkYS+2iloFdaPf7SEvRijKc+TlCGk+RJzkiPopOc/Nlwip3Uqa8fY4bFD0J64XmPj33AYt5voRorSGop5/Ce5ELyBn/azGzWJFbhOzQAv+AO1vCnl5k8DiAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Screenshot of browser trying to access private-app.demosfelipetrindade.top but it is unable to access&quot;
        title=&quot;&quot;
        src=&quot;/static/7e00724d4aa7fb1061e9e70e1453a142/f058b/private-app.png&quot;
        srcset=&quot;/static/7e00724d4aa7fb1061e9e70e1453a142/c26ae/private-app.png 158w,
/static/7e00724d4aa7fb1061e9e70e1453a142/6bdcf/private-app.png 315w,
/static/7e00724d4aa7fb1061e9e70e1453a142/f058b/private-app.png 630w,
/static/7e00724d4aa7fb1061e9e70e1453a142/40601/private-app.png 945w,
/static/7e00724d4aa7fb1061e9e70e1453a142/78612/private-app.png 1260w,
/static/7e00724d4aa7fb1061e9e70e1453a142/5ab15/private-app.png 2446w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;We can&apos;t, as expected! So let&apos;s see if we can access the private application within the cluster. For that, let&apos;s run a pod that has &lt;code class=&quot;language-text&quot;&gt;curl&lt;/code&gt; installed and curl the private app URL.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;kubectl run &lt;span class=&quot;token function&quot;&gt;curl&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--image&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;nginx:alpine
kubectl &lt;span class=&quot;token builtin class-name&quot;&gt;exec&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-it&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;curl&lt;/span&gt; -- &lt;span class=&quot;token function&quot;&gt;curl&lt;/span&gt; https://private-app.demosfelipetrindade.top&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/6802db543657cfe94e3be12d24cea08f/229ad/private-app-kubectl.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 10.126582278481013%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAACCAYAAABYBvyLAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAb0lEQVR42k2Myw2AIBBErQATv3xFTSSIWoMlePRsDfafEZBEDy/7srOz2XFeGI8b1O3QvYMaN3A1oxUGDTeo2QQmLYRewJSF9DOghjUitfOZQze8vYz646rpQfkUCyEMj0jRIS91hCSipz35e/H5A8L0NmJ9WeDeAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Screenshot of the output of the command &amp;quot;curl https://private-app.demosfelipetrindade.top&amp;quot;. It returns the correct output &amp;quot;Hello, from private-app&amp;quot;.&quot;
        title=&quot;&quot;
        src=&quot;/static/6802db543657cfe94e3be12d24cea08f/f058b/private-app-kubectl.png&quot;
        srcset=&quot;/static/6802db543657cfe94e3be12d24cea08f/c26ae/private-app-kubectl.png 158w,
/static/6802db543657cfe94e3be12d24cea08f/6bdcf/private-app-kubectl.png 315w,
/static/6802db543657cfe94e3be12d24cea08f/f058b/private-app-kubectl.png 630w,
/static/6802db543657cfe94e3be12d24cea08f/40601/private-app-kubectl.png 945w,
/static/6802db543657cfe94e3be12d24cea08f/78612/private-app-kubectl.png 1260w,
/static/6802db543657cfe94e3be12d24cea08f/229ad/private-app-kubectl.png 1356w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Bingo! The private application is accessible within the cluster! A great improvement would be to deploy a VPN in this network and access the private applications using the VPN but this is out of the scope of this blog post.&lt;/p&gt;
&lt;h2&gt;Cya!&lt;/h2&gt;
&lt;p&gt;Well, I hope you liked this blog post. We covered several tools used in real Kubernetes environments and explained in detail why they are needed and how they all are used together!&lt;/p&gt;
&lt;p&gt;See you around! 👋&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Image proxy cache using Harbor]]></title><description><![CDATA[You can check the GitHub repository an access all the manifests used in this blog post. A common problem when dealing with highly dynamic…]]></description><link>https://felipetrindade.com/proxy-cache-harbor/</link><guid isPermaLink="false">https://felipetrindade.com/proxy-cache-harbor/</guid><pubDate>Wed, 07 Aug 2024 21:40:32 GMT</pubDate><content:encoded>&lt;p&gt;You can check the &lt;a href=&quot;https://github.com/felipelaptrin/image-proxy-cache-harbor&quot;&gt;GitHub repository&lt;/a&gt; an access all the manifests used in this blog post.&lt;/p&gt;
&lt;p&gt;A common problem when dealing with highly dynamic and/or big Kubernetes cluster is facing pull limits from Dockerhub. The &lt;a href=&quot;https://docs.docker.com/docker-hub/download-rate-limit/&quot;&gt;pull rate limits&lt;/a&gt; are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;100 pulls per 6 hours per IP (for unauthenticated users)&lt;/li&gt;
&lt;li&gt;200 pulls per 6 hours (for authenticated users)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is usually enough for small clusters, but dynamic and big clusters face issues. A simple solution is implementing a proxy cache project with &lt;a href=&quot;https://goharbor.io/&quot;&gt;Harbor&lt;/a&gt;. Notice that there are several benefits, besides avoiding the low rate pull limit from Dockerhub, of implementing an image proxy cache, such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Reduce egress traffic&lt;/strong&gt;: NAT is usually charged by the amount of traffic&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Faster pod startup&lt;/strong&gt;: The image is already cached inside the cluster&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Improve reliability&lt;/strong&gt;: If the image is cached in the cluster, you do not depend on the external registry&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;How does a proxy cache work?&lt;/h2&gt;
&lt;p&gt;Harbor allows proxy cache to Dockerhub and Harbor repositories. Since our use case is to proxy cache from DockerHub I will only mention DockerHub, but keep in mind that it also applies to Harbor registries (in case you want to have a private image registry).&lt;/p&gt;
&lt;p&gt;A &lt;a href=&quot;https://goharbor.io/docs/2.1.0/administration/configure-proxy-cache/&quot;&gt;proxy cache&lt;/a&gt; basically does the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If the image is not cached in the registry (Harbor), it will proxy the pull image registry to Dockerhub and cache the image locally&lt;/li&gt;
&lt;li&gt;If the image is already cached, Harbor doesn&apos;t need to send the request to Dockerhub and will serve the cached image&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In reality, there are some extra steps that Harbor does before serving the cache image, such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Checking if the image still exists in Dockerhub. If it doesn&apos;t, the image is not served&lt;/li&gt;
&lt;li&gt;Checking if the image was updated. If it wasn&apos;t it serves cache, if it was it will proxy the request to Dockerhub (notice that this commonly happens if you use &lt;code class=&quot;language-text&quot;&gt;latest&lt;/code&gt; tag)&lt;/li&gt;
&lt;li&gt;If DockerHub is not reachable, it will serve the cache image&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Local Kubernetes setup&lt;/h2&gt;
&lt;p&gt;I will demo this locally using &lt;a href=&quot;&quot;&gt;Kind&lt;/a&gt;. I highly recommend you check my blog post on &lt;a href=&quot;https://felipetrindade.com/kubernetes-ingress-load-balancer/&quot;&gt;how to set up a local Kubernetes cluster with LoadBalancer and name resolution in MacOs and Linux&lt;/a&gt;. I will reuse all the components present in the blog post. Since I don&apos;t want to repeat myself and keep things dry, I highly recommend you read it (or skim the text).&lt;/p&gt;
&lt;h2&gt;Demo time&lt;/h2&gt;
&lt;h3&gt;Basic local Kubernetes cluster setup&lt;/h3&gt;
&lt;p&gt;Once again, some of these steps were explained in detail in my previous blog post, I won&apos;t go deeper into the explanation here&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Create a docker network&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token function&quot;&gt;docker&lt;/span&gt; network create &lt;span class=&quot;token parameter variable&quot;&gt;--subnet&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;172.100&lt;/span&gt;.0.0/16 custom-kind-network&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;Create a Kind cluster&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Using the following Kind configuration&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# kind.yaml&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Cluster
&lt;span class=&quot;token key atrule&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; kind.x&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;k8s.io/v1alpha4
&lt;span class=&quot;token key atrule&quot;&gt;nodes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;role&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; control&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;plane
    &lt;span class=&quot;token key atrule&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; kindest/node&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;v1.30.2
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;role&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; worker
    &lt;span class=&quot;token key atrule&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; kindest/node&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;v1.30.2&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;run the command:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token assign-left variable&quot;&gt;KIND_EXPERIMENTAL_DOCKER_NETWORK&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;custom-kind-network kind create cluster &lt;span class=&quot;token parameter variable&quot;&gt;--config&lt;/span&gt; kind.yaml&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;Install MetalLB&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Let&apos;s install MetalLB using Helm Chart&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;helm repo &lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt; metallb https://metallb.github.io/metallb
helm &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; metallb &lt;span class=&quot;token parameter variable&quot;&gt;-n&lt;/span&gt; metallb --create-namespace metallb/metallb
&lt;span class=&quot;token function&quot;&gt;sleep&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;
kubectl &lt;span class=&quot;token function&quot;&gt;wait&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-n&lt;/span&gt; metallb &lt;span class=&quot;token parameter variable&quot;&gt;-l&lt;/span&gt; app.kubernetes.io/component&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;controller &lt;span class=&quot;token parameter variable&quot;&gt;--for&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;condition&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;ready pod &lt;span class=&quot;token parameter variable&quot;&gt;--timeout&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;120s&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now that MetalLB is installed let&apos;s create our LBs IPs&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# ip-adress-pool.yaml&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; metallb.io/v1beta1
&lt;span class=&quot;token key atrule&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; IPAddressPool
&lt;span class=&quot;token key atrule&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ip&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;pool
  &lt;span class=&quot;token key atrule&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; metallb
&lt;span class=&quot;token key atrule&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;addresses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; 172.100.150.0&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;172.100.150.10&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;and announce using the L2 ARP protocol&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# advertisement.yaml&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; metallb.io/v1beta1
&lt;span class=&quot;token key atrule&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; L2Advertisement
&lt;span class=&quot;token key atrule&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; advertisement&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;l2
  &lt;span class=&quot;token key atrule&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; metallb
&lt;span class=&quot;token key atrule&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;ipAddressPools&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; ip&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;pool&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Apply these manifests:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;kubectl apply &lt;span class=&quot;token parameter variable&quot;&gt;-f&lt;/span&gt; ip-adress-pool.yaml
kubectl apply &lt;span class=&quot;token parameter variable&quot;&gt;-f&lt;/span&gt; advertisement.yaml&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ol start=&quot;4&quot;&gt;
&lt;li&gt;Install NGINX Ingress&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Once again, let&apos;s use Helm to install NGINX Ingress&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;helm repo &lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt; ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update
helm &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; ingress-nginx &lt;span class=&quot;token parameter variable&quot;&gt;-n&lt;/span&gt; ingress-nginx --create-namespace ingress-nginx/ingress-nginx&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You should see that an external-ip is being used by the NGINX service (&lt;code class=&quot;language-text&quot;&gt;kubectl get service -n ingress-nginx&lt;/code&gt;).&lt;/p&gt;
&lt;h3&gt;Harbor installation&lt;/h3&gt;
&lt;p&gt;The only configuration that needs to be changed for this demo (at the end of this blog post I will comment on several improvements that must be done in a real production environment) is related to the ingress, but I will also modify the admin password to be &lt;code class=&quot;language-text&quot;&gt;admin&lt;/code&gt; by simplicity (the classic &lt;code class=&quot;language-text&quot;&gt;admin&lt;/code&gt;/&lt;code class=&quot;language-text&quot;&gt;admin&lt;/code&gt;).&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# values.yaml&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;externalURL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; https&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;//harbor.ingress.local
&lt;span class=&quot;token key atrule&quot;&gt;harborAdminPassword&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; admin
&lt;span class=&quot;token key atrule&quot;&gt;expose&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ingress
  &lt;span class=&quot;token key atrule&quot;&gt;ingress&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;controller&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; default
    &lt;span class=&quot;token key atrule&quot;&gt;className&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; nginx
    &lt;span class=&quot;token key atrule&quot;&gt;hosts&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;core&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; harbor.ingress.local
  &lt;span class=&quot;token key atrule&quot;&gt;tls&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;certSource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; auto&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Notice that you will need to add &lt;code class=&quot;language-text&quot;&gt;harbor.ingress.local&lt;/code&gt; to your &lt;code class=&quot;language-text&quot;&gt;/etc/hosts&lt;/code&gt; file.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token assign-left variable&quot;&gt;INGRESS_LB_IP&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;kubectl get svc ingress-nginx-controller &lt;span class=&quot;token parameter variable&quot;&gt;-n&lt;/span&gt; ingress-nginx &lt;span class=&quot;token parameter variable&quot;&gt;-o&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;jsonpath&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;{.status.loadBalancer.ingress[0].ip}&apos;&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$INGRESS_LB_IP&lt;/span&gt; harbor.ingress.local&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;tee&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-a&lt;/span&gt; /etc/hosts&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And finally install Harbor&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;helm repo &lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt; harbor https://helm.goharbor.io
helm repo update
helm &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; harbor &lt;span class=&quot;token parameter variable&quot;&gt;-n&lt;/span&gt; harbor --create-namespace &lt;span class=&quot;token parameter variable&quot;&gt;--values&lt;/span&gt; values.yaml &lt;span class=&quot;token parameter variable&quot;&gt;--version&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1.15&lt;/span&gt;.0 harbor/harbor&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Wait for it to get ready and access &lt;code class=&quot;language-text&quot;&gt;https://harbor.ingress.local&lt;/code&gt; UI interface using username &lt;code class=&quot;language-text&quot;&gt;admin&lt;/code&gt; and password &lt;code class=&quot;language-text&quot;&gt;admin&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;After login, this will be the first page you will see&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/48b8daf11f1903e1c2784c4ee1dc10bc/5c5b6/harbor-login-screen.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 84.81012658227847%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAARCAYAAADdRIy+AAAACXBIWXMAAAsTAAALEwEAmpwYAAACnElEQVR42q2TSW/TUBDHLVpUtYcuUZrFceI9iR3Hu53EWWgpdAeOSHw/jnDlO0CJhBA1lpD4HlVfhpmXLmcCh7/eZDLvN9uz0HX8T04Q/+o47nWn5+XlUjXf3S7lpd39vLRXyfdIaO/slPNt9N9rb7e8jNkX83Kl8aNWl36LYuuj4KRZ0R+MQbZcpvYC0No9kPUOaB0HdNsH1XJB6fZB1DpQkXWoygbUFBOMtgWqaYPmxmD6KTO9GNSePxe083c/6y/fwpPgxe16eLzYGr1abAwuFmvx6WI9PntUcrZ4mpxz0e+t7PViE2M3h5eLtfDkdjN7AxvpxVdBSQ8LY3AEcjRjcjiDZj8FNchA6icg9iJUDA0nhqY74Dap6abQ8gZ39oDEJDzNZDoX7OlpETy/BBWBij+GFoKswQxUvNCwQhAtHEOYQX92jDEZKOGI293sEORgCGY6hd7kiBnpDOzhwVyom05h4B9i22EVzeKAWscDsetDw14CFX8IdEFGmIJwk+xgxEWVavGYafEEmk4yF2rtfqG4CUiWz6qmg61iO+EUmt6I29RuA6EkSsITkX0nDZO1+jFrUawdIhAr1Hxsr+uxim5DC2dB1ahYCQVpd6eMfi0cgxlPqRIwogmo/oj70cf43HvREmhgK7xCA58MwvVkCtSCjpfJJlEC9+AMvMMLbuvp9CFOiyY4w2egBNljhQqWLXZc3HLC5/Ig99Gm7d5vmPtRNF/sgiGU/EugTHPClglIi/hbSXbIaAwNO1gCVQ9n5IQrA1FMoqIIWNXtAqFQ0S0+w/8GRBh/Nv8MxKdyTZXheYPgW3SuohvJiQh8JbScqFBws/jAWR1nSJlWEKPt83fYtPwPupd+kyzvS9WwryjLCvqM+o5f0Ps/MRPpXV3iHL8AAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Screenshot of Harbor page after login&quot;
        title=&quot;&quot;
        src=&quot;/static/48b8daf11f1903e1c2784c4ee1dc10bc/f058b/harbor-login-screen.png&quot;
        srcset=&quot;/static/48b8daf11f1903e1c2784c4ee1dc10bc/c26ae/harbor-login-screen.png 158w,
/static/48b8daf11f1903e1c2784c4ee1dc10bc/6bdcf/harbor-login-screen.png 315w,
/static/48b8daf11f1903e1c2784c4ee1dc10bc/f058b/harbor-login-screen.png 630w,
/static/48b8daf11f1903e1c2784c4ee1dc10bc/40601/harbor-login-screen.png 945w,
/static/48b8daf11f1903e1c2784c4ee1dc10bc/78612/harbor-login-screen.png 1260w,
/static/48b8daf11f1903e1c2784c4ee1dc10bc/5c5b6/harbor-login-screen.png 1265w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3&gt;Creating a proxy cache project&lt;/h3&gt;
&lt;p&gt;I will show you how to create via UI the proxy cache project but I will also provide the Terraform code.&lt;/p&gt;
&lt;p&gt;Let&apos;s go to the &lt;code class=&quot;language-text&quot;&gt;Registries&lt;/code&gt; tab.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/ec83278dc3056bf1e3abdbca71a82dfb/5c5b6/harbor-registries.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 84.81012658227847%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAARCAYAAADdRIy+AAAACXBIWXMAAAsTAAALEwEAmpwYAAACpElEQVR42q2T227TQBCGLbUFUaQeQhon8TE+xPHZjh3nCEEVNE0TBAgkoBX06biEW94BSiSEaLCExHtU3QyzTlqKBBcELj7Netb+d+YfL1NzgndOGH8zHO/UsP1pPsdOtzZy09zWzjS3XZhuL9jczE83MH/B9lZ+/s5OeZovcF+KJf57uSy+ZZyknbpJFyTTIxU7hErVBkk1QDEcUK0AKqYHcs0FTq0BK2kZRVkHrWpCRbdA8WLQg4TofgwVO5gw6vj4Kzc4gpVocL7WGM5utB/MrjVHs5V4f7YaDy9ZaxzM1pKf3Ow+zN5db49nq9H++Xr3EVxPRh+ZYtVPS7UAdjSHFKoeiF4TKmEHBLcBJSv6I2U7vgrh3QT0Rm/CyG6c6lEHirpNWNUCNWyD3dkFtd4BHj8U7DoS4boOnBlm8WLNWeE8miERnBhqCQpyVSfVwhZwhktYpQb0JCHsgRB0QPax2qCJEXNOA0Tc40wUQ0H6nIEVYjdEwULwGT1s3kvdu2MwOntEjvogoYgWdTMxOWhllUqLNc1rcQ+MZh9taWOLt7OcUu8QPenTOGHEsJ1a3fugRL3MBxE/lqjIAty/sm6B0x+AvzsCuY559FoM2jRPFBQXg+aEYVUzVbAlzvDILakKwZ09OHj2EvaeHMLg6dEvjF4cZ3tDZHx4jLlDOHj+CrrDx9hdlw5zwhR1J1WoT16DlAwPqLkSTvp3UA8p0mWk/rZACVqE2iI40VxQQhGu5pMyCpZxan9LCafM43Bw6nPBCrYsOfVMkE5wCQjvLATx30tRFAqqSVjN/n+CKEZYjP8sWFCtU1oZxjMUPsfkMpzxTkSFTxjRiVIZ722x6mZTpictARG8BHgbpyyYwRvVTz7xpv+B1awTesoSvEc+451+/QNVl+3i+yk0LQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Screenshot of Harbor registry&quot;
        title=&quot;&quot;
        src=&quot;/static/ec83278dc3056bf1e3abdbca71a82dfb/f058b/harbor-registries.png&quot;
        srcset=&quot;/static/ec83278dc3056bf1e3abdbca71a82dfb/c26ae/harbor-registries.png 158w,
/static/ec83278dc3056bf1e3abdbca71a82dfb/6bdcf/harbor-registries.png 315w,
/static/ec83278dc3056bf1e3abdbca71a82dfb/f058b/harbor-registries.png 630w,
/static/ec83278dc3056bf1e3abdbca71a82dfb/40601/harbor-registries.png 945w,
/static/ec83278dc3056bf1e3abdbca71a82dfb/78612/harbor-registries.png 1260w,
/static/ec83278dc3056bf1e3abdbca71a82dfb/5c5b6/harbor-registries.png 1265w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Now, let&apos;s create the endpoint for Dockerhub&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/21eadf110c5bb851ede5d527def4ce76/5c5b6/new-registry-endpoint.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 84.81012658227847%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAARCAYAAADdRIy+AAAACXBIWXMAAAsTAAALEwEAmpwYAAAC80lEQVR42p2T227TQBCGozZNfD4kcezmQBzbiR0nTprm0DZt0wKlveAN4Bl4LC7hlneAEgkhGiIh8R5V3GFm7VQVRSBx8Wt2vetvjpsJBvsfotH0R7s3uPGjvWWpZC011VjqBXOp41ovWGytasZSUUv30nVjWdBNdm6Y9W9W5cnPStV+n+kOx6veaAoNP4ydMALH9aHR8MBtdcANeuC0Q2iiKg0XylUbzFoTbRNspwWe50Md77UH49jvD6EZdBcZ0ap958tV2NKMNeouWzCZtrTyHe2Z9PLddvp9o52ilVi6q5bWWd1ERvlzJsuLq5wgwY4gxWQ5WQVe1oCTFMhvJJLk3/TgXFLivKwAr+iLDC+pK0HRIJcCaS3pRWYJSuKZk8fanKNi2ksEzAviSsCI0CsDkkq2B8HhHPzJMZM7PIRqOIRad5+pHo2h+MTBf6QNPOaTADBCtbCSCgaIWjHGNeQRaHkh+LPn4E5OwZ3OoX14zqwzPgFnhA6mZ2B6HQQpIKg6ZqPHIlqELjKcpK0krUj5x5zMUgerFUJ48gLs4RE0EdA/u4Lg6CkD0t5BR6bbYbWkmnMUIQLxf0yZp5TxI9WQF0HGg3oQQYARUjQEsPdwrPoTsAdT9o2ANT+ComGCYVVAK5VZyjwDCtIqLXDMOiaI6D2A8PQSU04ickmU7v4sTXnO7nBi0kRUnDIWmR1sChsRbApZipIu9+aXDEYKTy7Ax5RbB2fgTU/BOzhnd8j5psucpOL4yI8jZEDHh+78CtoIcCbYFIR5GK0zJgczsPtjKNst1sCHQI6AFGE6LjEVOYmwA/7xBetuC2vpHWP6R8+YvNkF7L16A5VoArk8lzTlb0DyqlcbsItFp/HZbUdQwRmsdEeJ7Y2g9fI1GF4Xchz/R+BNCrxFrfFwjeA1gh9IXOM03Cu7vc0swth91G0KvM5wosKakhPopcjpW/6H8AGw957MIJtDbvNSMJp3OPFf8Bl9ygniNe7/Rx/zgvwV9fYXsE6hcVFp+xAAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Screenshot of Harbor new Dockerhub endpoint&quot;
        title=&quot;&quot;
        src=&quot;/static/21eadf110c5bb851ede5d527def4ce76/f058b/new-registry-endpoint.png&quot;
        srcset=&quot;/static/21eadf110c5bb851ede5d527def4ce76/c26ae/new-registry-endpoint.png 158w,
/static/21eadf110c5bb851ede5d527def4ce76/6bdcf/new-registry-endpoint.png 315w,
/static/21eadf110c5bb851ede5d527def4ce76/f058b/new-registry-endpoint.png 630w,
/static/21eadf110c5bb851ede5d527def4ce76/40601/new-registry-endpoint.png 945w,
/static/21eadf110c5bb851ede5d527def4ce76/78612/new-registry-endpoint.png 1260w,
/static/21eadf110c5bb851ede5d527def4ce76/5c5b6/new-registry-endpoint.png 1265w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Now we can create a new proxy cache project using the registry we just created&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/344f4ca4970005ba68eea779f3cb74c7/5c5b6/proxy-cache-project.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 84.81012658227847%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAARCAYAAADdRIy+AAAACXBIWXMAAAsTAAALEwEAmpwYAAACx0lEQVR42p1U227TQBCNShrHl7W9Thy7SdskvsSJk7QNKQmUXmihaiU+gEc+gm/iEV75ByiVEKIhEhL/UdUdZtbbtBV9gYejmd2dPXvmYhe6m48/DceTX53B5kUy3JpVq/7MttwZd7wZR587vvAt252ZVnUBzt2Zwz1x7nprP/z6+u96o/Wx0B9N5oPxBJpJmgXpEIIwgWYzgjDuQdgdQJD0oYX+ynoI3mpboNZoQyuIIYoSWMOzzuZ2lmyMoN3tnxd0b/WnWqvDklW9QlwXHe/6EWLJriHc3HJX7BXvYNnxFz7dLXIPMPZroWzyOQIQmbCGBSqz0Jqg3AHtK7opQGdljFGkRWRkVZOjQl6ZM8elRaaatiAzbAc06VOgZnFgTkVYzSS/CgavAMXTHvqZsDYSLmv6XGU2vsyykqrnynSWq5AKiETDR8SDCB0vqwT05YOCEOPPC4ogRCW6mRFRSTPAxaYkk10IR1NopCNo9LbuYXUwhspaGxQUIDIxzEyW6UahlSvUmFDnxyl0d19BNN2H4MkedJ4eQjw9gHCSr0P0KUY1mCDULSejLBSDkUJjTpuCEMlwDX7Ug+GLU+jgxfb4OfSRPCay7V0IcE2kXtjFrAxJyDMkxZrbOaGUm6esaiK4hyS9nSOIkIhICURIxKSQYkqanncZ79IUlEkhpSwbkdEYUGO8IBGE0eQA096BreOXkO6dwMbRa0ieHUJzOIZaKxbZyObdEt5TKAlr+HqEdWujIqpbsnMoiHp7x5Dsn8Lo7TtobEyhpJRvZva+QuosYlFDXl+HlWSIhe8LeFEq4McDWMGuhydvoBqmUCprD6e8IJQpUC0VLPgt2C2wbsvForA5ifkX4YUkvERc4SHCehgsB35Vcs+8waUkPCvgQIum4AyKOZTf5r8C/wO2/FJ09kFl5jdM7QuOwRmu/wefFY19R7z/A4CDmWr4Lk25AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Screenshot of Harbor new proxy cache project&quot;
        title=&quot;&quot;
        src=&quot;/static/344f4ca4970005ba68eea779f3cb74c7/f058b/proxy-cache-project.png&quot;
        srcset=&quot;/static/344f4ca4970005ba68eea779f3cb74c7/c26ae/proxy-cache-project.png 158w,
/static/344f4ca4970005ba68eea779f3cb74c7/6bdcf/proxy-cache-project.png 315w,
/static/344f4ca4970005ba68eea779f3cb74c7/f058b/proxy-cache-project.png 630w,
/static/344f4ca4970005ba68eea779f3cb74c7/40601/proxy-cache-project.png 945w,
/static/344f4ca4970005ba68eea779f3cb74c7/78612/proxy-cache-project.png 1260w,
/static/344f4ca4970005ba68eea779f3cb74c7/5c5b6/proxy-cache-project.png 1265w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Instead of manually managing your Harbor configuration, a better approach is to create all of this using infrastructure as a code. I highly recommend Terraform/OpenTofu in these cases. You can simply use the Harbor provider created by the community and have all configurations as a code. Let&apos;s create a simple Terraform module for that&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;hcl&quot;&gt;&lt;pre class=&quot;language-hcl&quot;&gt;&lt;code class=&quot;language-hcl&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# version.tf&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;terraform&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;required_providers&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;harbor&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;source&lt;/span&gt;  &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;goharbor/harbor&quot;&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;version&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;3.10.14&quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;provider&lt;span class=&quot;token type variable&quot;&gt; &quot;harbor&quot; &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;url&lt;/span&gt;      &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; var.url
  &lt;span class=&quot;token property&quot;&gt;username&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; var.username
  &lt;span class=&quot;token property&quot;&gt;password&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; var.password
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;hcl&quot;&gt;&lt;pre class=&quot;language-hcl&quot;&gt;&lt;code class=&quot;language-hcl&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# variables.tf&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;variable&lt;span class=&quot;token type variable&quot;&gt; &quot;url&quot; &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;description&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;The url of harbor&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;type&lt;/span&gt;        &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; string
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;variable&lt;span class=&quot;token type variable&quot;&gt; &quot;username&quot; &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;description&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;The username to be used to access harbor&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;type&lt;/span&gt;        &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; string
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;variable&lt;span class=&quot;token type variable&quot;&gt; &quot;password&quot; &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;description&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;The password to be used to access harbor&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;type&lt;/span&gt;        &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; string
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;variable&lt;span class=&quot;token type variable&quot;&gt; &quot;proxy_project_name&quot; &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;description&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;The name of the project that will be created in harbor.&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;type&lt;/span&gt;        &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; string
  &lt;span class=&quot;token property&quot;&gt;default&lt;/span&gt;     &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;proxy_cache&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;hcl&quot;&gt;&lt;pre class=&quot;language-hcl&quot;&gt;&lt;code class=&quot;language-hcl&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# main.tf&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;resource &lt;span class=&quot;token type variable&quot;&gt;&quot;harbor_registry&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;docker&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;name&lt;/span&gt;          &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;docker-hub&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;provider_name&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;docker-hub&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;endpoint_url&lt;/span&gt;  &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https://hub.docker.com&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;resource &lt;span class=&quot;token type variable&quot;&gt;&quot;harbor_project&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;proxy&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;name&lt;/span&gt;        &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; var.proxy_project_name
  &lt;span class=&quot;token property&quot;&gt;registry_id&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; harbor_registry.docker.registry_id
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;hcl&quot;&gt;&lt;pre class=&quot;language-hcl&quot;&gt;&lt;code class=&quot;language-hcl&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# vars.tfvars&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;url&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;http://harbor.ingress.local&quot;&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;username&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;admin&quot;&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;password&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;admin&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We are simply going to run (using OpenTofu):&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;tofu init
tofu apply -var-file&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;vars.tfvars&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;Using the harbor proxy cache&lt;/h3&gt;
&lt;p&gt;Ok, so let&apos;s recap what we have so far. We have a local Kubernetes cluster created using Kind with Harbor installed and configured with a proxy cache repository that is proxying DockerHub. Harbor is exposed by our MetalLB Load balancer and routed by the NGINX Ingress.&lt;/p&gt;
&lt;p&gt;Now we would like to run an application and see Harbor proxy working in action. Let&apos;s run the following demos:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Deploy an application that uses Kustomize&lt;/li&gt;
&lt;li&gt;Deploy an application that uses Helm Chart&lt;/li&gt;
&lt;li&gt;Deploy an application whose image is already presented in the node&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;When we are using a private image registry (remember that we didn&apos;t check the &lt;code class=&quot;language-text&quot;&gt;Public&lt;/code&gt; box while creating the proxy cache project) we need to tell Kubernetes &quot;Hey, use these credentials to pull the image from the registry&quot;. We do that by:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Pointing the image to the desired registry and repository. This looks obvious, but what I want to say is: that we are not going to directly use DockerHub images, we are going to use Harbor as a registry. This means that we will need to add a &lt;a href=&quot;https://goharbor.io/docs/2.1.0/administration/configure-proxy-cache/&quot;&gt;prefix&lt;/a&gt; to DockerHub images to point to the Harbor project (&lt;code class=&quot;language-text&quot;&gt;&amp;lt;harbor_server_name&gt;/&amp;lt;proxy_cache_name&gt;&lt;/code&gt;), in our use case this will be equivalent to &lt;code class=&quot;language-text&quot;&gt;harbor.ingress.local/proxy_cache&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Define the Kubernetes secret name that contains the credentials to access the private registry in the &lt;code class=&quot;language-text&quot;&gt;imagePullSecrets&lt;/code&gt; property of &lt;a href=&quot;https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#containers&quot;&gt;container definition&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For simplicity, I&apos;ll use the admin user credentials (that have permission to pull images from the proxy cache project). Notice that this secret needs to be created in every namespace that our apps will require the usage of Harbor registry, in our case we will deploy all apps in the &lt;code class=&quot;language-text&quot;&gt;default&lt;/code&gt; namespace. Let&apos;s create this secret by running&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;kubectl create secret &lt;span class=&quot;token parameter variable&quot;&gt;-n&lt;/span&gt; default docker-registry regcred-harbor &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  --docker-server&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;harbor.ingress.local &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  --docker-username&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;admin &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  --docker-password&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;admin&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Ok, now let&apos;s comment about a tricky step that we will need to do to make this work locally. In a real environment with real TLS certificates, you wouldn&apos;t have this problem, but since we are running this locally with self-signed TLS certificates (or even without) this problem will appear. Kubernetes, by default, won&apos;t pull from &quot;insecure&quot; registries and because of that, we will need to configure the Kubernetes nodes to trust our private Harbor registry.&lt;/p&gt;
&lt;p&gt;This configuration is done in the &lt;a href=&quot;https://kubernetes.io/docs/setup/production-environment/container-runtimes/&quot;&gt;container runtime&lt;/a&gt; that is being used by the Kubernetes cluster, in our case, Kind only supports &lt;a href=&quot;https://kind.sigs.k8s.io/docs/design/principles/#target-cri-functionality&quot;&gt;containerd&lt;/a&gt;, and for containerd, this &lt;a href=&quot;https://github.com/containerd/containerd/blob/v2.0.0-rc.3/docs/cri/config.md&quot;&gt;configuration&lt;/a&gt; is defined in the &lt;code class=&quot;language-text&quot;&gt;/etc/containerd/config.toml&lt;/code&gt; file.&lt;/p&gt;
&lt;p&gt;Let&apos;s connect your our Kubernetes node. We should do this to every worker node of our cluster, but since we only have 1 worker node this procedure will only be done once.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token function&quot;&gt;docker&lt;/span&gt; &lt;span class=&quot;token builtin class-name&quot;&gt;exec&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-it&lt;/span&gt; kind-worker &lt;span class=&quot;token function&quot;&gt;bash&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Let&apos;s install some utilities for editing the &lt;code class=&quot;language-text&quot;&gt;/etc/containerd/config.toml&lt;/code&gt; file&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token function&quot;&gt;apt-get&lt;/span&gt; update
&lt;span class=&quot;token function&quot;&gt;apt&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;vim&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-y&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;vim&lt;/span&gt; /etc/containerd/config.toml&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;and add the following lines to the file&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;toml&quot;&gt;&lt;pre class=&quot;language-toml&quot;&gt;&lt;code class=&quot;language-toml&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token table class-name&quot;&gt;plugins.&quot;io.containerd.grpc.v1.cri&quot;.registry&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token table class-name&quot;&gt;plugins.&quot;io.containerd.grpc.v1.cri&quot;.registry.mirrors&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token table class-name&quot;&gt;plugins.&quot;io.containerd.grpc.v1.cri&quot;.registry.mirrors.&quot;harbor.ingress.local&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token key property&quot;&gt;endpoint&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;https://harbor.ingress.local&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token table class-name&quot;&gt;plugins.&quot;io.containerd.grpc.v1.cri&quot;.registry.configs.&quot;harbor.ingress.local&quot;.tls&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token key property&quot;&gt;insecure_skip_verify&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token table class-name&quot;&gt;plugins.&quot;io.containerd.grpc.v1.cri&quot;.registry.configs.&quot;harbor.ingress.local&quot;.auth&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token key property&quot;&gt;username&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;admin&quot;&lt;/span&gt;
  &lt;span class=&quot;token key property&quot;&gt;password&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;admin&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;and finally restart containerd so these changes can be applied:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;systemctl restart containerd&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4&gt;Deploying an application that uses Kustomize&lt;/h4&gt;
&lt;p&gt;Let&apos;s deploy an NGINX Kubernetes Deployment manifest using Kustomize. A common pattern when using Kustomize is to have a &lt;code class=&quot;language-text&quot;&gt;base&lt;/code&gt; folder that will be reused by the &lt;code class=&quot;language-text&quot;&gt;overlays&lt;/code&gt; folder (usually the overlays folders represent an environment, e.g. &lt;code class=&quot;language-text&quot;&gt;development&lt;/code&gt;). We would apply generators and patches in the overlays folder but always use the base resources as a reference.&lt;/p&gt;
&lt;p&gt;Let&apos;s suppose the Kubernetes deployment manifest is in the base folder. One option is to manually modify the prefix of the image there to point to our Harbor registry. It&apos;s possible but unlikely that all environments (overlays) will use the same endpoint. So a better idea is to add this as a patch. Let&apos;s create a patch for adding the registry credential and the prefix to the image.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# base/deployment.yaml&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; apps/v1
&lt;span class=&quot;token key atrule&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Deployment
&lt;span class=&quot;token key atrule&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;labels&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; app&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;with&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;kustomize
  &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; app&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;with&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;kustomize
&lt;span class=&quot;token key atrule&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;replicas&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;selector&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;matchLabels&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; app&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;with&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;kustomize
  &lt;span class=&quot;token key atrule&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;labels&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; app&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;with&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;kustomize
    &lt;span class=&quot;token key atrule&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;containers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; nginx&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;1.26&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;alpine
          &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; nginx
&lt;span class=&quot;token punctuation&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# base/kustomization.yaml&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; kustomize.config.k8s.io/v1beta1
&lt;span class=&quot;token key atrule&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Kustomization
&lt;span class=&quot;token key atrule&quot;&gt;resources&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; deployment.yaml&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now let&apos;s check the overlays folder for the &lt;code class=&quot;language-text&quot;&gt;local&lt;/code&gt; environment:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# local/harbor-image-prefix.yaml&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; builtin
&lt;span class=&quot;token key atrule&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; PrefixTransformer
&lt;span class=&quot;token key atrule&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; image&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;prefix
&lt;span class=&quot;token key atrule&quot;&gt;prefix&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; harbor.ingress.local/proxy_cache/
&lt;span class=&quot;token key atrule&quot;&gt;fieldSpecs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; spec/template/spec/containers/image
&lt;span class=&quot;token punctuation&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# local/harbor-regcred.yaml&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;op&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; add
  &lt;span class=&quot;token key atrule&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; /spec/template/spec/imagePullSecrets
  &lt;span class=&quot;token key atrule&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; regcred&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;harbor
&lt;span class=&quot;token punctuation&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# local/kustomization.yaml&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; kustomize.config.k8s.io/v1beta1
&lt;span class=&quot;token key atrule&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Kustomization
&lt;span class=&quot;token key atrule&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; default
&lt;span class=&quot;token key atrule&quot;&gt;resources&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; ../base
&lt;span class=&quot;token key atrule&quot;&gt;patches&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; harbor&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;regcred.yaml
    &lt;span class=&quot;token key atrule&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; apps
      &lt;span class=&quot;token key atrule&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; v1
      &lt;span class=&quot;token key atrule&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Deployment
      &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; app&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;with&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;kustomize
&lt;span class=&quot;token key atrule&quot;&gt;transformers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; harbor&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;image&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;prefix.yaml&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Let&apos;s apply this using &lt;code class=&quot;language-text&quot;&gt;kubectl apply -k local&lt;/code&gt; and describe the pod.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/21c8af490bdcd90c1a964b1f2026432b/7321b/app-with-kustomize-describe.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 16.455696202531648%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAADCAYAAACTWi8uAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAmklEQVR42mWOSxKDIBBEvUAKiQIqUnzEv1YZTSr3P1kHMatk8epNL6ZnEuZqlKxDdR9Q1sFKI+camTBI9QRatqCiAS184NffmbuLTCO5DRpSrXDsCekX+NFAOgumPMjyBplfIN0DZDxA2g1kOK58ut+Dd6RmjpzHE9IYCDmiKlZwO0F5h0yGMtEi7bZYEhdkHz+gufmH2Qtu8QG3UFAQqOEW9wAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Output of describing the app-with-kustomize pod. It shows that the image was successfully pulled from harbor.ingress.local/proxy_cache/nginx:1.26-alpine&quot;
        title=&quot;&quot;
        src=&quot;/static/21c8af490bdcd90c1a964b1f2026432b/f058b/app-with-kustomize-describe.png&quot;
        srcset=&quot;/static/21c8af490bdcd90c1a964b1f2026432b/c26ae/app-with-kustomize-describe.png 158w,
/static/21c8af490bdcd90c1a964b1f2026432b/6bdcf/app-with-kustomize-describe.png 315w,
/static/21c8af490bdcd90c1a964b1f2026432b/f058b/app-with-kustomize-describe.png 630w,
/static/21c8af490bdcd90c1a964b1f2026432b/40601/app-with-kustomize-describe.png 945w,
/static/21c8af490bdcd90c1a964b1f2026432b/7321b/app-with-kustomize-describe.png 1184w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Let&apos;s check the &lt;code class=&quot;language-text&quot;&gt;proxy_cache&lt;/code&gt; project in the Harbor UI to see if a new registry was created there.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/5a84b0ad85414f074b2c0620ff705d45/5c5b6/app-with-kustomize-harbor.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 84.81012658227847%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAARCAYAAADdRIy+AAAACXBIWXMAAAsTAAALEwEAmpwYAAACp0lEQVR42q1T227TQBC1UBGoSFUStU6T+H5JbMf3S+y4TVVKSy+09BGJ/+MRXvkHKJEQosYSEv9RdTPMOC3ltYWHo9kZ7545c3bNWW740Y3Sn0PXvxyNg2q9w1ettfWq3eardqe7jDdotTaqtbVOg3Zrvepg3m53qw1e+L7ZE371+9IHzp2UtZdvgWz7THcj0EwHJG0EmuWB5oSg2j4oIxcUzEXDga5sAC8ZoJg2GEMHRKwPo4INwwxUJ5xz2snbH72DN/Ao2r9eiV8uVsuzxdPidLGSHt8hu4uPs5MmPitfN/tWp2eLleTwerU8hyeTV184ZfK8NvIXICc7DAEDL4e+P4VNO4aek0B/nGKMMeIa84E3ASEoQPAxehmIuMYaE4IczGw255zZUR3tnYKKhEqIo+NGM9kCLSxADfDQOAY1zGFApBZaEE0h2D0Ca7oLalzC5PAc4r0TNp4dgLe9P+d6plsbUQH9ocs2VAsU7DTKZjBMt0HHwxKqIHIBFYpu2uRqVIIWT5vmeozNky2mJdv4PZtzm0OvVvwMBDtkXdMFyc1ARlIlnWEsQMFDEgFHlf28Ae1RwhLkcNrswRqjiFMgISrUaCQrYLw+/qOoGdGOGpA6Ab2kNdUHlKPXAjVAYmx4QxgtCQ2UTwp5Y4ydCzBz9CfdQcyaXEOvdLSAlNOavCPV5KdT7tFlMAVtkILJnULFS1l/5C99uh0TIXq3yP5aLy0gqDi2GhZMRS8lL18SykhCIxPhgMa6D8gCO2ICPSnrZmR6HrIbLwlpw/3BBBJFHnZ1p0ZS4HWbddHD/0aIZM2z+WdCXncuSRnGKyS+xuJDcCW4CRFfcJKb1AreID5w1kMPqdMDwET6t8fJnBPt8L0eTL4KdvC5azgX1OUB+IT4hrf97jdHSOi8AUBRfwAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Screenshot of Harbor UI showing that there is a registry called proxy_cache/library/nginx&quot;
        title=&quot;&quot;
        src=&quot;/static/5a84b0ad85414f074b2c0620ff705d45/f058b/app-with-kustomize-harbor.png&quot;
        srcset=&quot;/static/5a84b0ad85414f074b2c0620ff705d45/c26ae/app-with-kustomize-harbor.png 158w,
/static/5a84b0ad85414f074b2c0620ff705d45/6bdcf/app-with-kustomize-harbor.png 315w,
/static/5a84b0ad85414f074b2c0620ff705d45/f058b/app-with-kustomize-harbor.png 630w,
/static/5a84b0ad85414f074b2c0620ff705d45/40601/app-with-kustomize-harbor.png 945w,
/static/5a84b0ad85414f074b2c0620ff705d45/78612/app-with-kustomize-harbor.png 1260w,
/static/5a84b0ad85414f074b2c0620ff705d45/5c5b6/app-with-kustomize-harbor.png 1265w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Perfect!&lt;/p&gt;
&lt;h4&gt;Deploying an application that uses Helm&lt;/h4&gt;
&lt;p&gt;When using Helm, there are two possibilities: you created the Helm chart, you are using someone else Helm chart. I&apos;m not going to detail the first option, since you have total control over the templates used you can simply add the variable to add the &lt;code class=&quot;language-text&quot;&gt;imagePullSecrets&lt;/code&gt; and also a variable to add a prefix to the image. The second option might be scary: what if the chart owner didn&apos;t add this variable? Well, it&apos;s possible but unlikely since these are very common things in a well-built Helm chart. Let&apos;s use &lt;a href=&quot;https://artifacthub.io/packages/helm/grafana/grafana&quot;&gt;Grafana chart&lt;/a&gt; as an example.&lt;/p&gt;
&lt;p&gt;As you can see in the &lt;code class=&quot;language-text&quot;&gt;default values&lt;/code&gt; section, there are two variables that we are going to use: &lt;code class=&quot;language-text&quot;&gt;global.imageRegistry&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;global.imagePullSecrets&lt;/code&gt;. So let&apos;s create our custom values for this chart:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# values.yaml&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;global&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;imageRegistry&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; harbor.ingress.local/proxy_cache
  &lt;span class=&quot;token key atrule&quot;&gt;imagePullSecrets&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; regcred&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;harbor&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And install it by running&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;helm repo &lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt; grafana https://grafana.github.io/helm-charts
helm repo update
helm &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; grafana grafana/grafana &lt;span class=&quot;token parameter variable&quot;&gt;-n&lt;/span&gt; default &lt;span class=&quot;token parameter variable&quot;&gt;--version&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;8.4&lt;/span&gt;.1 &lt;span class=&quot;token parameter variable&quot;&gt;--values&lt;/span&gt; values.yaml&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Let&apos;s describe the pod and check how it went:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/3c49219869020863627f4433a5ae2ba9/4d383/app-with-helm-describe.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 15.18987341772152%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAADCAYAAACTWi8uAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAp0lEQVR42lWO2w6CMBAF+QO1UJCLlVrA5aaJETD+/3+NFRITH04mu5udnOBsC8KiZlcI+8yw12cOYclRNShl0bHDlD2RZxhbn8svUeL+ZqVLgqo2pLWXXZ+E1j8cv5IK0RMn3VNkHe2w0N9edJ7tMCPdtLIb/W5ckH5eb8YOBPXVkTshbh4ktiLJG7JckHwizYTUCI08Ge7vVVK6Gwff5NtW6S1bu40fZ4xSpVDMqQIAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Output of describing the app-with-helm pod (Grafana). It shows that the image was successfully pulled from harbor.ingress.local/proxy_cache/grafana/grafana:11.1.3&quot;
        title=&quot;&quot;
        src=&quot;/static/3c49219869020863627f4433a5ae2ba9/f058b/app-with-helm-describe.png&quot;
        srcset=&quot;/static/3c49219869020863627f4433a5ae2ba9/c26ae/app-with-helm-describe.png 158w,
/static/3c49219869020863627f4433a5ae2ba9/6bdcf/app-with-helm-describe.png 315w,
/static/3c49219869020863627f4433a5ae2ba9/f058b/app-with-helm-describe.png 630w,
/static/3c49219869020863627f4433a5ae2ba9/40601/app-with-helm-describe.png 945w,
/static/3c49219869020863627f4433a5ae2ba9/4d383/app-with-helm-describe.png 1195w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Finally, let&apos;s check if the registry was created in Harbor&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/924ad77df69a7f74d80c21d4e11130a8/5c5b6/app-with-helm-harbor.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 84.81012658227847%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAARCAYAAADdRIy+AAAACXBIWXMAAAsTAAALEwEAmpwYAAACxUlEQVR42qWS227TQBCGI4gEFKlqIhonjuND7LixYzs+xIckTUtogZa2qsQNEu/HJdzyDlAiIURNJCTeo+pm+NcJLQguoFx8mp09/Dv/7Ja6jv/WCQZfTds777pB3pL1XKiJuSTKuVCX8mq1llcqSzY2NvP19WpBZeNBXkVeqQj5Zk36XG9I30RRflNyktHcTcekWB5rOwG1txxS9C4Zlkc6cg1RwZzadall2CQoBtVkg9SORYZpUwvzZpAx049Js/1Zqf3s5ZfG4xd0K9i/LIdPFmujk8Xd7Ghxe3AIDhZlxHJ8eB1X3B+dLu5lx4u14cmiHD29XBud0p3k6ENJTaZzI90jJdphgJpuSqI3pLoVUsOOSOwNEEPEqEDyUpL6wE2WIG86MeNznXgyK9mTg3nw6Jg0CKo+rHsJdaIxtf2MtD4O9ELSfByygoJOskP+9ID0wTaZGI+OnvOc9SaPyd3en5UaHWduBBmJpsM2tS6puGkrnpCJA3owJNmNC3EJlbacASlYb+NCDXN8r4GxHo5ZO9rGejwr1U13rnoxSZbPhI5DshPjEKobTIrIK+bILo8pqQAHSQ1GpPij5R4/Yzxv2iEEUWGbW+r2Wc3oQXBQVNTkfev6JBZWQ1QYrcZBMZbctEDmLryE8cqbdrAUNMIhKgxYTbdJwwYzm5IW71I7mZIawl44IgNtULGmIddhkwvwvb3xHpnpLlO4cD+9rlB1B6xhuqSiOj15SHo6LaJWCAJYuhqv4H3r4nI8DjPiHezJloIKbHLL4pZXWL3CCn/N/wB/NDwWg23eiqUg/x6KE/4u+BfwvgLWRF+LHgq6PYco1XSLCXiUfxVcwSTu8mdBiBXf5r8F8bLnvDLECwhfYvImXEgOtxyelWQnmqtoLD44a6CH/KYbwFo/HqVl+a/1fvJRsvrvBcM+47fcgHfgEz79q+8pqOqbV6rfzQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Screenshot of Harbor UI showing that there is a registry called proxy_cache/library/nginx&quot;
        title=&quot;&quot;
        src=&quot;/static/924ad77df69a7f74d80c21d4e11130a8/f058b/app-with-helm-harbor.png&quot;
        srcset=&quot;/static/924ad77df69a7f74d80c21d4e11130a8/c26ae/app-with-helm-harbor.png 158w,
/static/924ad77df69a7f74d80c21d4e11130a8/6bdcf/app-with-helm-harbor.png 315w,
/static/924ad77df69a7f74d80c21d4e11130a8/f058b/app-with-helm-harbor.png 630w,
/static/924ad77df69a7f74d80c21d4e11130a8/40601/app-with-helm-harbor.png 945w,
/static/924ad77df69a7f74d80c21d4e11130a8/78612/app-with-helm-harbor.png 1260w,
/static/924ad77df69a7f74d80c21d4e11130a8/5c5b6/app-with-helm-harbor.png 1265w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Perfect!&lt;/p&gt;
&lt;h4&gt;Deploy an application whose image is already presented in the node&lt;/h4&gt;
&lt;p&gt;Now we are going to deploy an application using an image directly from Dockerhub and modifying it to use Harbor proxy cache and see how it&apos;s going to behave, it might surprise you.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; apps/v1
&lt;span class=&quot;token key atrule&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Deployment
&lt;span class=&quot;token key atrule&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;labels&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; dummy
  &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; dummy
  &lt;span class=&quot;token key atrule&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; default
&lt;span class=&quot;token key atrule&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;replicas&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;selector&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;matchLabels&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; dummy
  &lt;span class=&quot;token key atrule&quot;&gt;strategy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;labels&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; dummy
    &lt;span class=&quot;token key atrule&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;containers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; httpd
        &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; httpd&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Let&apos;s apply that and see that indeed it worked as expected. The pod is running. Now, let&apos;s modify this deployment a bit to use the Harbor proxy cache and the registry private credential.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; apps/v1
&lt;span class=&quot;token key atrule&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Deployment
&lt;span class=&quot;token key atrule&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;labels&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; dummy
  &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; dummy
&lt;span class=&quot;token key atrule&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;replicas&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;selector&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;matchLabels&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; dummy
  &lt;span class=&quot;token key atrule&quot;&gt;strategy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;labels&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; dummy
    &lt;span class=&quot;token key atrule&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;imagePullSecrets&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; regcred&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;harbor
      &lt;span class=&quot;token key atrule&quot;&gt;containers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; harbor.ingress.local/proxy_cache/httpd
        &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; httpd&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now, let&apos;s describe the pod and see if it pulled the image correctly:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/6a16c184bd79842a41edb3fa06a05624/7321b/app-already-present-describe.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 15.822784810126581%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAADCAYAAACTWi8uAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAmklEQVR42k2ObQ6DIBBEPUCjoIKoKCAq4kfTtN7/alNSmtYfL28z2c1OwgcJwWbU+QIhZzRSoWQ9ikqD6A20HkG5CQx/qi/MREoNWqiPk5uTaLsNY35CDjvmVUEaDd5ZZO6JzL+QLcHrGe3POAcTewcxB2jrQJopPJ+QpM6C6wNSPCDsAeVHlGZB2vqwvP+OSO9jE3ZpdoXH/A2Vj0/+90T36gAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Output of describing the httpd pod. It shows that the image was successfully pulled from harbor.ingress.local/proxy_cache/httpd&quot;
        title=&quot;&quot;
        src=&quot;/static/6a16c184bd79842a41edb3fa06a05624/f058b/app-already-present-describe.png&quot;
        srcset=&quot;/static/6a16c184bd79842a41edb3fa06a05624/c26ae/app-already-present-describe.png 158w,
/static/6a16c184bd79842a41edb3fa06a05624/6bdcf/app-already-present-describe.png 315w,
/static/6a16c184bd79842a41edb3fa06a05624/f058b/app-already-present-describe.png 630w,
/static/6a16c184bd79842a41edb3fa06a05624/40601/app-already-present-describe.png 945w,
/static/6a16c184bd79842a41edb3fa06a05624/7321b/app-already-present-describe.png 1184w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Now if you check Harbor UI you will see that the &lt;code class=&quot;language-text&quot;&gt;httpd&lt;/code&gt; image is not there! Why? The image was already present in our node! So kubelet queried Harbor to resolve the name of the image (in our case &lt;code class=&quot;language-text&quot;&gt;httpd:latest&lt;/code&gt;) to an image digest. Since the image digest wanted was the same as the one cached locally (if I waited a time long enough to make the digest different - i.e. a new image was pushed to &lt;code class=&quot;language-text&quot;&gt;latest&lt;/code&gt; tag - this would be different!) then kubelet used the cached image (presented on the node).&lt;/p&gt;
&lt;p&gt;Let&apos;s run an experiment to prove our point. Let&apos;s connect to the Kubernetes node and delete the image presented on the node.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token function&quot;&gt;docker&lt;/span&gt; &lt;span class=&quot;token builtin class-name&quot;&gt;exec&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-it&lt;/span&gt; kind-worker &lt;span class=&quot;token function&quot;&gt;bash&lt;/span&gt;
crictl rmi harbor.ingress.local/proxy_cache/httpd&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now, let&apos;s roll out the deployment (or simply delete the pod, downtime is irrelevant here) and check if the image will now be present in Harbor.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/92f87ad1106fc6af3d89f9665b65505d/5c5b6/app-already-present-harbor.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 84.81012658227847%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAARCAYAAADdRIy+AAAACXBIWXMAAAsTAAALEwEAmpwYAAACyUlEQVR42qVSyW4TQRAdoSBQkCLbSsaxPftmz7hn3+yxnY3sJCBOSPwfR7jyDxAsIUTMSEj8R5R2UT12kgMKSOHwVN0z1a9evSquR4KPJEx+WsS77Pb92XqDn9XW1mf1Oj+rN5qLuESttjFbW2tUqNfWZw281+vN2QYvfN9sCb/abekDR7KidPMRyLZHdRKCZjogaV3Qei5oTgCq7YHSJaDgXTQcaMoG8JIBimmDYTkg4ncrHFArSEF1gimnnb790Tp4A4/C/euV6HC+WpzPnw7O5ivJyR3Su/g4Pa3is+Jllbc6PJ+vxEfXq8UreJK9+MIp2W5p5M9BjrcoAjpuDm1vCJt2BC0nhnY/wRhhxDPeBS8D0R+A4GaYm4Lo5dAhKRX8HMx0MuWcyXEZ7p2BioRKgK3jAzMegRYMQPUzEPoRqAE+ckJo2yHoyRj83RMwsy0w0gkUp68h3Duh/ckBuOP9KdcySWmEA2hbhG6oPVCwUhcTLXyoh0OQUAUjF1CpSBKQ8b8WFaDiPxnVGfEY7yOqoQiRpFNu03JLxUtBsAPaNAlIJMVHqC6ZVJEpZpDcrCJQEPgQlLAAmZFijuTlVMScjhMhISrUWEs9n/J6/1ZRh/lmL9pkZ4F5uLwztQJ6LSC5xIiRkBHfEhrRsFLIG31QkMzId1DhVgWZeYlKDLTg5szak7B1lmsPd9kwqMLs8bM7hYqb0JblVv5YowMwx4dgTY6QfBt09PQGBhsGYnHeBme0D9Zgh7I8NSwWhDKazVpudb2qZTnAaogqutm9YJ6arFCMQwlHKCpfELL1kElE20hYebb06sa/v2GZRwUmygmnXFN3SiQFXrdpEz38F8E9+JMQyaq1+W9CXncumTKMV0h8jR8fgiuBxIz4gpNIXCo4CFzwaiis0gNQLTbu6pQT7eC97mdfBdv/3DScC1blAfiE+IbDfPcb/rbrPjsigKsAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Screenshot of Harbor UI showing that there is a registry called proxy_cache/library/httpd&quot;
        title=&quot;&quot;
        src=&quot;/static/92f87ad1106fc6af3d89f9665b65505d/f058b/app-already-present-harbor.png&quot;
        srcset=&quot;/static/92f87ad1106fc6af3d89f9665b65505d/c26ae/app-already-present-harbor.png 158w,
/static/92f87ad1106fc6af3d89f9665b65505d/6bdcf/app-already-present-harbor.png 315w,
/static/92f87ad1106fc6af3d89f9665b65505d/f058b/app-already-present-harbor.png 630w,
/static/92f87ad1106fc6af3d89f9665b65505d/40601/app-already-present-harbor.png 945w,
/static/92f87ad1106fc6af3d89f9665b65505d/78612/app-already-present-harbor.png 1260w,
/static/92f87ad1106fc6af3d89f9665b65505d/5c5b6/app-already-present-harbor.png 1265w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Bingo!&lt;/p&gt;
&lt;h2&gt;Cya&lt;/h2&gt;
&lt;p&gt;Hope you enjoyed this blog post! Please, don&apos;t copy and paste this to directly deploy to the production environment. There are SEVERAL things that you would need to do to be in a production-ready state. Here are a few tips before deploying this in a real environment.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Use TLS and certificates&lt;/strong&gt;: Enhance your security by using TLS/HTTPS and certificates. Adding insecure registries to Kubernetes nodes is not a good practice.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Configure external database for Harbor&lt;/strong&gt;: The configurations for Harbor (such as SSO, and users) are all stored in the database. By default, the Helm chart deploys a Postgres database for you but it&apos;s more secure to use an external Postgres database (it can even be a database that you run inside Kubernetes with an Operator).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Don&apos;t Harbor credentials explicit in Terraform variables&lt;/strong&gt;: A good option is to store this somewhere safe (AWS Secrets Manager, SOPS, Hashicorp Vault...) and pass only the name of the secret that contains the credentials. This way you can use the correct provider to pull this information.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Don&apos;t use admin user credentials as image pull secret&lt;/strong&gt;: Create a user with limited permissions and use these credentials&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;See you in the next post! 👋&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Local Kubernetes deployment with LoadBalancer and name resolution in MacOs and Linux]]></title><description><![CDATA[The goal of this post is to teach you (explaining the concepts, problems and doing a demo) how to create a local Kubernetes cluster, expose…]]></description><link>https://felipetrindade.com/kubernetes-ingress-load-balancer/</link><guid isPermaLink="false">https://felipetrindade.com/kubernetes-ingress-load-balancer/</guid><pubDate>Sun, 28 Jul 2024 20:20:32 GMT</pubDate><content:encoded>&lt;p&gt;The goal of this post is to teach you (explaining the concepts, problems and doing a demo) how to create a local Kubernetes cluster, expose your application using an Load Balancer and manage the traffic using Ingress using Linux and MacOs.&lt;/p&gt;
&lt;p&gt;I will slowly explain the logic behind the setup and the need for every tool used here. If you want to check the code directly you can take a look on the &lt;a href=&quot;https://github.com/felipelaptrin/local-kubernetes-ingress-load-balancer&quot;&gt;GitHub repository&lt;/a&gt;. Let&apos;s start!&lt;/p&gt;
&lt;h2&gt;Local Kubernetes cluster&lt;/h2&gt;
&lt;p&gt;There are several tools to run Kubernetes locally, such as &lt;a href=&quot;https://kind.sigs.k8s.io/&quot;&gt;Kind&lt;/a&gt;, &lt;a href=&quot;https://k3s.io/&quot;&gt;k3s&lt;/a&gt;, &lt;a href=&quot;https://k3d.io/v5.7.2/&quot;&gt;k3d&lt;/a&gt;, &lt;a href=&quot;https://minikube.sigs.k8s.io/docs/start/?arch=%2Fmacos%2Farm64%2Fstable%2Fbinary+download&quot;&gt;Minikube&lt;/a&gt;. My personal choice is always Kind because it&apos;s lightweight (Kind stands for Kubernetes in Docker) because it runs in containers instead of virtual machines and is one of the few tools (if not the only) that allows you to run a multi node cluster (multi control plane and multi worker nodes), which is very handy to test affinity, tolerance, rolling restarts... So we are going to use Kind to provision a local Kubernetes cluster.&lt;/p&gt;
&lt;p&gt;Since Kind uses containers we also need to choose a container runtime to run our containers (nodes of the Kubernetes cluster). I will stick to &lt;code class=&quot;language-text&quot;&gt;Docker Desktop&lt;/code&gt; for MacOs and Linux, which comes by default when you intall Docker.&lt;/p&gt;
&lt;p&gt;For MacOs there are other options, such as &lt;a href=&quot;https://github.com/abiosoft/colima&quot;&gt;Colima&lt;/a&gt; or &lt;a href=&quot;https://orbstack.dev/&quot;&gt;Orbstack&lt;/a&gt;. Due to Docker Inc &lt;a href=&quot;https://www.docker.com/blog/updating-product-subscriptions/&quot;&gt;licence agreement change&lt;/a&gt; (Docker Desktop is no longer free for enterprise usage), some engineers decided to use Colima, since an open-source project, to setup the container environment. In my case Docker Desktop is fine, specially because for the Kubernetes set up I&apos;ll comment in this blog post I will need a tool that is only supported in Docker Desktop.&lt;/p&gt;
&lt;h2&gt;Docker container network&lt;/h2&gt;
&lt;p&gt;To run containers in MacOs you will need to run them inside a Linux virtual machine (VM), since Docker containers run o a Linux kernel. This means that Docker Desktop and Colima, for exameple, will create VMs to run your containers.&lt;/p&gt;
&lt;p&gt;This reminds me of Matrix. In fact, when running a container in MacOs we are running the container inside a Virtual Machine. So in MacOs, when you run a container with port-forward (e.g. &lt;code class=&quot;language-text&quot;&gt;docker container run nginx -p 3000:80&lt;/code&gt;) the traffic gets routed to the VM and then the VM routes the request to the container inside it.&lt;/p&gt;
&lt;p&gt;So, using MacOs, because the containers run inside a Virtual Machine we can&apos;t directly access their network interface, there is no traffic by default to the VM. We could configure the iptable and routing rules to send traffic from MacOs to the VM and then set forward traffic rules to the Kind network. This is tedious. I way better and automatic approach of managing that is to install &lt;a href=&quot;https://github.com/chipmk/docker-mac-net-connect&quot;&gt;docker-mac-net-connect&lt;/a&gt;. This tool allow us to directly connect to the ip of the container.&lt;/p&gt;
&lt;p&gt;Let&apos;s give an example to make it easier to understand. Without the tool installed I will run an nginx container without port-forward (&lt;code class=&quot;language-text&quot;&gt;docker container run -d --name nginx nginx&lt;/code&gt;). Without port-forward you won&apos;t be able to access this container. Let&apos;s check the IP of the container (&lt;code class=&quot;language-text&quot;&gt;NGINX_IP=$(docker inspect --format &apos;{{ .NetworkSettings.IPAddress }}&apos; nginx)&lt;/code&gt;) and try to run a curl (&lt;code class=&quot;language-text&quot;&gt;curl $NGINX_IP). You will get a timeout. You simply can&apos;t reach the container, there is no traffic. Now, install the &lt;/code&gt;docker-mac-net-connect` tool and you will see that you are able to get a response from the NGINX container! Don&apos;t forget to kill the container we created for testing (docker rm -f nginx).&lt;/p&gt;
&lt;p&gt;If you are a Linux user, you can ignore all the previous explanation. In Linux, you have direct access to the docker network interface and can access the containers directly by IP!&lt;/p&gt;
&lt;h2&gt;MetalLB&lt;/h2&gt;
&lt;p&gt;This setup is completely local (similar to an on-premise environment), there is no cloud involved so if you want to expose your application using a LoadBalancer you will need to have a tool to create this LoadBalancer for you inside your cluster. The most popular tool is, by far, &lt;a href=&quot;https://metallb.universe.tf/&quot;&gt;MetalLB&lt;/a&gt;. MetalLb provides a network load balancer implementation, i.e. allows you to create Kubernetes services of type LoadBalancer in clusters that don&apos;t run on a cloud provider.&lt;/p&gt;
&lt;p&gt;To use MetalLb you will need to define two things (considering you have already installed it in your cluster):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The IPs that MetalLb will assign to the load balancer. Notice that for our use-case, this IP must be in the same network space (network CIDR) of our local Kubernetes cluster. This is done by instanciating a Kubernetes resource of kind &lt;code class=&quot;language-text&quot;&gt;IPAddressPool&lt;/code&gt; (a custom resource that comes with MetalLB installation).&lt;/li&gt;
&lt;li&gt;Announce the IPs of the load balancer to make the network beyond our cluster aware of the IPs of the LoadBalancers. This can be achieve using two routing protocols: ARP/NDP (OSI Layer 2) or BGP. This is done by instanciating a Kubernetes resource of kind &lt;code class=&quot;language-text&quot;&gt;L2Advertisement&lt;/code&gt; for ARP. BGP set up is more specific and we won&apos;t use in this demo, so I won&apos;t comment to make this post less confusing.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To summarize, MetalLb allow us to expose your applications (demo comes soon!). So if we deploy an application (e.g. Kubernetes Deployment) and expose this application using Kubernetes service of type &lt;code class=&quot;language-text&quot;&gt;LoadBalancer&lt;/code&gt;, MetalLB will assign one IP for our service and user using this IP can access our applications.&lt;/p&gt;
&lt;h2&gt;Ingress Controller&lt;/h2&gt;
&lt;p&gt;Let&apos;s be honest, directly using IPs to access application is something tedious and no one does that. We access applications using names (e.g. &lt;code class=&quot;language-text&quot;&gt;myapp.mydomain.tld&lt;/code&gt;). Also, if we expose every application with a load balancer we might run out of IPs.&lt;/p&gt;
&lt;p&gt;A common practice is to re-use the LoadBalancer and internally (inside Kubernets) forward the traffic to the requested service. There are several cloud-agnostic ingress controllers, such as &lt;a href=&quot;https://docs.nginx.com/nginx-ingress-controller/&quot;&gt;NGINX Ingress&lt;/a&gt; and &lt;a href=&quot;https://doc.traefik.io/traefik/&quot;&gt;Traefik&lt;/a&gt;. Since I&apos;m more familiar with NGINX Ingress will use it in this post.&lt;/p&gt;
&lt;h2&gt;Demo time&lt;/h2&gt;
&lt;p&gt;For this demo I will assume you have:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/chipmk/docker-mac-net-connect&quot;&gt;docker-mac-net-connect&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://kubernetes.io/docs/tasks/tools/#kubectl&quot;&gt;Kubectl&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://kind.sigs.k8s.io/&quot;&gt;Kind&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.docker.com/&quot;&gt;Docker&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://helm.sh/&quot;&gt;Helm&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For a better experience, please check the content of &lt;a href=&quot;https://github.com/felipelaptrin/local-kubernetes-ingress-load-balancer&quot;&gt;GitHub repository&lt;/a&gt; containing all code needed to run this demo.&lt;/p&gt;
&lt;h3&gt;Creating the Kind Cluster&lt;/h3&gt;
&lt;p&gt;The MetalLB IPs need to be in the range of the IPs of the cluster. I good strategy is to before the Kubernetes cluster creation to create the range of IPs we want our Kubernetes cluster to be in. This way the Kubernetes manifest for the MetalLB ip pool (&lt;code class=&quot;language-text&quot;&gt;IPAddressPool&lt;/code&gt;) won&apos;t need to be modified. This docker network will be used by our Kind cluster on the following step.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token function&quot;&gt;docker&lt;/span&gt; network create &lt;span class=&quot;token parameter variable&quot;&gt;--subnet&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;172.100&lt;/span&gt;.0.0/16 custom-kind-network&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now, let&apos;s create our local Kind cluster with two nodes: one control-plane and one worker.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token function&quot;&gt;cat&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;EOF&lt;span class=&quot;token bash punctuation&quot;&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; /tmp/kind-config.yaml&lt;/span&gt;
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
  image: kindest/node:v1.30.2
- role: worker
  image: kindest/node:v1.30.2
EOF&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;KIND_EXPERIMENTAL_DOCKER_NETWORK&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;custom-kind-network kind create cluster &lt;span class=&quot;token parameter variable&quot;&gt;--config&lt;/span&gt; /tmp/kind-config.yaml&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You can check the nodes of the cluster by running &lt;code class=&quot;language-text&quot;&gt;docker ps&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;Installing MetalLB&lt;/h3&gt;
&lt;p&gt;Install MetalLB using Helm.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;helm repo &lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt; metallb https://metallb.github.io/metallb
helm &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; metallb &lt;span class=&quot;token parameter variable&quot;&gt;-n&lt;/span&gt; metallb --create-namespace metallb/metallb
&lt;span class=&quot;token function&quot;&gt;sleep&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;
kubectl &lt;span class=&quot;token function&quot;&gt;wait&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-n&lt;/span&gt; metallb &lt;span class=&quot;token parameter variable&quot;&gt;-l&lt;/span&gt; app.kubernetes.io/component&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;controller &lt;span class=&quot;token parameter variable&quot;&gt;--for&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;condition&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;ready pod &lt;span class=&quot;token parameter variable&quot;&gt;--timeout&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;120s&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now, let&apos;s create a range of IPs (that must be inside the docker network we just created).&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;kubectl apply &lt;span class=&quot;token parameter variable&quot;&gt;-f&lt;/span&gt; - &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;EOF
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
  name: ip-pool
  namespace: metallb
spec:
  addresses:
  - 172.100.150.0-172.100.150.10
EOF&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Notice that we are allocating 10 IPs for load balancer in our local cluster, which is more than enought for this demo.&lt;/p&gt;
&lt;p&gt;Now, using ARP protocol, let&apos;s announce these IPs.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;kubectl apply &lt;span class=&quot;token parameter variable&quot;&gt;-f&lt;/span&gt; - &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;EOF
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
  name: advertisement-l2
  namespace: metallb
spec:
  ipAddressPools:
  - ip-pool
EOF&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;Install NGINX Ingress&lt;/h3&gt;
&lt;p&gt;Install NGINX Ingress using Helm.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;helm repo &lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt; ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update
helm &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; ingress-nginx &lt;span class=&quot;token parameter variable&quot;&gt;-n&lt;/span&gt; ingress-nginx --create-namespace ingress-nginx/ingress-nginx&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Run &lt;code class=&quot;language-text&quot;&gt;kubectl get service -n ingress-nginx&lt;/code&gt; to notice that NGINX Ingress create a service of type Load Balancer (and MetalLB assigned an external ip to this Load Balancer in the range we specified before). This will be used later!&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/614447d9f0f07f9f77bcc99e51726cbb/ecf19/nginx-load-balancer.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 6.329113924050632%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAABCAYAAADeko4lAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAUElEQVR42gXB3Q5AIACAUU9AN1ghrdaPZaNp7pj3f6nPOY28Htp00W6VLhSE2REqMWRPlzWjDFT3EaeK8yelviwmc5RI2CxqXlmNRmpLn29+Zh4cezb90oYAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Screenshot showing kubectl output of command &amp;quot;kubectl get service -n ingress-nginx&amp;quot;. It shows NGINX service with external IP assigned by MetalLB&quot;
        title=&quot;&quot;
        src=&quot;/static/614447d9f0f07f9f77bcc99e51726cbb/f058b/nginx-load-balancer.png&quot;
        srcset=&quot;/static/614447d9f0f07f9f77bcc99e51726cbb/c26ae/nginx-load-balancer.png 158w,
/static/614447d9f0f07f9f77bcc99e51726cbb/6bdcf/nginx-load-balancer.png 315w,
/static/614447d9f0f07f9f77bcc99e51726cbb/f058b/nginx-load-balancer.png 630w,
/static/614447d9f0f07f9f77bcc99e51726cbb/40601/nginx-load-balancer.png 945w,
/static/614447d9f0f07f9f77bcc99e51726cbb/ecf19/nginx-load-balancer.png 948w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3&gt;Deploy application with LoadBalancer service&lt;/h3&gt;
&lt;p&gt;Let&apos;s deploy an application that uses LoadBalancer service type.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;kubectl apply &lt;span class=&quot;token parameter variable&quot;&gt;-f&lt;/span&gt; - &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;EOF
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: app-load-balancer
  name: app-load-balancer
  namespace: default
spec:
  replicas: 1
  selector:
    matchLabels:
      app: app-load-balancer
  template:
    metadata:
      labels:
        app: app-load-balancer
    spec:
      containers:
      - image: nginx
        name: nginx
        command:
          - sh
          - -c
          - &quot;echo &apos;Hello, from app-load-balancer&apos; &gt; /usr/share/nginx/html/index.html &amp;amp;&amp;amp; nginx -g &apos;daemon off;&apos;&quot;
EOF&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And for the service:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;kubectl apply &lt;span class=&quot;token parameter variable&quot;&gt;-f&lt;/span&gt; - &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;EOF
apiVersion: v1
kind: Service
metadata:
  labels:
    app: app-load-balancer
  name: app-load-balancer
  namespace: default
spec:
  ports:
  - port: 80
    protocol: TCP
    targetPort: 80
  selector:
    app: app-load-balancer
  type: LoadBalancer
EOF&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now, let&apos;s check if our application was correctly provisioned with a Load Balancer service with an external IP (run &lt;code class=&quot;language-text&quot;&gt;kubectl get service -n default&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/6903a6a0a9b9a3e0e86e7958d85168dd/d6331/app-load-balancer.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 5.69620253164557%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAABCAYAAADeko4lAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAU0lEQVR42g3K0QpAMABAUV+AUmRNGFpWC7PalCJv/v+DLuf5JOZ6yedAbiLZuFP1E8Jp0qGhEAo7HPj5pqstaloJ8WHZToTUOG9Z/isbidCOsjV8iI0dQ0KPfIEAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Screenshot showing kubectl output of command &amp;quot;kubectl get service -n default&amp;quot;. It shows app-load-balancer service with external IP assigned by MetalLB&quot;
        title=&quot;&quot;
        src=&quot;/static/6903a6a0a9b9a3e0e86e7958d85168dd/f058b/app-load-balancer.png&quot;
        srcset=&quot;/static/6903a6a0a9b9a3e0e86e7958d85168dd/c26ae/app-load-balancer.png 158w,
/static/6903a6a0a9b9a3e0e86e7958d85168dd/6bdcf/app-load-balancer.png 315w,
/static/6903a6a0a9b9a3e0e86e7958d85168dd/f058b/app-load-balancer.png 630w,
/static/6903a6a0a9b9a3e0e86e7958d85168dd/d6331/app-load-balancer.png 702w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Now, let&apos;s check if we can access this service via Load Balancer external IP (&lt;code class=&quot;language-text&quot;&gt;172.100.150.1&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/5574b73afbf837e9389be9f09dc221aa/f793b/app-load-balancer-access.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 15.822784810126581%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAADCAYAAACTWi8uAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAfklEQVR42p2ITQ6CMBgFewIS1ySt/JR+pAYTPTZagepaIwXUQ/WBXRpWTjKZl8cy0tgWJaTehwpJ4BlB5AqpqiCSHVR5BE80Yk4o6IBUVog28aqs6Tq01uJkLjBNi/psYK83uGFcnNC7F4bxE/rsp+V7h31/uFWZ9x5ffvsvM82Iq4q8PaNJAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Screenshot of the browser accessing ip 172.100.150.1. The text says &amp;quot;Hello, from app-load-balancer&amp;quot;&quot;
        title=&quot;&quot;
        src=&quot;/static/5574b73afbf837e9389be9f09dc221aa/f058b/app-load-balancer-access.png&quot;
        srcset=&quot;/static/5574b73afbf837e9389be9f09dc221aa/c26ae/app-load-balancer-access.png 158w,
/static/5574b73afbf837e9389be9f09dc221aa/6bdcf/app-load-balancer-access.png 315w,
/static/5574b73afbf837e9389be9f09dc221aa/f058b/app-load-balancer-access.png 630w,
/static/5574b73afbf837e9389be9f09dc221aa/40601/app-load-balancer-access.png 945w,
/static/5574b73afbf837e9389be9f09dc221aa/78612/app-load-balancer-access.png 1260w,
/static/5574b73afbf837e9389be9f09dc221aa/f793b/app-load-balancer-access.png 1404w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3&gt;Deploy application with Ingress&lt;/h3&gt;
&lt;p&gt;Let&apos;s deploy an application that uses the NGINX Ingress. Starting with the deployment manifest:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;kubectl apply &lt;span class=&quot;token parameter variable&quot;&gt;-f&lt;/span&gt; - &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;EOF
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: app-ingress
  name: app-ingress
  namespace: default
spec:
  replicas: 1
  selector:
    matchLabels:
      app: app-ingress
  template:
    metadata:
      labels:
        app: app-ingress
    spec:
      containers:
      - image: nginx
        name: nginx
        command:
          - sh
          - &quot;-c&quot;
          - &quot;echo &apos;Hello, from app-ingress&apos; &gt; /usr/share/nginx/html/index.html &amp;amp;&amp;amp; nginx -g &apos;daemon off;&apos;&quot;
EOF&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The service&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;kubectl apply &lt;span class=&quot;token parameter variable&quot;&gt;-f&lt;/span&gt; - &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;EOF
apiVersion: v1
kind: Service
metadata:
  labels:
    app: app-ingress
  name: app-ingress
  namespace: default
spec:
  ports:
  - port: 80
    protocol: TCP
    targetPort: 80
  selector:
    app: app-ingress
  type: ClusterIP
EOF&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And finally the ingress&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;kubectl apply &lt;span class=&quot;token parameter variable&quot;&gt;-f&lt;/span&gt; - &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;EOF
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: app-ingress
  namespace: default
spec:
  ingressClassName: nginx
  rules:
  - host: app-ingress.local
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: app-ingress
            port:
              number: 80
EOF&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;app-ingress&lt;/code&gt; application is exposed by the NGINX Ingress Load Balancer and routed accordingly by the Ingress resource. In order to locally resolve &quot;app-ingress.local&quot; we will need to add this entry to our &lt;code class=&quot;language-text&quot;&gt;/etc/hosts&lt;/code&gt; file. If it was a real Kubernetes deployment we would add an A record to the DNS server. To be honest, you can setup a local DNS server (using dnsmasq for example) to specify that all &lt;code class=&quot;language-text&quot;&gt;.local&lt;/code&gt; names should be resolved to the IP of the NGINX Load Balancer. This is specially useful if you have several services exposed locally (&lt;code class=&quot;language-text&quot;&gt;/etc/hosts&lt;/code&gt; does not support wildcard, so we would need to add an entry for every service we expose via Ingress).&lt;/p&gt;
&lt;p&gt;Get the NGINX Ingress IP&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token assign-left variable&quot;&gt;INGRESS_LB_IP&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;kubectl get svc ingress-nginx-controller &lt;span class=&quot;token parameter variable&quot;&gt;-n&lt;/span&gt; ingress-nginx &lt;span class=&quot;token parameter variable&quot;&gt;-o&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;jsonpath&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;{.status.loadBalancer.ingress[0].ip}&apos;&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Add &lt;span class=&quot;token variable&quot;&gt;$INGRESS_LB_IP&lt;/span&gt; to /etc/hosts&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now add this IP to the &lt;code class=&quot;language-text&quot;&gt;/etc/hosts&lt;/code&gt; file&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$INGRESS_LB_IP&lt;/span&gt; app-ingress.local&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;tee&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-a&lt;/span&gt; /etc/hosts&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now access &lt;code class=&quot;language-text&quot;&gt;http://app-ingress.local&lt;/code&gt;. Check if the application is accessible.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/449aad732ace7633858c99893e09c3b8/f793b/app-ingress-access.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 15.822784810126581%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAADCAYAAACTWi8uAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAhElEQVR42qXFSw6CMBhF4W5Ao1MlVEAoBcNAd00k8hgYnUik5eGiev1p4sA49CRfDgviFLtIIkwyegI3EHC8EHwv4IsM3DtAyBM9hUsiebQcLrFYbbBcb7+wsq5RNQ2KS4n8XOB6u0PpHp3S9IE+Qvcv+5nSk/XsBjxa9YMZYzD3+b+9ASEUrBlovYlkAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Screenshot of the browser accessing http://app-ingress.local/. The text says &amp;quot;Hello, from app-ingress&amp;quot;&quot;
        title=&quot;&quot;
        src=&quot;/static/449aad732ace7633858c99893e09c3b8/f058b/app-ingress-access.png&quot;
        srcset=&quot;/static/449aad732ace7633858c99893e09c3b8/c26ae/app-ingress-access.png 158w,
/static/449aad732ace7633858c99893e09c3b8/6bdcf/app-ingress-access.png 315w,
/static/449aad732ace7633858c99893e09c3b8/f058b/app-ingress-access.png 630w,
/static/449aad732ace7633858c99893e09c3b8/40601/app-ingress-access.png 945w,
/static/449aad732ace7633858c99893e09c3b8/78612/app-ingress-access.png 1260w,
/static/449aad732ace7633858c99893e09c3b8/f793b/app-ingress-access.png 1404w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;If you are done with the demo just run the command below to destroy the Kubernetes cluster.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;kind delete cluster&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;Cya&lt;/h2&gt;
&lt;p&gt;Hope you enjoyed this blog post! There only a few resources online explaining how to do that using MacOs, because the VMs is tricky. This demo works for both Linux and MacOs and I hope you found it helpful!&lt;/p&gt;
&lt;p&gt;See you in the next post! 👋&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Consistent deployments - a real-world case scenario]]></title><description><![CDATA[Recently I was part of a project that didn't have consistent deployments (infra and application-related). I'll go over some problems faced…]]></description><link>https://felipetrindade.com/consistent-deployments/</link><guid isPermaLink="false">https://felipetrindade.com/consistent-deployments/</guid><pubDate>Sun, 09 Jun 2024 11:20:32 GMT</pubDate><content:encoded>&lt;p&gt;Recently I was part of a project that didn&apos;t have consistent deployments (infra and application-related). I&apos;ll go over some problems faced by the client and the proposed solution for that.&lt;/p&gt;
&lt;p&gt;If you would like to directly check the code used in this blog post, please check the following GitHub repositories:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/felipelaptrin/consistent-deployments-app-blog&quot;&gt;consistent-deployments-app&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/felipelaptrin/consistent-deployments-infra-blog&quot;&gt;consistent-deployments-infra&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Deployment state&lt;/h2&gt;
&lt;p&gt;The client had two repositories: one for infrastructure and another one for all the applications (Lambdas and Glue jobs). The CI/CD of the application repository was responsible for running tests, building the Docker image, pushing the image (latest tag) to the ECR of the environment, and updating Lambda code to use this new code version. The CI/CD of the infra repository was responsible for planning the changes and then deploying the changes to the infrastructure (Lambda, Glue jobs and other things).&lt;/p&gt;
&lt;p&gt;Before we start to comment on it I would like to clarify some concepts (I will use these words a lot during this blog post):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Artifacts (assets)&lt;/strong&gt;: It represents the application. In our case Docker images (used in the Lambdas). It could be another thing, such as a JAR file, a zip file containing application code, or Python scripts for Glue jobs...&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Manifest&lt;/strong&gt;: Declarative configuration of the infrastructure. In our case, it will be Terraform files, but it could have been CDK code, CloudFormation template, Pulumi code...&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;At first glance, this looks perfectly fine. Indeed, I&apos;ve seen that multiple times in multiple clients, but there are several problems here and I would highlight four:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Deployments are not atomic. The configuration/infra and artifact should be deployed simultaneously: either both are successfully deployed, or neither is.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Ensuring that both the application code (artifact) and its configuration are deployed together reduces the risk of incompatibilities because partial deployments can lead to failures and unexpected behaviors if the configuration does not match the deployed code. Also, managing dependencies and orchestrating deployments to ensure they happen simultaneously can be challenging.&lt;/p&gt;
&lt;p&gt;Imagine the scenario where a Lambda needs to access a new cloud resource (e.g. S3, SQS...). If the application code is updated before the IAM Role has the needed permissions it can lead to failures. We want to deploy the new code with the needed permissions simultaneously. The same problem would happen if it was a new code that requires more memory/CPU or a new environment variable.&lt;/p&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;Different artifacts are being used in different environments. There is no artifact promotion involved.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;For every environment, a new Docker image is being built and pushed to ECR. Deploying the same artifact that was tested ensures the environment closely mirrors the one in which the artifact was validated (i.e. production environment should use the same artifact that was tested in staging, that should use the same artifact that was tested in development). This reduces the risk of deploying untested or modified artifacts.&lt;/p&gt;
&lt;p&gt;Even when using a Dockerfile it&apos;s possible to generate two different artifacts. There are potential causes for that, such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Environment differences: Development applications might have additional debugging tools or dependencies that are not present in production.&lt;/li&gt;
&lt;li&gt;External resources: If the build process relies on external resources that can change over time this can lead to non-deterministic builds.&lt;/li&gt;
&lt;li&gt;Dependency version: When not specifying versions when installing dependencies (e.g. &lt;code class=&quot;language-text&quot;&gt;apt install&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;npm install&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;pip install&lt;/code&gt;) the latest version will be used, which can be different from development and production depending on when the build ran.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Even if you are not satisfied with this answer, there is a cost impact. We are multiplying our costs based on the number of environments. We are storing the &quot;same&quot; build over different environments, instead of having a centralized place to store the artifacts.&lt;/p&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;There is not an easy way to revert configuration/infra and artifact.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;It&apos;s a common requirement for deployment systems to be able to rollback in case of issues with the new release. The way to do it on the presented scenario is to revert the infra change (revert PR on the infrastructure repository) and revert the application change (revert PR on the application repository). We would still wait for the CI/CD to run to generate new assets. Oh, and we would have to wait for deployment in development, staging, and production (assuming only 3 environments).&lt;/p&gt;
&lt;ol start=&quot;4&quot;&gt;
&lt;li&gt;Every time a new deployment is done, manifests get outdated (drift is shown)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Since the application repository has a CD that forces the update of a new Lambda, this will generate a drift in the infra repository, because the infra repository state applied in the past a different manifest (even if the Docker image is the same as the hash of the image is different - it&apos;s a different artifact!).&lt;/p&gt;
&lt;h2&gt;Looking for solutions&lt;/h2&gt;
&lt;p&gt;We can improve by a lot the deployment reliability/consistency, let&apos;s check some important points to consider.&lt;/p&gt;
&lt;h3&gt;Application versioning&lt;/h3&gt;
&lt;p&gt;If you are using the same Docker tag for your deployments you are not versioning your application (e.g. using &lt;code class=&quot;language-text&quot;&gt;dev&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;latest&lt;/code&gt; for the development environment). This is a big problem. If you are using the same image tag you don&apos;t know what version of your application is running and don&apos;t have an easy way to run a previous version.&lt;/p&gt;
&lt;p&gt;There are two widely used options to version your artifacts: &lt;a href=&quot;https://semver.org/&quot;&gt;Semantic Versioning&lt;/a&gt; (SemVer) and Git Hash.&lt;/p&gt;
&lt;p&gt;SemVer uses a part number system &lt;code class=&quot;language-text&quot;&gt;MAJOR.MINOR.PATCH&lt;/code&gt; (e.g. &lt;code class=&quot;language-text&quot;&gt;1.5.4&lt;/code&gt;). You are probably very familiar with SemVer because it&apos;s widely used in the IT world. It&apos;s readable (it&apos;s just tree numbers!), easy to communicate (&quot;We just deployed version X.Y.Z&quot;), and predictable (if it&apos;s a major change you can expect breaking changes). On the other hand, the initial overhead to implement can be challenging and you don&apos;t know what a given version represents in code (unless you use Git tags).&lt;/p&gt;
&lt;p&gt;Git hash is even more familiar to you because every developer knows (at least that&apos;s what I expect...) Git. A SHA-1 hash that is unique and identifies a commit in a Git repository. It&apos;s commonly used because it maps to a specific point in the repository&apos;s history (just do &lt;code class=&quot;language-text&quot;&gt;git checkout &amp;lt;GIT_HASH&gt;&lt;/code&gt; and you can see it!), making it easier to understand exactly what code is being used, and it&apos;s easy to implement in CI/CD pipelines since you don&apos;t need to worry if the change is a breaking change, minor change or a patch/hotfix to name your version, it&apos;s simply the git hash. On the other hand, the readability is terrible (&quot;Hey, I&apos;m deploying version 7b2d773&quot;) and it&apos;s meaningless (it&apos;s a hash and that&apos;s it - you can&apos;t get good information from it).&lt;/p&gt;
&lt;p&gt;I&apos;ve worked with both approaches and my personal choice is SemVer, although the initial setup is harder, I think it pays off in the long run, but of course, this depends on several things, such as the deadline, personal preference of the team, maturity of the team (not everyone knows if they should increment a major, a minor or a patch).&lt;/p&gt;
&lt;h3&gt;IaaC as the source of truth&lt;/h3&gt;
&lt;p&gt;Drifts are happening because we are not treating the infrastructure repository as the source of truth. We shouldn&apos;t deploy a new application version from the application repository. From the artifacts point of view, the application repository should only build new artifacts.&lt;/p&gt;
&lt;p&gt;The ultimate goal is to have the infrastructure repository in a 1:1 relationship with what we have deployed. In other words, the infrastructure repository should be the source of truth when it comes to what we have deployed in the environments. This is completely achievable but hard, especially if we are talking about ephemeral deployments, but this is a subject for another time.&lt;/p&gt;
&lt;p&gt;The artifact version should be managed by the application repository! Well, it can be done manually, but we - as DevOps - hate manual things, and automation is the way to go. The CD of the application repository should open a Pull Request to modify the version of the application in the manifests. E.g. if you are using Terraform it can simply be a change on the variable in the &lt;code class=&quot;language-text&quot;&gt;var.tfvars&lt;/code&gt; file that corresponds to the version of the application Lambda. To be honest, the CD can directly push to the main/master branch of the infrastructure repository but this can be very risky and seen as not a good practice by some DevOps Engineers.&lt;/p&gt;
&lt;p&gt;If the change does not require infrastructure change (IAM permission, new environment variable, changes on the memory/cpu), the developers can simply merge the PR. Otherwise, changes can be implemented on this pull request to deploy infra and code changes at the same time. Notice that team coordination is ALWAYS important. If a new version needs to be deployed and this new version requires infrastructure change this needs to be aware by all the team (dev + ops).&lt;/p&gt;
&lt;h3&gt;Monorepo x Multi-Repo&lt;/h3&gt;
&lt;p&gt;I mentioned that the client used a multi-repo approach: one for the infrastructure and another for the applications (a single repository per application would also have the same impact). A possible solution is to use a monorepo approach: infrastructure and application code on the same git repository.&lt;/p&gt;
&lt;p&gt;A single pull request that changes infra and application code would do the trick. Atomic changes are a big pro of using a monorepo strategy.&lt;/p&gt;
&lt;p&gt;To be honest, I&apos;m not a big fan of having applications and infrastructure together on the same repository. I rather have a clear separation using different repositories. This way you can have an easier way to controll access to codebase, an independent development cycle, and higher performance (faster cloning, building, testing).&lt;/p&gt;
&lt;p&gt;If you go for a monorepo approach you will need more collaboration from your team, i.e., if the application team wants to deploy a new code that uses a new environment variable, it needs to collaborate with the infrastructure team to add this environment variable simultaneously.&lt;/p&gt;
&lt;h3&gt;Centralized assets&lt;/h3&gt;
&lt;p&gt;As we commented, deploying the &quot;same&quot; assets in different accounts it&apos;s not a good practice. Ideally, you should have a centralized place to put your assets (scripts, images). Many companies use the production environment for that and I don&apos;t like that.&lt;/p&gt;
&lt;p&gt;The best practice is to have a specific AWS account only for the assets. This account can also be used to host all CI/CD (if you decide to use CodePipeline, CodeBuild, or CodeDeploy).&lt;/p&gt;
&lt;p&gt;All the other environments (e.g. &lt;code class=&quot;language-text&quot;&gt;dev&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;staging&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;prod&lt;/code&gt;) should be able (cross-account permissions) to pull assets from this account!&lt;/p&gt;
&lt;h3&gt;Hotfix workflow&lt;/h3&gt;
&lt;p&gt;Imagine the following scenario: the new application version was tested in dev, staging, and production. It&apos;s running fine for some time but suddenly you start to see several errors related to your application and you NEED to rollback the version in production.&lt;/p&gt;
&lt;p&gt;A hotfix workflow is a workflow that runs directly in production to fix things as fast as possible. You don&apos;t want to run tests, build (remember that the old asset already exists!), and other things. You just want to fix the problem as fast as possible. You might see some DevOps that allows you to add new code (instead of rolling back the version) in the hotfix workflow, this is also possible, but I think rolling back is safer.&lt;/p&gt;
&lt;h2&gt;Hands-on&lt;/h2&gt;
&lt;p&gt;I&apos;ll propose the following solution:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Multi-repo approach&lt;/li&gt;
&lt;li&gt;Application repository builds and push assets to assets account and creates a pull request (remember to enable that in the GitHub Actions settings of the repository) to the infrastructure repository to update image being used&lt;/li&gt;
&lt;li&gt;Git hash to version images&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I wouldn&apos;t write this and not demo what I&apos;m saying! To demo this I chose the following stack:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;AWS&lt;/strong&gt;: For the Cloud provider.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ECR&lt;/strong&gt;: For the container registry.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Python&lt;/strong&gt;: For the Application (Lambda) code. It&apos;s a dummy application, don&apos;t worry!&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Terraform&lt;/strong&gt;: For the infrastructure deployment.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;GitHub Actions&lt;/strong&gt;: For the CI/CD.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Some things were created beforehand because they were used in another personal projects or because manual creation is the way to go:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;IAM Identity Provider&lt;/strong&gt;: GitHub&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;IAM Role&lt;/strong&gt;: Role that will be assumed by GitHub CI/CD using OIDC&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Github Actions Environments&lt;/strong&gt;: 3 environments that require manual approval (&lt;code class=&quot;language-text&quot;&gt;assets&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;dev&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;prod&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;GIthub Secrets&lt;/strong&gt;: Added &lt;code class=&quot;language-text&quot;&gt;PAT_GITHUB_TOKEN&lt;/code&gt; to the application repository (needed to create pull requests on the infrastructure repository)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;S3 Buckets&lt;/strong&gt;: To store Terraform state&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Application&lt;/h3&gt;
&lt;p&gt;Let&apos;s check the application code:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;python&quot;&gt;&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; os

&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; cowsay


&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;lambda_handler&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; context&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-interpolation&quot;&gt;&lt;span class=&quot;token string&quot;&gt;f&quot;event ==&gt; $&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    cowsay&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;cow&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;os&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;environ&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;TO_SAY&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;It&apos;s a simple application that uses &lt;code class=&quot;language-text&quot;&gt;cowsay&lt;/code&gt;! The code will run as a dockerized lambda that has the following Dockerfile:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;dockerfile&quot;&gt;&lt;pre class=&quot;language-dockerfile&quot;&gt;&lt;code class=&quot;language-dockerfile&quot;&gt;&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;FROM&lt;/span&gt; public.ecr.aws/lambda/python:3.11&lt;/span&gt;
&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;COPY&lt;/span&gt; requirements.txt  .&lt;/span&gt;
&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;RUN&lt;/span&gt; pip3 install -r requirements.txt --target &lt;span class=&quot;token string&quot;&gt;&quot;${LAMBDA_TASK_ROOT}&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;COPY&lt;/span&gt; .  .&lt;/span&gt;
&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;CMD&lt;/span&gt; [ &lt;span class=&quot;token string&quot;&gt;&quot;main.lambda_handler&quot;&lt;/span&gt; ]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The CI/CD contains two jobs (&lt;code class=&quot;language-text&quot;&gt;build-and-push&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;infra-pr&lt;/code&gt;):&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Application CI/CD

&lt;span class=&quot;token key atrule&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;branches&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; main
  &lt;span class=&quot;token key atrule&quot;&gt;pull_request&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;

&lt;span class=&quot;token key atrule&quot;&gt;permissions&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;id-token&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; write
  &lt;span class=&quot;token key atrule&quot;&gt;contents&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; write
  &lt;span class=&quot;token key atrule&quot;&gt;pull-requests&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; write

&lt;span class=&quot;token key atrule&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;AWS_ACCOUNT_ID&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;730335516527&quot;&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;AWS_IAM_ROLE_OIDC_NAME&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;GitHubActions&quot;&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;AWS_REGION&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;us-east-1&quot;&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;ECR_REPO_NAME&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;consistent-deployments&quot;&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;INFRA_REPOSITORY&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; felipelaptrin/consistent&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;deployments&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;infra&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;blog

&lt;span class=&quot;token key atrule&quot;&gt;jobs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;build-and-push&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;runs-on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ubuntu&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;latest
    &lt;span class=&quot;token key atrule&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Checkout code
        &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; actions/checkout@v4

      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Build Docker image
        &lt;span class=&quot;token key atrule&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;ECR_REPOSITORY&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; env.ECR_REPO_NAME &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;IMAGE_TAG&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; github.sha &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; docker build &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;t $ECR_REPOSITORY&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;$IMAGE_TAG .

      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Configure AWS Credentials
        &lt;span class=&quot;token key atrule&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; github.ref == &apos;refs/heads/main&apos;
        &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; aws&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;actions/configure&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;aws&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;credentials@v3
        &lt;span class=&quot;token key atrule&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;role-to-assume&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; arn&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;aws&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;iam&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;$&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; env.AWS_ACCOUNT_ID &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;role/$&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; env.AWS_IAM_ROLE_OIDC_NAME &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;aws-region&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; env.AWS_REGION &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;role-session-name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; GitHubActions

      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Push image to Amazon ECR
        &lt;span class=&quot;token key atrule&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; github.ref == &apos;refs/heads/main&apos;
        &lt;span class=&quot;token key atrule&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;ECR_REGISTRY&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; env.AWS_ACCOUNT_ID &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;.dkr.ecr.$&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; env.AWS_REGION &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;.amazonaws.com
          &lt;span class=&quot;token key atrule&quot;&gt;ECR_REPOSITORY&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; env.ECR_REPO_NAME &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;IMAGE_TAG&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; github.sha &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token scalar string&quot;&gt;
          aws ecr get-login-password --region ${{ env.AWS_REGION }} | docker login --username AWS --password-stdin ${{ env.AWS_ACCOUNT_ID }}.dkr.ecr.${{ env.AWS_REGION }}.amazonaws.com
          docker tag $ECR_REPOSITORY:$IMAGE_TAG $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
          docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG&lt;/span&gt;

  &lt;span class=&quot;token key atrule&quot;&gt;infra-pr&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; github.ref == &apos;refs/heads/main&apos;
    &lt;span class=&quot;token key atrule&quot;&gt;runs-on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ubuntu&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;latest
    &lt;span class=&quot;token key atrule&quot;&gt;needs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;build&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;and&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;push&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Checkout the infrastructure repository
        &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; actions/checkout@v4
        &lt;span class=&quot;token key atrule&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;repository&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; env.INFRA_REPOSITORY &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;token&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; secrets.PAT_GITHUB_TOKEN &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Set up Git
        &lt;span class=&quot;token key atrule&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token scalar string&quot;&gt;
          git config --global user.name &apos;github-actions[bot]&apos;
          git config --global user.email &apos;github-actions[bot]@users.noreply.github.com&apos;&lt;/span&gt;

      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Update app_version (tfvars file)
        &lt;span class=&quot;token key atrule&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;VERSION&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; github.sha &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token scalar string&quot;&gt;
          sed -i -E &apos;s/(app_version\s*=\s*&quot;).*&quot;/\1&apos;&quot;${VERSION}&quot;&apos;&quot;/&apos; dev/dev.tfvars
          sed -i -E &apos;s/(app_version\s*=\s*&quot;).*&quot;/\1&apos;&quot;${VERSION}&quot;&apos;&quot;/&apos; prod/prod.tfvars&lt;/span&gt;

      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Create Pull Request
        &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; peter&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;evans/create&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;pull&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;request@v4
        &lt;span class=&quot;token key atrule&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;token key atrule&quot;&gt;token&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; secrets.PAT_GITHUB_TOKEN &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token key atrule&quot;&gt;commit-message&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Update application version to $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; github.sha &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token key atrule&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Update application version to $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; github.sha &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token key atrule&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; This was created by Application CI/CD.
            &lt;span class=&quot;token key atrule&quot;&gt;base&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; main
            &lt;span class=&quot;token key atrule&quot;&gt;branch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; feat/$&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; github.sha &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;Infrastructure&lt;/h3&gt;
&lt;p&gt;The infrastructure contains two modules: ECR and Lambda. The ECR module contains the following resources:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;hcl&quot;&gt;&lt;pre class=&quot;language-hcl&quot;&gt;&lt;code class=&quot;language-hcl&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# ECR&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;locals&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;account_ids&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;dev&lt;/span&gt;    &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;937168356724&quot;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;prod&lt;/span&gt;   &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;654654203090&quot;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;assets&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;730335516527&quot;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;base_name&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;consistent-deployments&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;resource &lt;span class=&quot;token type variable&quot;&gt;&quot;aws_ecr_repository&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;this&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;name&lt;/span&gt;                 &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; local.base_name
  &lt;span class=&quot;token property&quot;&gt;image_tag_mutability&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;IMMUTABLE&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;resource &lt;span class=&quot;token type variable&quot;&gt;&quot;aws_ecr_repository_policy&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;this&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;repository&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; aws_ecr_repository.this.name
  &lt;span class=&quot;token property&quot;&gt;policy&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; jsonencode(&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;Version&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;2012-10-17&quot;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;Statement&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;Effect&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Allow&quot;&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;Principal&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;token property&quot;&gt;AWS&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; values(local.account_ids)
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;Action&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
          &lt;span class=&quot;token string&quot;&gt;&quot;ecr:BatchCheckLayerAvailability&quot;&lt;/span&gt;,
          &lt;span class=&quot;token string&quot;&gt;&quot;ecr:BatchGetImage&quot;&lt;/span&gt;,
          &lt;span class=&quot;token string&quot;&gt;&quot;ecr:GetDownloadUrlForLayer&quot;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;,
      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;Effect&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Allow&quot;&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;Principal&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;token property&quot;&gt;Service&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;lambda.amazonaws.com&quot;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;Action&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
          &lt;span class=&quot;token string&quot;&gt;&quot;ecr:BatchCheckLayerAvailability&quot;&lt;/span&gt;,
          &lt;span class=&quot;token string&quot;&gt;&quot;ecr:BatchGetImage&quot;&lt;/span&gt;,
          &lt;span class=&quot;token string&quot;&gt;&quot;ecr:GetDownloadUrlForLayer&quot;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;,
        &lt;span class=&quot;token property&quot;&gt;Condition&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;token property&quot;&gt;StringLike&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;&quot;aws:sourceArn&quot;&lt;/span&gt;: &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; for account in values(local.account_ids) : &lt;span class=&quot;token string&quot;&gt;&quot;arn:aws:lambda:&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token type variable&quot;&gt;aws_region&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;:&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;account&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;:function:*&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
          &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;)
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The Lambda module contains the following resources:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;hcl&quot;&gt;&lt;pre class=&quot;language-hcl&quot;&gt;&lt;code class=&quot;language-hcl&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# LAMBDA&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;locals&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;account_ids&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;dev&lt;/span&gt;    &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;937168356724&quot;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;prod&lt;/span&gt;   &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;654654203090&quot;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;assets&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;730335516527&quot;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;base_name&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;consistent-deployments&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;data &lt;span class=&quot;token type variable&quot;&gt;&quot;aws_iam_policy_document&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;this&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;count&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; var.deploy ? &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; : &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;statement&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;actions&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;sts:AssumeRole&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;principals&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;type&lt;/span&gt;        &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Service&quot;&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;identifiers&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;lambda.amazonaws.com&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;resource &lt;span class=&quot;token type variable&quot;&gt;&quot;aws_iam_role&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;this&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;count&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; var.deploy ? &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; : &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;

  &lt;span class=&quot;token property&quot;&gt;name&lt;/span&gt;               &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; local.base_name
  &lt;span class=&quot;token property&quot;&gt;assume_role_policy&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; data.aws_iam_policy_document.this&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;.json
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;resource &lt;span class=&quot;token type variable&quot;&gt;&quot;aws_iam_role_policy&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;this&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;count&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; var.deploy ? &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; : &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;

  &lt;span class=&quot;token property&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;lambda-policy&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;role&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; aws_iam_role.this&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;.id
  &lt;span class=&quot;token property&quot;&gt;policy&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; jsonencode(&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;Version&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;2012-10-17&quot;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;Statement&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;Action&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
          &lt;span class=&quot;token string&quot;&gt;&quot;logs:CreateLogGroup&quot;&lt;/span&gt;,
          &lt;span class=&quot;token string&quot;&gt;&quot;logs:CreateLogStream&quot;&lt;/span&gt;,
          &lt;span class=&quot;token string&quot;&gt;&quot;logs:PutLogEvents&quot;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;Effect&lt;/span&gt;   &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Allow&quot;&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;Resource&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;*&quot;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;,
      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;Action&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
          &lt;span class=&quot;token string&quot;&gt;&quot;ecr:GetDownloadUrlForLayer&quot;&lt;/span&gt;,
          &lt;span class=&quot;token string&quot;&gt;&quot;ecr:BatchGetImage&quot;&lt;/span&gt;,
          &lt;span class=&quot;token string&quot;&gt;&quot;ecr:BatchCheckLayerAvailability&quot;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;Effect&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Allow&quot;&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;Resource&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
          &lt;span class=&quot;token string&quot;&gt;&quot;arn:aws:ecr:&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token type variable&quot;&gt;aws_region&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;:&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;local&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token type variable&quot;&gt;account_ids&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;assets&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;:repository/&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;local&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token type variable&quot;&gt;base_name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;)
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;resource &lt;span class=&quot;token type variable&quot;&gt;&quot;aws_lambda_function&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;this&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;count&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; var.deploy ? &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; : &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;

  &lt;span class=&quot;token property&quot;&gt;function_name&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token type variable&quot;&gt;environment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;-&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;local&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token type variable&quot;&gt;base_name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;role&lt;/span&gt;          &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; aws_iam_role.this&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;.arn
  &lt;span class=&quot;token property&quot;&gt;image_uri&lt;/span&gt;     &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token type variable&quot;&gt;ecr_repo_url&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;:&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token type variable&quot;&gt;app_version&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;package_type&lt;/span&gt;  &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Image&quot;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;environment&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;variables&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;TO_SAY&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Hello from Lambda!&quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We have two GitHub Actions workflows. The first one is for the normal deployment of the infrastructure that contains two jobs per environment: one for generating the plan (tf plan) of the deployment and another one for deploying. The second is the hotfix workflow.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Infrastructure CI/CD

&lt;span class=&quot;token key atrule&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;branches&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; main
  &lt;span class=&quot;token key atrule&quot;&gt;pull_request&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;

&lt;span class=&quot;token key atrule&quot;&gt;permissions&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;id-token&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; write
  &lt;span class=&quot;token key atrule&quot;&gt;contents&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; read

&lt;span class=&quot;token key atrule&quot;&gt;jobs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;plan-assets&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Plan assets infra deployment
    &lt;span class=&quot;token key atrule&quot;&gt;runs-on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ubuntu&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;latest
    &lt;span class=&quot;token key atrule&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Checkout code
        &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; actions/checkout@v4

      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Plan assets deployment
        &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ./.github/actions/plan&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;infra
        &lt;span class=&quot;token key atrule&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;environment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; assets
          &lt;span class=&quot;token key atrule&quot;&gt;working_directory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; assets
          &lt;span class=&quot;token key atrule&quot;&gt;aws_account_id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;730335516527&lt;/span&gt;

  &lt;span class=&quot;token key atrule&quot;&gt;deploy-assets&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; github.ref == &apos;refs/heads/main&apos;
    &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Deploy assets infra
    &lt;span class=&quot;token key atrule&quot;&gt;needs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;plan&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;assets&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;runs-on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ubuntu&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;latest
    &lt;span class=&quot;token key atrule&quot;&gt;environment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; assets
    &lt;span class=&quot;token key atrule&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Checkout code
        &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; actions/checkout@v4

      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Deploy assets deployment
        &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ./.github/actions/deploy&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;infra
        &lt;span class=&quot;token key atrule&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;environment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; assets
          &lt;span class=&quot;token key atrule&quot;&gt;working_directory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; assets
          &lt;span class=&quot;token key atrule&quot;&gt;aws_account_id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;730335516527&lt;/span&gt;

  &lt;span class=&quot;token key atrule&quot;&gt;plan-dev&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Plan dev infra deployment
    &lt;span class=&quot;token key atrule&quot;&gt;runs-on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ubuntu&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;latest
    &lt;span class=&quot;token key atrule&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Checkout code
        &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; actions/checkout@v4

      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Plan dev deployment
        &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ./.github/actions/plan&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;infra
        &lt;span class=&quot;token key atrule&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;environment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; dev
          &lt;span class=&quot;token key atrule&quot;&gt;working_directory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; dev
          &lt;span class=&quot;token key atrule&quot;&gt;aws_account_id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;937168356724&lt;/span&gt;

  &lt;span class=&quot;token key atrule&quot;&gt;deploy-dev&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; github.ref == &apos;refs/heads/main&apos;
    &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Deploy dev infra
    &lt;span class=&quot;token key atrule&quot;&gt;needs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;plan&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;dev&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; deploy&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;assets&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;runs-on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ubuntu&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;latest
    &lt;span class=&quot;token key atrule&quot;&gt;environment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; dev
    &lt;span class=&quot;token key atrule&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Checkout code
        &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; actions/checkout@v4

      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Plan dev deployment
        &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ./.github/actions/deploy&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;infra
        &lt;span class=&quot;token key atrule&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;environment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; dev
          &lt;span class=&quot;token key atrule&quot;&gt;working_directory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; dev
          &lt;span class=&quot;token key atrule&quot;&gt;aws_account_id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;937168356724&lt;/span&gt;

  &lt;span class=&quot;token key atrule&quot;&gt;plan-prod&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Plan prod infra deployment
    &lt;span class=&quot;token key atrule&quot;&gt;runs-on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ubuntu&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;latest
    &lt;span class=&quot;token key atrule&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Checkout code
        &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; actions/checkout@v4

      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Plan prod deployment
        &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ./.github/actions/plan&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;infra
        &lt;span class=&quot;token key atrule&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;environment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; prod
          &lt;span class=&quot;token key atrule&quot;&gt;working_directory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; prod
          &lt;span class=&quot;token key atrule&quot;&gt;aws_account_id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;654654203090&lt;/span&gt;

  &lt;span class=&quot;token key atrule&quot;&gt;deploy-prod&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; github.ref == &apos;refs/heads/main&apos;
    &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Deploy prod infra
    &lt;span class=&quot;token key atrule&quot;&gt;needs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;plan&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;prod&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; deploy&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;dev&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;runs-on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ubuntu&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;latest
    &lt;span class=&quot;token key atrule&quot;&gt;environment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; prod
    &lt;span class=&quot;token key atrule&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Checkout code
        &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; actions/checkout@v4

      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Plan prod deployment
        &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ./.github/actions/deploy&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;infra
        &lt;span class=&quot;token key atrule&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;environment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; prod
          &lt;span class=&quot;token key atrule&quot;&gt;working_directory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; prod
          &lt;span class=&quot;token key atrule&quot;&gt;aws_account_id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;654654203090&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;These jobs are very repetitive, so to make it easier to reuse and avoid repetition the following actions were created:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; plan&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;environment
&lt;span class=&quot;token key atrule&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Plan Opentofu
&lt;span class=&quot;token key atrule&quot;&gt;inputs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;environment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Environment name &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; accepts `dev` and `prod`
    &lt;span class=&quot;token key atrule&quot;&gt;required&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean important&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;working_directory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Working directory
    &lt;span class=&quot;token key atrule&quot;&gt;required&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean important&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;aws_account_id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; AWS Account ID to run
    &lt;span class=&quot;token key atrule&quot;&gt;required&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean important&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;aws_iam_role_oidc_name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Name of the IAM Role that will be assumed via OIDC
    &lt;span class=&quot;token key atrule&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;GitHubActions&quot;&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;aws_region&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; AWS region
    &lt;span class=&quot;token key atrule&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;us-east-1&quot;&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;opentofu_version&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; OpenTofu version to use
    &lt;span class=&quot;token key atrule&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1.7.2

&lt;span class=&quot;token key atrule&quot;&gt;runs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;using&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;composite&quot;&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Configure AWS Credentials
      &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; aws&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;actions/configure&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;aws&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;credentials@v3
      &lt;span class=&quot;token key atrule&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;role-to-assume&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; arn&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;aws&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;iam&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;$&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; inputs.aws_account_id &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;role/$&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; inputs.aws_iam_role_oidc_name &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;aws-region&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; inputs.aws_region &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;role-session-name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; GitHubActions

    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; opentofu/setup&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;opentofu@v1
      &lt;span class=&quot;token key atrule&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;tofu_wrapper&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean important&quot;&gt;false&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;tofu_version&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; inputs.opentofu_version &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Opentofu init
      &lt;span class=&quot;token key atrule&quot;&gt;shell&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; bash
      &lt;span class=&quot;token key atrule&quot;&gt;working-directory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; inputs.working_directory &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; tofu init

    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Opentofu plan
      &lt;span class=&quot;token key atrule&quot;&gt;shell&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; bash
      &lt;span class=&quot;token key atrule&quot;&gt;working-directory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; inputs.working_directory &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token scalar string&quot;&gt;
 if find . -maxdepth 1 -name &quot;*.tfvars&quot; | grep -q .; then
 tofu plan -out tf-${{ inputs.environment }}.plan -lock=false -var-file=&quot;${{ inputs.environment }}.tfvars&quot;
 else
 tofu plan -out tf-${{ inputs.environment }}.plan -lock=false
 fi&lt;/span&gt;

    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Upload Opentofu plan file as artifact
      &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; actions/upload&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;artifact@v4
      &lt;span class=&quot;token key atrule&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; tfplan&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;$&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; inputs.environment &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; inputs.working_directory &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;/tf&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;$&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; inputs.environment &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;.plan&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;and&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; deploy&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;environment
&lt;span class=&quot;token key atrule&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Deploy Opentofu
&lt;span class=&quot;token key atrule&quot;&gt;inputs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;environment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Environment name &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; accepts `dev` and `prod`
    &lt;span class=&quot;token key atrule&quot;&gt;required&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean important&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;working_directory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Working directory
    &lt;span class=&quot;token key atrule&quot;&gt;required&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean important&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;aws_account_id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; AWS Account ID to run
    &lt;span class=&quot;token key atrule&quot;&gt;required&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean important&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;aws_iam_role_oidc_name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Name of the IAM Role that will be assumed via OIDC
    &lt;span class=&quot;token key atrule&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;GitHubActions&quot;&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;aws_region&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; AWS region
    &lt;span class=&quot;token key atrule&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;us-east-1&quot;&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;opentofu_version&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; OpenTofu version to use
    &lt;span class=&quot;token key atrule&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1.7.2

&lt;span class=&quot;token key atrule&quot;&gt;runs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;using&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;composite&quot;&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Configure AWS Credentials
      &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; aws&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;actions/configure&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;aws&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;credentials@v3
      &lt;span class=&quot;token key atrule&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;role-to-assume&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; arn&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;aws&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;iam&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;$&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; inputs.aws_account_id &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;role/$&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; inputs.aws_iam_role_oidc_name &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;aws-region&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; inputs.aws_region &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;role-session-name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; GitHubActions

    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; opentofu/setup&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;opentofu@v1
      &lt;span class=&quot;token key atrule&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;tofu_wrapper&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean important&quot;&gt;false&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;tofu_version&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; inputs.opentofu_version &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Opentofu init
      &lt;span class=&quot;token key atrule&quot;&gt;shell&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; bash
      &lt;span class=&quot;token key atrule&quot;&gt;working-directory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; inputs.working_directory &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; tofu init

    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Download Opentofu plan from previous action
      &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; actions/download&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;artifact@v4
      &lt;span class=&quot;token key atrule&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; tfplan&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;$&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; inputs.environment &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; inputs.working_directory &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; OpenTofu Apply
      &lt;span class=&quot;token key atrule&quot;&gt;shell&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; bash
      &lt;span class=&quot;token key atrule&quot;&gt;working-directory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; inputs.working_directory &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; tofu apply &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;input=false &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;lock&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;timeout=600s tf&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;$&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; inputs.environment &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;.plan&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The hotfix workflow is the following:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Hotflow CI/CD

&lt;span class=&quot;token key atrule&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;workflow_dispatch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;inputs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;rollback_version&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Version that should be used to rollback the application
        &lt;span class=&quot;token key atrule&quot;&gt;required&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean important&quot;&gt;true&lt;/span&gt;

&lt;span class=&quot;token key atrule&quot;&gt;permissions&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;id-token&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; write
  &lt;span class=&quot;token key atrule&quot;&gt;contents&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; write

&lt;span class=&quot;token key atrule&quot;&gt;jobs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;plan-prod&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Plan prod infra deployment
    &lt;span class=&quot;token key atrule&quot;&gt;runs-on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ubuntu&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;latest
    &lt;span class=&quot;token key atrule&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Checkout code
        &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; actions/checkout@v4

      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Rollback app_version
        &lt;span class=&quot;token key atrule&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;VERSION&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; github.event.inputs.rollback_version &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; sed &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;i &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;E &apos;s/(app_version\s&lt;span class=&quot;token important&quot;&gt;*=\s*&quot;).*&quot;/\1&apos;&quot;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;VERSION&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&quot;&apos;&quot;/&apos; prod/prod.tfvars

      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Plan prod deployment
        &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ./.github/actions/plan&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;infra
        &lt;span class=&quot;token key atrule&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;environment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; prod
          &lt;span class=&quot;token key atrule&quot;&gt;working_directory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; prod
          &lt;span class=&quot;token key atrule&quot;&gt;aws_account_id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;654654203090&lt;/span&gt;

  &lt;span class=&quot;token key atrule&quot;&gt;deploy-prod&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; github.ref == &apos;refs/heads/main&apos;
    &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Deploy prod infra
    &lt;span class=&quot;token key atrule&quot;&gt;needs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;plan&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;prod&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;runs-on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ubuntu&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;latest
    &lt;span class=&quot;token key atrule&quot;&gt;environment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; prod
    &lt;span class=&quot;token key atrule&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Checkout code
        &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; actions/checkout@v4

      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Deploy prod deployment
        &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ./.github/actions/deploy&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;infra
        &lt;span class=&quot;token key atrule&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;environment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; prod
          &lt;span class=&quot;token key atrule&quot;&gt;working_directory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; prod
          &lt;span class=&quot;token key atrule&quot;&gt;aws_account_id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;654654203090&lt;/span&gt;

      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Set up Git
        &lt;span class=&quot;token key atrule&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token scalar string&quot;&gt;
 git config --global user.name &apos;github-actions[bot]&apos;
 git config --global user.email &apos;github-actions[bot]@users.noreply.github.com&apos;&lt;/span&gt;

      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Rollback app_version
        &lt;span class=&quot;token key atrule&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;VERSION&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; github.event.inputs.rollback_version &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; sed &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;i &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;E &apos;s/(app_version\s&lt;span class=&quot;token important&quot;&gt;*=\s*&quot;).*&quot;/\1&apos;&quot;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;VERSION&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&quot;&apos;&quot;/&apos; prod/prod.tfvars

      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Push changes
        &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ad&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;m/github&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;push&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;action@v0.8.0
        &lt;span class=&quot;token key atrule&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;github_token&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; secrets.GITHUB_TOKEN &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;branch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; main&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4&gt;Assets account&lt;/h4&gt;
&lt;p&gt;The assets account will only use the ECR module:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;hcl&quot;&gt;&lt;pre class=&quot;language-hcl&quot;&gt;&lt;code class=&quot;language-hcl&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# main.tf&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;module&lt;span class=&quot;token type variable&quot;&gt; &quot;this&quot; &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;source&lt;/span&gt;      &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;../modules/ecr&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;aws_region&lt;/span&gt;  &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;us-east-1&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;environment&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;assets&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4&gt;Dev account&lt;/h4&gt;
&lt;p&gt;The dev account will use only the Lambda module.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;hcl&quot;&gt;&lt;pre class=&quot;language-hcl&quot;&gt;&lt;code class=&quot;language-hcl&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# main.tf&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;data &lt;span class=&quot;token type variable&quot;&gt;&quot;terraform_remote_state&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;assets&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;backend&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;s3&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;config&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;bucket&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;terraform-states-730335516527&quot;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;key&lt;/span&gt;    &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;consistent-deployments.tfstate&quot;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;region&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;us-east-1&quot;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;module&lt;span class=&quot;token type variable&quot;&gt; &quot;this&quot; &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;source&lt;/span&gt;       &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;../modules/lambda&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;aws_region&lt;/span&gt;   &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;us-east-1&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;environment&lt;/span&gt;  &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;dev&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;ecr_repo_url&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; data.terraform_remote_state.assets.outputs.ecr_repo_url
  &lt;span class=&quot;token property&quot;&gt;deploy&lt;/span&gt;       &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;app_version&lt;/span&gt;  &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; var.app_version
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Notice that we are reading from the remote state in the assets environment of the ECR repository that was created.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;hcl&quot;&gt;&lt;pre class=&quot;language-hcl&quot;&gt;&lt;code class=&quot;language-hcl&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# dev.tfvars&lt;/span&gt;

&lt;span class=&quot;token property&quot;&gt;app_version&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;fea18f2595ef250eee020aee633666f349a13973&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;Prod account&lt;/h3&gt;
&lt;p&gt;Prod account is exactly the same as the dev account.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;hcl&quot;&gt;&lt;pre class=&quot;language-hcl&quot;&gt;&lt;code class=&quot;language-hcl&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# main.tf&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;data &lt;span class=&quot;token type variable&quot;&gt;&quot;terraform_remote_state&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;assets&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;backend&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;s3&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;config&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;bucket&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;terraform-states-730335516527&quot;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;key&lt;/span&gt;    &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;consistent-deployments.tfstate&quot;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;region&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;us-east-1&quot;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;module&lt;span class=&quot;token type variable&quot;&gt; &quot;this&quot; &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;source&lt;/span&gt;       &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;../modules/lambda&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;aws_region&lt;/span&gt;   &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;us-east-1&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;environment&lt;/span&gt;  &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;prod&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;ecr_repo_url&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; data.terraform_remote_state.assets.outputs.ecr_repo_url
  &lt;span class=&quot;token property&quot;&gt;deploy&lt;/span&gt;       &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;app_version&lt;/span&gt;  &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; var.app_version
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And the &lt;code class=&quot;language-text&quot;&gt;prod.tfvars&lt;/code&gt; is shown below:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;hcl&quot;&gt;&lt;pre class=&quot;language-hcl&quot;&gt;&lt;code class=&quot;language-hcl&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# dev.tfvars&lt;/span&gt;

&lt;span class=&quot;token property&quot;&gt;app_version&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;fea18f2595ef250eee020aee633666f349a13973&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Notice that this value is managed by the PR created by the application CI/CD!&lt;/p&gt;
&lt;h2&gt;Final flow&lt;/h2&gt;
&lt;p&gt;So, the final flow, based on this new proposed solution is:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The application developer opens a PR on the application repository to add new features to the code&lt;/li&gt;
&lt;li&gt;Application PR is approved and merged&lt;/li&gt;
&lt;li&gt;The Docker image is created and pushed to ECR (tagged based on git commit hash) on the assets account&lt;/li&gt;
&lt;li&gt;A pull request is created in the infrastructure repository to update &lt;code class=&quot;language-text&quot;&gt;app_version&lt;/code&gt; in the tfvars file to use the new image&lt;/li&gt;
&lt;li&gt;If no infra changes are required, the application team can approve the update of the new Lambda version, otherwise, the infra team add changes to this PR&lt;/li&gt;
&lt;li&gt;Infra PR is approved and merged&lt;/li&gt;
&lt;li&gt;Deploy to assets account to create ECR repository (requires manual approval)&lt;/li&gt;
&lt;li&gt;Deploy to the dev account to create a new Lambda version (requires manual approval)&lt;/li&gt;
&lt;li&gt;Deploy to prod account to create new Lambda version (requires manual approval)&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Cya&lt;/h2&gt;
&lt;p&gt;That&apos;s all! Hope this brought some insights for you about consistent deployments. Hope to see you in the next on the next post!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[On-premise Kubernetes cluster management using Rancher and Terraform]]></title><description><![CDATA[If you want to go directly to the code/source code you can check my GitHub repository. Otherwise, let's start to comment about Rancher…]]></description><link>https://felipetrindade.com/on-premise-kubernetes/</link><guid isPermaLink="false">https://felipetrindade.com/on-premise-kubernetes/</guid><pubDate>Thu, 06 Jun 2024 01:40:32 GMT</pubDate><content:encoded>&lt;p&gt;If you want to go directly to the code/source code you can check my &lt;a href=&quot;https://github.com/felipelaptrin/on-premise-k8s&quot;&gt;GitHub repository&lt;/a&gt;. Otherwise, let&apos;s start to comment about Rancher, Vagrant, OpenTofu/Terraform!&lt;/p&gt;
&lt;h2&gt;Rancher&lt;/h2&gt;
&lt;p&gt;I usually like to understand a tool by asking questions such as: what is the problem that the tool is trying to solve? How the tool can solve this problem? So let&apos;s go directly to these questions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;The problem&lt;/strong&gt;: Managing Kubernetes at scale is complex and a real challenge. You need to manage things such as multi-cluster, standardization, security/access, and deploy applications consistently across clusters.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;What is Rancher?&lt;/strong&gt;: Rancher is an open-source platform to simplify the deployment, management and operation of Kubernetes clusters (cloud, on-prem or hybrid). It makes it easier to solve the mentioned problem because:&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;It acts as a centralized tool for managing all clusters&lt;/li&gt;
&lt;li&gt;You can use GUI and CLI&lt;/li&gt;
&lt;li&gt;Cluster provisioning is all done by Rancher, which is perfect for on-premise clusters&lt;/li&gt;
&lt;li&gt;Provides authentication and RBAC for access/permission control&lt;/li&gt;
&lt;li&gt;Provides built-in monitoring and logging compatible with Prometheus and Grafana&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;and many more!&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Basic Diagram&lt;/strong&gt;: I&apos;m a very visual person, so even simple diagrams help me visualize things.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 355px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/f66b95867c2e0cfb26ed8805769b091e/526ee/rancher-diagram.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 95.56962025316456%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAATCAYAAACQjC21AAAACXBIWXMAAAsTAAALEwEAmpwYAAACfUlEQVR42p2UXU/TUBjH9wH8AN77EfxGJl5piIkaNXplNHphgkQTRQMEbxQCAY0JEqIubgyLDOY2RtdOtnXrxjq6rm9bS9f+7TmlY2JI2Jo8ac95en75Py/niWCEx3U9eB4xgGEt7AlHdN9fIjIsjIBcNzg+s6bj5tsWbkzKiGUt6o8Mp4ySEI1G8WVlBXdnTcz/0PFssY2JZfX8wCA8j36Xy2UwzCbsroZo1sXVFxrGJnXwonM+YAgi6niehyAIdC2KIv7wLGJMBqksD6V1iFqtFgC9U3YaZlkWCoUCFEXp78uHh9gvliCUi77qEtptzfe3zlYYwlRVRalUotBQKbG+cg/wBlREev6OZFiQTRuSbuFA60K3g3zU63Ua2n/FOQakizbuTMt4MqegofQCYLLawoUHC4jc+oCLD5dx6elncE0Ne7sZ5DmOhlmtVimYvInF19dRKvK4/07D1KqGR+/beLOiBcB92cDliVUspgVcmWdwfSlJHT8ZBhzH00ST/JHqJhIJCs5ksqiLFTz/aOLxnIbbUy0sJcwA6Pj6G3qQH806gtKx+6E1Gg1IknRGkh1UJQPjC3XMxwwoqo5OxzypMsml651cqTDpzWaTtott2/2iEF8+z+HX5gZSWxvg2TRyuV2wLBsA3ePDgw08WGnDMOjPpOKDlQ4bzXOd/tlzN7bjOH5OuYGqe+j1PHxNdbFTGHE4EHilUkE8noBlKphe6/jDQcbYaxnff3eHHw6h2lQqhe3kFu7N6lhc1zG+1MbLT+rwwH/HFxDPObj2ym+bGQP7B85owBBKhgSbS+NbbBs7GQ5So+7fZ3U0IHnIDRLFGg5qFQozzQ5trb/hlKGcP1Q2MwAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Rancher basic diagram&quot;
        title=&quot;&quot;
        src=&quot;/static/f66b95867c2e0cfb26ed8805769b091e/526ee/rancher-diagram.png&quot;
        srcset=&quot;/static/f66b95867c2e0cfb26ed8805769b091e/c26ae/rancher-diagram.png 158w,
/static/f66b95867c2e0cfb26ed8805769b091e/6bdcf/rancher-diagram.png 315w,
/static/f66b95867c2e0cfb26ed8805769b091e/526ee/rancher-diagram.png 355w&quot;
        sizes=&quot;(max-width: 355px) 100vw, 355px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3&gt;Deployment and Installation&lt;/h3&gt;
&lt;p&gt;Rancher can be deployed in several cloud providers (AWS, GCP, Azure, Linode, DigitalOcean...) or bare metal. You can use your preferred IaC tool (such as Terraform, Pulumi or even Ansible) to configure and deploy the environment that will have Rancher installed. In this blog post, I will use a virtual machine, provisioned via Vagrant, that will deploy Rancher using Terraform. It doesn&apos;t matter the deployment method you are going to use, you just need to make sure that Rancher can reach (network) the Kubernetes cluster that it&apos;s going to provision/manage.&lt;/p&gt;
&lt;p&gt;Rancher can be &lt;a href=&quot;https://ranchermanager.docs.rancher.com/getting-started/installation-and-upgrade#overview-of-installation-options&quot;&gt;installed&lt;/a&gt; in several different ways. There is no right or wrong, each one has pros and cons when compared to other methods. Let&apos;s check a few ways of doing that:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Multi-cluster&lt;/strong&gt;: For the production environment you NEED high availability, so you should set up at least three (3) nodes when using RKE and two (2) nodes when using K3s (don&apos;t freak out about RKE or K3s, I will comment about them soon!). Using Helm as a Kubernetes Package Manager makes your life easier to install Rancher on multiple nodes on the management Kubernetes cluster that will have Rancher installed.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/664e8aaabc7199d24b7886d65545738c/ea7fb/rancher-multi-node.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 50%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAAAsTAAALEwEAmpwYAAACLElEQVR42oWSzWsTURTF87cI4k4FdVUX7opgN5KFunVREIq6ciGiUtBuI1gNFWo3VWlE0fhBW5SU2DaJA9EYk5i0mTQfNslkJsk0bTKT5Od7EwstVLybedx579xzzj0u/lO9ft/5xtcMlpQ8L3yv0Q2D4HKEN+8XabVaPPf5SSTTmGYT17+A+n+BZJXLm2h6k1gyz+ET5wgrMbwzLzk65Ma2bYbdV7h9f5J+r4ur17XZ3m5htTvs7OzQNE26liUR6XQ6GIJNuVyhUTewbIupmTlS6SzqRoGHU7MO4Cv/Im8/fBb327ji63VyFYuqeFAz6sgBG1qdkgCREnq9AdOvMZVQVMXvf0exUCAcUfg4vyCGlVGza+Q3cpRKJVxLK1Gu3fKwvC5+FDa5euMeC+JxvqxhC4YdwbZR18lvNgmE0xw5NUI8kcHjneXkmQv7bDKFOtenwDLHT7v5Hk9R0XQOHTvLauQbW4KdvCBtkJNrtRqWZXN34hE/fqZJCtk3xx9gi94+QF3XnY3p9YbTXAkpVGsGsi83uLucjPqbWEIlEAiQzWZRFIVgMEgul2M1FEFV1YHkRqPheCXNlyyk+YOeSbvdFj2draZBpmAy/yXD0PAlfmVUHk/PMXx+lG63y8jFMSafPJMhG8Rmb0T2nnerWBSSNU0koMXo9XGisSQrwpbLY3fE0npMeJ7infaJxwLwIICDMqkJwLaIViqZdM5atUoikXBskXILxaLj4R85Xcv2haSdFAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Rancher diagram Multi-cluster&quot;
        title=&quot;&quot;
        src=&quot;/static/664e8aaabc7199d24b7886d65545738c/f058b/rancher-multi-node.png&quot;
        srcset=&quot;/static/664e8aaabc7199d24b7886d65545738c/c26ae/rancher-multi-node.png 158w,
/static/664e8aaabc7199d24b7886d65545738c/6bdcf/rancher-multi-node.png 315w,
/static/664e8aaabc7199d24b7886d65545738c/f058b/rancher-multi-node.png 630w,
/static/664e8aaabc7199d24b7886d65545738c/ea7fb/rancher-multi-node.png 788w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Single-node&lt;/strong&gt;: As you can imagine, a single node running Rancher is good for testing, cost-saving and non-critical environments, not for production. For testing and demonstration purposes you can even install Rancher using Docker!&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/fac4a93d49b41658936bd01a24e25954/394f7/rancher-single-node.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 55.69620253164557%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAAsTAAALEwEAmpwYAAACe0lEQVR42n2Ty2sTURTG5y/ooktFRVcqblwJ4gNcCC4VF4J/gAtFQUEruhGEIooIUpS6kRatpVVqK61Ka1/Umk4yeUyax3SSZpJJMklnxmSS5jGPfN57S4tC8cKdOTP3zO9+57tnOPxneF6HXhGI6+BXSxj5+BlKVkW+UMTA+08oFjVklCzeDY9hY0NHtVoFxz7s/AvqdDps0qHrZRiWjXBCxb5j5zEwNA4lV0D3oTOY/L6AcFRC98HTmJ5dhm23toAEAdfzdiDb0Eqlgmh0FY1GA67r4cHjlxifnGPrtx8+w+wij3a7jZv3euHzi1tA87eFUNJAs9nApmWhZlUZwDRN1OubbDMxJiOdKSASDkKSJKi5HPz8CqRkEoqSQSDghyzL0DQNXKVax8SCgjv3e/FVSELIllHWikSRy5S4dgN8zMQPXsWlq9exvBJmJV+4fA1CKMZyHMdh0zAMcFRRNC5j71Hiz8gUWq6DQqFA1NVhEcWRSAQ2Kau8YeL42SvofzsKw6zg8ImLGBqdYkDbdtid5nO6rrOHwaExSHKGLLZRJNK3B02yqhUW+3gBCSnF4sUlH9ZS6zt5VCFlcfl8ntWeWU8zP1Q1z6TTFmg2WyyZF1VML8XR96qf+CUgFIrg6fM+iKIIIRjC6zeDkFMplEolcFQBBeZUFVliNoXRE6YeGoYJeS2JvN6GL5zFniPnMPjhC9KKiq4Dp/Bt5ieCkQS69p/EzNwveMQubreG/rt9VLJRrVYjJbnoefQCE1Pz7P2tnieYX/Kj2Wrhxl3SNgERrmNvASnAI33o7dKLVKmmlQi0jthqFOvEGlpFMCggnaaxTg4uxMqlf8ofl4kPv5meSXoAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Rancher diagram Single-Cluster&quot;
        title=&quot;&quot;
        src=&quot;/static/fac4a93d49b41658936bd01a24e25954/f058b/rancher-single-node.png&quot;
        srcset=&quot;/static/fac4a93d49b41658936bd01a24e25954/c26ae/rancher-single-node.png 158w,
/static/fac4a93d49b41658936bd01a24e25954/6bdcf/rancher-single-node.png 315w,
/static/fac4a93d49b41658936bd01a24e25954/f058b/rancher-single-node.png 630w,
/static/fac4a93d49b41658936bd01a24e25954/394f7/rancher-single-node.png 707w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Ok.. Now that we know that we can deploy in a multi-cluster or single-node way (in this demo I will use a single-node approach), where we are installing Rancher? And how we are going to use Helm to install Rancher if we don&apos;t even have a running Kubernetes cluster at first? To answer this let&apos;s comment about bootstrapping.&lt;/p&gt;
&lt;h3&gt;Bootstrapping&lt;/h3&gt;
&lt;p&gt;Bootstrap is the initial process of setting up a system/environment with basic components, dependencies, and configuration. To install Rancher you will need a Kubernetes cluster. I know... this is confusing, you will need a Kubernetes cluster to install a tool that creates/manages Kubernetes clusters. And this is part of the bootstrap. You will need to create manually/script/IaC a Kubernetes cluster to run Rancher. You can use several &lt;a href=&quot;https://www.suse.com/suse-rancher/support-matrix/all-supported-versions/rancher-v2-8-4/&quot;&gt;Kubernetes engines&lt;/a&gt; to host Rancher: RKE (also known as RKE1), RKE2, K3s, EKS, AKS and GKE. The last three engines are cloud-specific, so if you are going for on-premise you should focus on RKE1, RKE2 or K3s.&lt;/p&gt;
&lt;p&gt;One very important thing: It is NOT recommended to run workloads in this cluster. This cluster should be used entirely for Rancher and will serve as the control plane for managing the other clusters. This cluster is also known as the &quot;Rancher Manager Cluster&quot;.&lt;/p&gt;
&lt;h2&gt;Vagrant&lt;/h2&gt;
&lt;p&gt;Since I don&apos;t have physical server (or a home lab) I will create virtual machines using a hypervisor. Instead of manually creating these machines using the hypervisor interface/GUI I will use Vagrant to automate this task. Once again let&apos;s target these questions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;The problem&lt;/strong&gt;: Managing the creation, access and configuration of Virtual Machines is a tedious task that can be automated!&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;What is Vagrant?&lt;/strong&gt;: Vagrant is a tool that allows us to declaratively define a manifest (called Vagrantfile) with the configuration of the VMs that we would like to create. Notice that Vagrant is not a hypervisor, it sits between the hypervisor and the user.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As commented, we still need a hypervisor. There are several hypervisors in the market. For this tutorial I decided to use &lt;code class=&quot;language-text&quot;&gt;VMware Fusion Pro&lt;/code&gt;. I could have used any other hypervisor (e.g. Virtualbox is probably the most famous one) to deploy the VMs but I think VMware Fusion is a great option because it&apos;s one of the few free (for personal use) hypervisors that you can use with M-series Macbooks and x86 architectures.&lt;/p&gt;
&lt;h3&gt;Installation&lt;/h3&gt;
&lt;p&gt;Set up Vagrant with VMware Fusion Pro integration is fairly simple but a bit tricky because it requires you to install several things. The step-by-step guide is the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.hashicorp.com/vagrant/docs/installation&quot;&gt;Install Vagrant&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://support.broadcom.com/group/ecx/productdownloads?subfamily=VMware+Fusion&quot;&gt;Install VMWare Fusion Pro&lt;/a&gt; (it requires you to create a free account on &lt;a href=&quot;https://profile.broadcom.com/web/registration&quot;&gt;Broadcom&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.hashicorp.com/vagrant/docs/providers/vmware/vagrant-vmware-utility&quot;&gt;Install VMware Utility&lt;/a&gt; (this will provide VMware provider plugin - that we are installing next - various functionalities)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.hashicorp.com/vagrant/docs/providers/vmware/installation&quot;&gt;Install Vagrant VMware plugin&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Configuration&lt;/h3&gt;
&lt;p&gt;To configure the VMs I will use a &lt;code class=&quot;language-text&quot;&gt;Vagrantfile&lt;/code&gt; that will consume inputs from a YAML file.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ruby&quot;&gt;&lt;pre class=&quot;language-ruby&quot;&gt;&lt;code class=&quot;language-ruby&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Vagrantfile&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&apos;yaml&apos;&lt;/span&gt;&lt;/span&gt;
settings &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;YAML&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;load_file&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;join&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dirname&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;__FILE__&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&apos;vagrant.yaml&apos;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

Vagrant&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;configure&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;2&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;config&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;
  config&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;vm&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;box &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; settings&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&apos;box_name&apos;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;

  settings&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&apos;vm&apos;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;vm_config&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;
    config&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;vm&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;define vm_config&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&apos;name&apos;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;vm&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;
      vm&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;vm&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;hostname &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; vm_config&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&apos;name&apos;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
      vm&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;vm&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;network &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;private_network&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token symbol&quot;&gt;ip&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; vm_config&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&apos;ip&apos;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
      vm&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;vm&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;synced_folder &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/vagrant&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token symbol&quot;&gt;disabled&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;

      vm&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;vm&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;provider &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;vmware_fusion&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;vb&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;
        vb&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;memory &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; vm_config&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&apos;memory&apos;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
        vb&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;cpus &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; vm_config&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&apos;cpus&apos;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If you are not familiar with Vagrant please don&apos;t get scared. It&apos;s actually fairly easy to read this. For every input in the &lt;code class=&quot;language-text&quot;&gt;vagrant.yaml&lt;/code&gt; file we will create a VM in a private network with a given private IP and a given amount of CPU and memory. Easy, right? Now let&apos;s check the &lt;code class=&quot;language-text&quot;&gt;vagrant.yaml&lt;/code&gt; file:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;box_name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;bento/ubuntu-22.04&quot;&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;vm&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;rancher-manager&quot;&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;ip&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;192.168.110.10&quot;&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;memory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;4096&quot;&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;cpus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;1&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;node01&quot;&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;ip&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;192.168.110.11&quot;&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;memory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;4096&quot;&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;cpus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;1&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;node02&quot;&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;ip&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;192.168.110.12&quot;&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;memory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;2048&quot;&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;cpus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;1&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;node03&quot;&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;ip&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;192.168.110.13&quot;&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;memory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;1024&quot;&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;cpus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;1&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I&apos;m defining the OS of the VMs (you can check all the available OS in the &lt;a href=&quot;https://app.vagrantup.com/boxes/search&quot;&gt;Vagrant Box website&lt;/a&gt;) and the configuration of each VM. In our case, four VMs: one for the Rancher manager cluster and others for the Kubernetes cluster that Rancher will create (one VM will assume the role of the &lt;code class=&quot;language-text&quot;&gt;etcd&lt;/code&gt;, another for the &lt;code class=&quot;language-text&quot;&gt;controlplane&lt;/code&gt; and the last one for the &lt;code class=&quot;language-text&quot;&gt;worker&lt;/code&gt; role).&lt;/p&gt;
&lt;h3&gt;Deployment&lt;/h3&gt;
&lt;p&gt;Vagrant deployment is as easy as running &lt;code class=&quot;language-text&quot;&gt;vagrant up&lt;/code&gt;. That&apos;s it. That&apos;s all.&lt;/p&gt;
&lt;h2&gt;OpenTofu (Terraform)&lt;/h2&gt;
&lt;p&gt;Once again, here we go:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;The problem&lt;/strong&gt;: I could write an entire blog post commenting about infrastructure as code (IaaC) and its benefits. To be direct to the point I will say that manually creating infrastructure is terrible: not reproducible, hard to review, prone to errors, there is no history of actions performed...&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;What OpenTofu is?&lt;/strong&gt;: OpenTofu is the open-source version of Terraform (which is &lt;a href=&quot;https://www.hashicorp.com/blog/hashicorp-adopts-business-source-license&quot;&gt;no longer open source&lt;/a&gt; since 2023). It allows us to declaratively define manifests (in JSON or HCL - a domain-specific language) it has the concepts of &quot;blocks&quot; to create resources.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here we will use OpenTofu to install Rancher a configure Kubernetes nodes. Although Terraform was created to manage infrastructure it can be used to manage configuration in machines. Ansible is a more specific tool for this purpose but I think Terraform/OpenTofu can be perfectly used here.&lt;/p&gt;
&lt;h3&gt;Configuration&lt;/h3&gt;
&lt;p&gt;Let&apos;s start with the variables of the OpenTofu code:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;hcl&quot;&gt;&lt;pre class=&quot;language-hcl&quot;&gt;&lt;code class=&quot;language-hcl&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// variables.tf&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;variable&lt;span class=&quot;token type variable&quot;&gt; &quot;k3s_version&quot; &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;description&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;K3s version that will be used in the management cluster (Rancher). Check all available versions on GitHub releases page: https://github.com/k3s-io/k3s/releases&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;type&lt;/span&gt;        &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; string
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;variable&lt;span class=&quot;token type variable&quot;&gt; &quot;cert_manager_version&quot; &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;description&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Cert Manager version that will be deployed, as a Rancher dependency. Check all available versions on Github releases page: https://github.com/cert-manager/cert-manager/releases&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;type&lt;/span&gt;        &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; string
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;variable&lt;span class=&quot;token type variable&quot;&gt; &quot;rancher_version&quot; &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;description&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Rancher version that will be deployed, as a Rancher dependency. Check all available versions on Github releases page: https://github.com/rancher/rancher/releases&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;type&lt;/span&gt;        &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; string
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;variable&lt;span class=&quot;token type variable&quot;&gt; &quot;rancher_endpoint&quot; &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;description&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;URL that points to the Rancher server&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;type&lt;/span&gt;        &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; string
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;variable&lt;span class=&quot;token type variable&quot;&gt; &quot;management_cluster&quot; &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;description&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Defines the management cluster that will be created.&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; object(&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;host&lt;/span&gt;        &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; string
    &lt;span class=&quot;token property&quot;&gt;user&lt;/span&gt;        &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; string
    &lt;span class=&quot;token property&quot;&gt;password&lt;/span&gt;    &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; string
    &lt;span class=&quot;token property&quot;&gt;port&lt;/span&gt;        &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; number
    &lt;span class=&quot;token property&quot;&gt;private_key&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; string
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;)
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;variable&lt;span class=&quot;token type variable&quot;&gt; &quot;managed_cluster&quot; &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;description&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Defines all the clusters that will be created. Also defines all the nodes of the cluster.&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; map(
    object(&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;kubernetes_version&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; string
      &lt;span class=&quot;token property&quot;&gt;nodes&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; list(object(&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;host&lt;/span&gt;        &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; string
        &lt;span class=&quot;token property&quot;&gt;user&lt;/span&gt;        &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; string
        &lt;span class=&quot;token property&quot;&gt;password&lt;/span&gt;    &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; string
        &lt;span class=&quot;token property&quot;&gt;port&lt;/span&gt;        &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; number
        &lt;span class=&quot;token property&quot;&gt;private_key&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; string
        &lt;span class=&quot;token property&quot;&gt;role&lt;/span&gt;        &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; list(string) &lt;span class=&quot;token comment&quot;&gt;# Accept values: etcd, controlplane, worker&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;)) &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;))
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Just a few variables: versions of K3s (I decided to use K3s instead of RKE), Cert Manager (needed to install Rancher) and Rancher. Also the definition of the Rancher endpoint and the configuration of the node that will contain the Rancher installation and the nodes that Rancher will create the managed Kubernetes cluster.&lt;/p&gt;
&lt;p&gt;A completely valid configuration can be seen below:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;hcl&quot;&gt;&lt;pre class=&quot;language-hcl&quot;&gt;&lt;code class=&quot;language-hcl&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// vars.tfvars&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;k3s_version&lt;/span&gt;          &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;v1.28.10+k3s1&quot;&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;cert_manager_version&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;v1.14.5&quot;&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;rancher_version&lt;/span&gt;      &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;v2.8.4&quot;&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;rancher_endpoint&lt;/span&gt;     &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;192.168.110.10.sslip.io&quot;&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;management_cluster&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;host&lt;/span&gt;        &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;127.0.0.1&quot;&lt;/span&gt;,
  &lt;span class=&quot;token property&quot;&gt;user&lt;/span&gt;        &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;vagrant&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;password&lt;/span&gt;    &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;vagrant&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;port&lt;/span&gt;        &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2222&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;private_key&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/Users/felipe/Desktop/folders/personal/rancher/.vagrant/machines/rancher-manager/vmware_fusion/private_key&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;managed_cluster&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;development&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;kubernetes_version&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;v1.28.10+rke2r1&quot;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;nodes&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;host&lt;/span&gt;        &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;127.0.0.1&quot;&lt;/span&gt;,
      &lt;span class=&quot;token property&quot;&gt;user&lt;/span&gt;        &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;vagrant&quot;&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;password&lt;/span&gt;    &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;vagrant&quot;&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;port&lt;/span&gt;        &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2200&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;private_key&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/Users/felipe/Desktop/folders/personal/rancher/.vagrant/machines/node01/vmware_fusion/private_key&quot;&lt;/span&gt;,
      &lt;span class=&quot;token property&quot;&gt;role&lt;/span&gt;        &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;controlplane&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;,&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;host&lt;/span&gt;        &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;127.0.0.1&quot;&lt;/span&gt;,
      &lt;span class=&quot;token property&quot;&gt;user&lt;/span&gt;        &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;vagrant&quot;&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;password&lt;/span&gt;    &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;vagrant&quot;&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;port&lt;/span&gt;        &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2201&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;private_key&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/Users/felipe/Desktop/folders/personal/rancher/.vagrant/machines/node02/vmware_fusion/private_key&quot;&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;role&lt;/span&gt;        &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;etcd&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;,
      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;host&lt;/span&gt;        &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;127.0.0.1&quot;&lt;/span&gt;,
      &lt;span class=&quot;token property&quot;&gt;user&lt;/span&gt;        &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;vagrant&quot;&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;password&lt;/span&gt;    &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;vagrant&quot;&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;port&lt;/span&gt;        &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2202&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;private_key&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/Users/felipe/Desktop/folders/personal/rancher/.vagrant/machines/node03/vmware_fusion/private_key&quot;&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;role&lt;/span&gt;        &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;worker&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;PS: Make sure to edit &lt;code class=&quot;language-text&quot;&gt;managed_cluster.&amp;lt;cluster-name&gt;.nodes[]&lt;/code&gt; accordingly. You can run &lt;code class=&quot;language-text&quot;&gt;vagrant ssh-config&lt;/code&gt; (after the VMs are created) to get the &lt;code class=&quot;language-text&quot;&gt;host&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;user&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;password&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;port&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;private_key&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The following providers will be used:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;hcl&quot;&gt;&lt;pre class=&quot;language-hcl&quot;&gt;&lt;code class=&quot;language-hcl&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// versions.tf&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;terraform&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;required_version&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;~&gt; 1.7&quot;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;required_providers&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;rancher2&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;source&lt;/span&gt;  &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;rancher/rancher2&quot;&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;version&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;4.1.0&quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;provider&lt;span class=&quot;token type variable&quot;&gt; &quot;local&quot; &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;provider&lt;span class=&quot;token type variable&quot;&gt; &quot;rancher2&quot; &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;alias&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;bootstrap&quot;&lt;/span&gt;

  &lt;span class=&quot;token property&quot;&gt;api_url&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https://&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token type variable&quot;&gt;rancher_endpoint&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;insecure&lt;/span&gt;  &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;bootstrap&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;provider&lt;span class=&quot;token type variable&quot;&gt; &quot;rancher2&quot; &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;alias&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;admin&quot;&lt;/span&gt;

  &lt;span class=&quot;token property&quot;&gt;api_url&lt;/span&gt;   &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; rancher2_bootstrap.this.url
  &lt;span class=&quot;token property&quot;&gt;token_key&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; rancher2_bootstrap.this.token
  &lt;span class=&quot;token property&quot;&gt;insecure&lt;/span&gt;  &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Notice that we are using Rancher (we will use that to create a managed cluster). You might ask why two providers for Rancher. This is needed because one of the providers will deal with the bootstrapping of the Rancher (change the initial admin password, set Rancher endpoint, create and manage the rotation of the Rancher token) and the other we will use to manage Rancher itself (using the token created by the bootstrap provider).&lt;/p&gt;
&lt;p&gt;Now the most important part: the resources created by OpenTofu.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;hcl&quot;&gt;&lt;pre class=&quot;language-hcl&quot;&gt;&lt;code class=&quot;language-hcl&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// main.tf&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;locals&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;bootstrap_remote_folder_path&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/tmp/bootstrap&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;bootstrap_folder_path&lt;/span&gt;        &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token type variable&quot;&gt;module&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;/bootstrap&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;rancher_install_command&lt;/span&gt;      &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/bin/bash &lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;local&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token type variable&quot;&gt;bootstrap_remote_folder_path&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;/install-manager.sh &lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token type variable&quot;&gt;k3s_version&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token type variable&quot;&gt;cert_manager_version&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token type variable&quot;&gt;rancher_version&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;local&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token type variable&quot;&gt;bootstrap_remote_folder_path&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;nodes&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; flatten(&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
    for cluster_name, cluster_value in var.managed_cluster : &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
      for index, node_value in cluster_value.nodes :
      merge(&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;cluster&quot;&lt;/span&gt; : cluster_name &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;, node_value)
    &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;)
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;data &lt;span class=&quot;token type variable&quot;&gt;&quot;archive_file&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;check_if_bootstrap_needs_to_run&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;type&lt;/span&gt;        &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;zip&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;source_dir&lt;/span&gt;  &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; local.bootstrap_folder_path
  &lt;span class=&quot;token property&quot;&gt;output_path&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/tmp/k3s-bootstrap.zip&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;resource &lt;span class=&quot;token type variable&quot;&gt;&quot;null_resource&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;bootstrap_rancher&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;connection&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;type&lt;/span&gt;        &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;ssh&quot;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;host&lt;/span&gt;        &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; var.management_cluster&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;host&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;user&lt;/span&gt;        &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; var.management_cluster&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;user&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;password&lt;/span&gt;    &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; var.management_cluster&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;password&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;port&lt;/span&gt;        &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; var.management_cluster&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;port&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;private_key&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; file(var.management_cluster&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;private_key&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;)
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;provisioner&lt;span class=&quot;token type variable&quot;&gt; &quot;file&quot; &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;source&lt;/span&gt;      &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; local.bootstrap_folder_path
    &lt;span class=&quot;token property&quot;&gt;destination&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; local.bootstrap_remote_folder_path
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;# We can´t have a single &apos;remote-exec&apos; for running 2 commands because it will&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;# consider that this resource was successful if the last inline command run fine&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;provisioner&lt;span class=&quot;token type variable&quot;&gt; &quot;remote-exec&quot; &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;inline&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
      local.rancher_install_command,
    &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;provisioner&lt;span class=&quot;token type variable&quot;&gt; &quot;remote-exec&quot; &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;inline&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
      &lt;span class=&quot;token string&quot;&gt;&quot;rm -rf &lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;local&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token type variable&quot;&gt;bootstrap_remote_folder_path&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;,
    &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token property&quot;&gt;triggers&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;script_hash&lt;/span&gt;     &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; data.archive_file.check_if_bootstrap_needs_to_run.output_sha
    &lt;span class=&quot;token property&quot;&gt;install_command&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; md5(local.rancher_install_command)
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;resource &lt;span class=&quot;token type variable&quot;&gt;&quot;rancher2_bootstrap&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;this&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;depends_on&lt;/span&gt;       &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;null_resource.bootstrap_rancher&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;provider&lt;/span&gt;         &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; rancher2.bootstrap
  &lt;span class=&quot;token property&quot;&gt;initial_password&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;ChangedByTerraform&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;telemetry&lt;/span&gt;        &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;resource &lt;span class=&quot;token type variable&quot;&gt;&quot;rancher2_cluster_v2&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;this&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;for_each&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; var.managed_cluster

  &lt;span class=&quot;token property&quot;&gt;provider&lt;/span&gt;           &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; rancher2.admin
  &lt;span class=&quot;token property&quot;&gt;name&lt;/span&gt;               &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; each.key
  &lt;span class=&quot;token property&quot;&gt;kubernetes_version&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; each.value.kubernetes_version
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;resource &lt;span class=&quot;token type variable&quot;&gt;&quot;null_resource&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;bootstrap_node&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;depends_on&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;null_resource.bootstrap_rancher, rancher2_cluster_v2.this&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;for_each&lt;/span&gt;   &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; for k, v in local.nodes : &lt;span class=&quot;token property&quot;&gt;k&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&gt; v &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;connection&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;type&lt;/span&gt;        &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;ssh&quot;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;host&lt;/span&gt;        &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; each.value&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;host&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;user&lt;/span&gt;        &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; each.value.user
    &lt;span class=&quot;token property&quot;&gt;password&lt;/span&gt;    &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; each.value.password
    &lt;span class=&quot;token property&quot;&gt;port&lt;/span&gt;        &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; each.value.port
    &lt;span class=&quot;token property&quot;&gt;private_key&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; file(each.value.private_key)
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;provisioner&lt;span class=&quot;token type variable&quot;&gt; &quot;file&quot; &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;source&lt;/span&gt;      &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; local.bootstrap_folder_path
    &lt;span class=&quot;token property&quot;&gt;destination&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; local.bootstrap_remote_folder_path
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;provisioner&lt;span class=&quot;token type variable&quot;&gt; &quot;remote-exec&quot; &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;inline&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
      &lt;span class=&quot;token string&quot;&gt;&quot;/bin/bash &lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;local&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token type variable&quot;&gt;bootstrap_remote_folder_path&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;/install-node.sh&quot;&lt;/span&gt;,
      &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;rancher2_cluster_v2&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;this&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;each&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;cluster&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;cluster_registration_token&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;insecure_node_command&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot; &quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;for x in each&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;role &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;--%s&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; x&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;,
    &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;provisioner&lt;span class=&quot;token type variable&quot;&gt; &quot;remote-exec&quot; &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;inline&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
      &lt;span class=&quot;token string&quot;&gt;&quot;rm -rf &lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;local&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token type variable&quot;&gt;bootstrap_remote_folder_path&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;,
    &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token property&quot;&gt;triggers&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;always&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; timestamp()
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Notice that the code does the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Copy the &lt;code class=&quot;language-text&quot;&gt;install-manager.sh&lt;/code&gt; (we will get there soon!) script to the node that will have Rancher installed and run it&lt;/li&gt;
&lt;li&gt;Creates the managed cluster by Rancher&lt;/li&gt;
&lt;li&gt;Copy the &lt;code class=&quot;language-text&quot;&gt;install-node.sh&lt;/code&gt; (once again, we will get there!) script to all the managed nodes and run the command to add these nodes to the newly created cluster by Rancher (once Rancher detects the nodes it will install Kubernetes in these nodes and manage everything for you!)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now, let&apos;s comment about these two scripts:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# install-manager.sh&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;#!/bin/bash&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-euo&lt;/span&gt; pipefail

&lt;span class=&quot;token comment&quot;&gt;# Description: This bash script install K3s, Cert Manager and Rancher. It&apos;s supposed to&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;#              be executed in the node of the management cluster.&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Arguments: This bash script expects the follow arguments in order:&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# 1) K3S_VERSION: Version of K3S&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# 2) CERT_MANAGER_VERSION: Version of K3S&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# 3) RANCHER_VERSION: Version of K3S&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# 4) HELM_VALUES_RANCHER_FOLDER_PATH: Path of the folder that contains the custom &quot;values.yaml&quot; file for the Rancher Helm installation&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$#&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-ne&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt;
  &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Error: Exactly 4 arguments are required.&quot;&lt;/span&gt;
  &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Usage: &lt;span class=&quot;token variable&quot;&gt;$0&lt;/span&gt; &amp;lt;K3S_VERSION&gt; &amp;lt;CERT_MANAGER_VERSION&gt; &amp;lt;RANCHER_VERSION&gt; &amp;lt;HELM_VALUES_RANCHER_FOLDER_PATH&gt;&quot;&lt;/span&gt;
  &lt;span class=&quot;token builtin class-name&quot;&gt;exit&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;fi&lt;/span&gt;

&lt;span class=&quot;token assign-left variable&quot;&gt;K3S_VERSION&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$1&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;CERT_MANAGER_VERSION&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$2&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;RANCHER_VERSION&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$3&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;HELM_VALUES_RANCHER_FOLDER_PATH&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$4&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Basic tools&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;apt-get&lt;/span&gt; update
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;apt&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;curl&lt;/span&gt; jq &lt;span class=&quot;token parameter variable&quot;&gt;-y&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# K3s Installation&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;curl&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-sfL&lt;/span&gt; https://get.k3s.io &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;INSTALL_K3S_VERSION&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$K3S_VERSION&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;sh&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-s&lt;/span&gt; - server --cluster-init
&lt;span class=&quot;token builtin class-name&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;KUBECONFIG&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token environment constant&quot;&gt;$HOME&lt;/span&gt;/.kube/config&quot;&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;export KUBECONFIG=&lt;span class=&quot;token variable&quot;&gt;$KUBECONFIG&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token environment constant&quot;&gt;$HOME&lt;/span&gt;/.bashrc
&lt;span class=&quot;token function&quot;&gt;mkdir&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-p&lt;/span&gt; &lt;span class=&quot;token environment constant&quot;&gt;$HOME&lt;/span&gt;/.kube
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; k3s kubectl config view &lt;span class=&quot;token parameter variable&quot;&gt;--raw&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token environment constant&quot;&gt;$HOME&lt;/span&gt;/.kube/config
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;chmod&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;600&lt;/span&gt; &lt;span class=&quot;token environment constant&quot;&gt;$HOME&lt;/span&gt;/.kube/config

&lt;span class=&quot;token comment&quot;&gt;# Kubernetes&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;## Helm&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;curl&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-fsSL&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-o&lt;/span&gt; /tmp/get_helm.sh https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3
&lt;span class=&quot;token function&quot;&gt;chmod&lt;/span&gt; +x /tmp/get_helm.sh
/tmp/get_helm.sh

&lt;span class=&quot;token comment&quot;&gt;## Cert Manager&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;CERT_MANAGER_NAMESPACE&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;cert-manager
&lt;span class=&quot;token assign-left variable&quot;&gt;CERT_MANAGER_HELM_RELEASE&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;cert-manager
kubectl apply &lt;span class=&quot;token parameter variable&quot;&gt;-f&lt;/span&gt; https://github.com/cert-manager/cert-manager/releases/download/&lt;span class=&quot;token variable&quot;&gt;$CERT_MANAGER_VERSION&lt;/span&gt;/cert-manager.crds.yaml
helm repo &lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt; jetstack https://charts.jetstack.io
helm repo update
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; helm status &lt;span class=&quot;token parameter variable&quot;&gt;-n&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$CERT_MANAGER_NAMESPACE&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$CERT_MANAGER_HELM_RELEASE&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; /dev/null &lt;span class=&quot;token operator&quot;&gt;&lt;span class=&quot;token file-descriptor important&quot;&gt;2&lt;/span&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token file-descriptor important&quot;&gt;&amp;amp;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt;
  &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Release &apos;&lt;span class=&quot;token variable&quot;&gt;$CERT_MANAGER_HELM_RELEASE&lt;/span&gt;&apos; exists.&quot;&lt;/span&gt;
  &lt;span class=&quot;token assign-left variable&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;helm list &lt;span class=&quot;token parameter variable&quot;&gt;-n&lt;/span&gt; $CERT_MANAGER_NAMESPACE &lt;span class=&quot;token parameter variable&quot;&gt;-o&lt;/span&gt; json &lt;span class=&quot;token parameter variable&quot;&gt;-a&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; jq &lt;span class=&quot;token parameter variable&quot;&gt;-r&lt;/span&gt; .&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;.app_version&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$version&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$CERT_MANAGER_VERSION&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt;
    &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Upgrading release &apos;&lt;span class=&quot;token variable&quot;&gt;$CERT_MANAGER_HELM_RELEASE&lt;/span&gt;&apos; to version &apos;&lt;span class=&quot;token variable&quot;&gt;$CERT_MANAGER_VERSION&lt;/span&gt;&apos;...&quot;&lt;/span&gt;
    helm upgrade &lt;span class=&quot;token variable&quot;&gt;$CERT_MANAGER_HELM_RELEASE&lt;/span&gt; jetstack/cert-manager &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
      &lt;span class=&quot;token parameter variable&quot;&gt;--namespace&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$CERT_MANAGER_NAMESPACE&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--version&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$CERT_MANAGER_VERSION&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;fi&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt;
  &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Release &apos;&lt;span class=&quot;token variable&quot;&gt;$CERT_MANAGER_HELM_RELEASE&lt;/span&gt;&apos; does not exist. Installing version &apos;&lt;span class=&quot;token variable&quot;&gt;$CERT_MANAGER_VERSION&lt;/span&gt;&apos;...&quot;&lt;/span&gt;
  helm &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$CERT_MANAGER_HELM_RELEASE&lt;/span&gt; jetstack/cert-manager &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;token parameter variable&quot;&gt;--namespace&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$CERT_MANAGER_NAMESPACE&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--version&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$CERT_MANAGER_VERSION&lt;/span&gt; --create-namespace
&lt;span class=&quot;token keyword&quot;&gt;fi&lt;/span&gt;
kubectl &lt;span class=&quot;token parameter variable&quot;&gt;-n&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$CERT_MANAGER_NAMESPACE&lt;/span&gt; rollout status deploy/cert-manager

&lt;span class=&quot;token comment&quot;&gt;## Rancher&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;RANCHER_NAMESPACE&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;cattle-system
&lt;span class=&quot;token assign-left variable&quot;&gt;RANCHER_HELM_RELEASE&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;rancher
helm repo &lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt; rancher-latest https://releases.rancher.com/server-charts/latest
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; helm status &lt;span class=&quot;token parameter variable&quot;&gt;-n&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$RANCHER_NAMESPACE&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$RANCHER_HELM_RELEASE&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; /dev/null &lt;span class=&quot;token operator&quot;&gt;&lt;span class=&quot;token file-descriptor important&quot;&gt;2&lt;/span&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token file-descriptor important&quot;&gt;&amp;amp;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt;
  &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Release &apos;&lt;span class=&quot;token variable&quot;&gt;$RANCHER_HELM_RELEASE&lt;/span&gt;&apos; exists.&quot;&lt;/span&gt;
  &lt;span class=&quot;token assign-left variable&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;helm list &lt;span class=&quot;token parameter variable&quot;&gt;-n&lt;/span&gt; $RANCHER_NAMESPACE &lt;span class=&quot;token parameter variable&quot;&gt;-o&lt;/span&gt; json &lt;span class=&quot;token parameter variable&quot;&gt;-a&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; jq &lt;span class=&quot;token parameter variable&quot;&gt;-r&lt;/span&gt; .&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;.app_version&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$version&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$RANCHER_VERSION&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt;
    &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Upgrading release &apos;&lt;span class=&quot;token variable&quot;&gt;$RANCHER_HELM_RELEASE&lt;/span&gt;&apos; to version &apos;&lt;span class=&quot;token variable&quot;&gt;$RANCHER_VERSION&lt;/span&gt;&apos;...&quot;&lt;/span&gt;
    helm upgrade &lt;span class=&quot;token variable&quot;&gt;$RANCHER_HELM_RELEASE&lt;/span&gt; rancher-latest/rancher &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
      &lt;span class=&quot;token parameter variable&quot;&gt;--namespace&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$RANCHER_NAMESPACE&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--version&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$RANCHER_VERSION&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
      &lt;span class=&quot;token parameter variable&quot;&gt;-f&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$HELM_VALUES_RANCHER_FOLDER_PATH&lt;/span&gt;/values.yaml
  &lt;span class=&quot;token keyword&quot;&gt;fi&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt;
  &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Release &apos;&lt;span class=&quot;token variable&quot;&gt;$RANCHER_HELM_RELEASE&lt;/span&gt;&apos; does not exist. Installing version &apos;&lt;span class=&quot;token variable&quot;&gt;$RANCHER_VERSION&lt;/span&gt;&apos;...&quot;&lt;/span&gt;
  helm &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$RANCHER_HELM_RELEASE&lt;/span&gt; rancher-latest/rancher &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;token parameter variable&quot;&gt;--namespace&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$RANCHER_NAMESPACE&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--version&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$RANCHER_VERSION&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;token parameter variable&quot;&gt;-f&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$HELM_VALUES_RANCHER_FOLDER_PATH&lt;/span&gt;/values.yaml --create-namespace
  kubectl &lt;span class=&quot;token parameter variable&quot;&gt;-n&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$RANCHER_NAMESPACE&lt;/span&gt; rollout status deploy/rancher
  &lt;span class=&quot;token function&quot;&gt;sleep&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;30&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;fi&lt;/span&gt;
kubectl &lt;span class=&quot;token parameter variable&quot;&gt;-n&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$RANCHER_NAMESPACE&lt;/span&gt; rollout status deploy/rancher

&lt;span class=&quot;token comment&quot;&gt;# Clean up&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;rm&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-rf&lt;/span&gt; HELM_VALUES_RANCHER_FOLDER_PATH&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This script is tedious to read but it, as the comment in the script says, installs K3s, Cert Manager (via Helm Chart) and Rancher (via Helm Chart).&lt;/p&gt;
&lt;p&gt;I&apos;m using custom values for the Helm Chart.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# values.yaml&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;hostname&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 192.168.110.10.sslip.io
&lt;span class=&quot;token key atrule&quot;&gt;replicas&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;bootstrapPassword&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ChangedByTerraform&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Notice that the hostname is the static IP that we set in the &lt;code class=&quot;language-text&quot;&gt;vagrant.yaml&lt;/code&gt; file! Also, the &lt;code class=&quot;language-text&quot;&gt;bootstrapPassword&lt;/code&gt; will be modified by the bootstrap Rancher provider of OpenTofu.&lt;/p&gt;
&lt;p&gt;Finally, the &lt;code class=&quot;language-text&quot;&gt;install-node.sh&lt;/code&gt; script!&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# install-node.sh&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;#!/bin/bash&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-euo&lt;/span&gt; pipefail

&lt;span class=&quot;token comment&quot;&gt;# Description: This bash script applies best practices for Kubernetes nodes.&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Disable Swap (Kubernetes requirements)&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; swapoff &lt;span class=&quot;token parameter variable&quot;&gt;-a&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;sed&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-i&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;/swap/d&apos;&lt;/span&gt; /etc/fstab

&lt;span class=&quot;token comment&quot;&gt;# Install recommended packages&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;apt-get&lt;/span&gt; update
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;apt-get&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-y&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;curl&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;tar&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;It&apos;s recommended to set the nodes with some &lt;a href=&quot;https://ranchermanager.docs.rancher.com/reference-guides/best-practices/rancher-server/tips-for-running-rancher#make-sure-nodes-are-configured-correctly-for-kubernetes&quot;&gt;Kubernetes configurations&lt;/a&gt;, such as disable swap.&lt;/p&gt;
&lt;h3&gt;Deployment&lt;/h3&gt;
&lt;p&gt;Deploying all of this using OpenTofu is as simple as running &lt;code class=&quot;language-text&quot;&gt;tofu init&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;tofu apply --var-file=&quot;vars.tfvars&quot;&lt;/code&gt;. After it&apos;s deployed you can simply access &lt;code class=&quot;language-text&quot;&gt;192.168.110.10.sslip.io&lt;/code&gt; and verify the cluster deployment. The username to access the Rancher application is &lt;code class=&quot;language-text&quot;&gt;admin&lt;/code&gt; and the password can be found on the &lt;code class=&quot;language-text&quot;&gt;terraform.tfstate&lt;/code&gt; file (since the bootstrap provider modified the initial admin password).&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/7ee7da99ebe6451daf3b1d6f180a56d1/e8e04/rancher.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 50%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAAAsTAAALEwEAmpwYAAAB/0lEQVR42l2RW2/TQBCF/SNSKI0v68va69hO7LjNpXUuTpqUi3DSpInagip4ohIgBALRhwLilx/GmzQIHj7N7OzMmbO2YsU9sGaO7LRA2h7CcCKYbuMfytoD/99ZBON1WF6CZ/NbKI3rOzSGK4juS/jZDOFgsSPon6ORX+BoeoXD6aWMYX+J2gnd91YIehc0s0CUE8MFeHsGRTcFgqxAPV9RcYlo8Jewv0AyWmMwf4vjFzcydosVjpczZOs58stzXH8p8O4HcV/g9ecCyt5jHX5rCr/zFKJ9tsPfUuY8ndDSCSY3OVZfiW8jvLrL8eHXGJ9+j/H+foTb7zk+/jwlwcoTaDqHSDKEzQzBAymd057MraAFM0hhRymYaMIQyS4aFFnQAQs7pONsBTUbHg1EcVsSljHpIGy0wCyBapURJlSNw+D0k9wYzA7ARQzDqkHfUuoolb0DSiw4joBmcMpNiaoyErBpewv1529QG69hxydQqwZU3YZOPQYtK3NZo/5SR6mQQ52KnNek5Y2bzaVmOPDqbdS6Z/CORnCCQ+q15CJVNaGTS415W8HSxFawHDYdTs/zoJH1Ulz4kXSdxCnU/Sq0Aw0unYUo6z4YLSsFDa+5WyAdPto3ZMJsDp259AQHpumCMQ7bFvD9Ojm3YVHO3UAuk4LmplczPfmpVM2S/AEe1TZvZ8LBIQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Rancher UI&quot;
        title=&quot;&quot;
        src=&quot;/static/7ee7da99ebe6451daf3b1d6f180a56d1/f058b/rancher.png&quot;
        srcset=&quot;/static/7ee7da99ebe6451daf3b1d6f180a56d1/c26ae/rancher.png 158w,
/static/7ee7da99ebe6451daf3b1d6f180a56d1/6bdcf/rancher.png 315w,
/static/7ee7da99ebe6451daf3b1d6f180a56d1/f058b/rancher.png 630w,
/static/7ee7da99ebe6451daf3b1d6f180a56d1/40601/rancher.png 945w,
/static/7ee7da99ebe6451daf3b1d6f180a56d1/78612/rancher.png 1260w,
/static/7ee7da99ebe6451daf3b1d6f180a56d1/e8e04/rancher.png 1270w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2&gt;Cya&lt;/h2&gt;
&lt;p&gt;Hope you enjoyed this blog post! This time the post was really hands-on and not so focused on the theorical part of it, but I hope it&apos;s a bit clear how you can deploy Kubernetes clusters using Rancher and OpenTofu!&lt;/p&gt;
&lt;p&gt;See you in the next post! 👋&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Configuring an email forward to enhance your phone security in case of a robbery]]></title><description><![CDATA[Today we use our phones for absolutely everything and to access everything, such as internet banking, social networks, email, phone calls…]]></description><link>https://felipetrindade.com/email-forwarder/</link><guid isPermaLink="false">https://felipetrindade.com/email-forwarder/</guid><pubDate>Sat, 04 May 2024 19:23:32 GMT</pubDate><content:encoded>&lt;p&gt;Today we use our phones for absolutely everything and to access everything, such as internet banking, social networks, email, phone calls... And this might be very risky.&lt;/p&gt;
&lt;p&gt;In case you want to go directly to the Git repository, please check &lt;a href=&quot;https://github.com/felipelaptrin/email-forwarder&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;The risk&lt;/h2&gt;
&lt;p&gt;I remember that I saw a piece of news about a man who had his phone robbed. The thing is that the phone was unblocked. The robber looked for bank apps installed on the phone and for every bank app the robber started the &quot;forget password&quot; procedure. The reset password email was sent to the email of the person who was robbed (the email account was logged into the phone) and the robber had access to his bank account, stealing hundreds of dollars.&lt;/p&gt;
&lt;p&gt;The bank security system to reset the password must be more secure, I agree with you. But this doesn&apos;t change the fact that the robber with access to the phone and SIM card can reset several accounts of this person (bank apps, social networks, e-commerce account...).&lt;/p&gt;
&lt;p&gt;Because of that, I created a new Gmail account that only receives the subject of the emails received by my main and secondary emails and removed the main and secondary email accounts from my phone. This way, even if I had the &lt;a href=&quot;https://youtu.be/lxxvT4KVqEs&quot;&gt;bad luck&lt;/a&gt; of getting robbed with my phone unlocked I know that at least the robber won&apos;t have access to my email, making it very hard for him to reset the password of my bank account. In fact, he will see in my phone that new emails are arriving but since it&apos;s only the title of the email (the content won&apos;t be forwarded) the robber won&apos;t receive the code or link to reset the password of my account.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/b25826bc049412a6a04d3a1b7467828b/1b1d5/diagram.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 45.56962025316456%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAYAAAAywQxIAAAACXBIWXMAAAsTAAALEwEAmpwYAAABU0lEQVR42p1S20rDQBDd//8IH/RBRYpS1IJF1AehBSkipVbbJDapadJczK25bJI9TlKstcUiDgzD7BlmzpxZlg/6SIYDJL0HFHMTlQkh8F9jydUlgmYDzskRtNsb8GXHn1VVvmvIGs6ymQ7TthE4DlLLgheG0FQVIcV1tmJXs6JYxrIAc3wfpmFgbs3BCeBZBtd14dN7zjMkmYCn9RHqQ8JR45zzZaR+sTxG1DhE0G4hbJ2DfU0WG9rleY44XqDT7eO02caTYYOnKd51HYqiQJYk6LYD57GH8P4OHt3h47oNVqhvyGxiN1GARYQgiqDIMixav7KJOkP/WUG+2lDUXpZlTaKgIaqmQdKmCGgzllycwT8+gLu/h2m3g4AKkjiuGW7Lta1kQvUeyWMTgYoMi8cvsElDR5aQmsa30BuM/vxtDNPE62iEEWmScr77or8eWqz8E0EMtOq6j0yGAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;The diagram of the project: main and secondary emails getting forwarded&quot;
        title=&quot;&quot;
        src=&quot;/static/b25826bc049412a6a04d3a1b7467828b/f058b/diagram.png&quot;
        srcset=&quot;/static/b25826bc049412a6a04d3a1b7467828b/c26ae/diagram.png 158w,
/static/b25826bc049412a6a04d3a1b7467828b/6bdcf/diagram.png 315w,
/static/b25826bc049412a6a04d3a1b7467828b/f058b/diagram.png 630w,
/static/b25826bc049412a6a04d3a1b7467828b/1b1d5/diagram.png 876w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2&gt;Proposed Architecture&lt;/h2&gt;
&lt;p&gt;The gear icon in the diagram above is composed of several components that will be deployed in the cloud (I used AWS). I will comment on the cost of this project later (spoiler: basically free).&lt;/p&gt;
&lt;p&gt;From time to time (cron-based) a code written in Python, using the &lt;a href=&quot;https://en.wikipedia.org/wiki/Internet_Message_Access_Protocol&quot;&gt;IMAP&lt;/a&gt; protocol, will communicate with the main/secondary email, get the recent emails and forward only the title of these emails to the receiver email account using pub-sub pattern.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/aa539b50f5e04c5c97f2807e8e4de7b8/21335/architecture.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 40.50632911392405%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAYAAAD5nd/tAAAACXBIWXMAAAsTAAALEwEAmpwYAAABiElEQVR42n2Rz27TQBDGfeTMCfVleBO4cUe9cKl4Aw6cQapo4dBLuSEkJNKoitog+odApbYJkXGj4rWztlM73jhr/xinDqgc8mlnd7TzzTc7s076+oB4/T3m0mdORTEvMZOQdOfJwmyeMjMFhcSyvUv047ekm4fYmmtmkgP591Pyr13Mt2Oc6NE2/oMNblrnXMcBg58uoTdAP7tPsrFGpgOGcneVKMJXbYK158RPdwknEf2LC65UQNT+TPjpI9F+G0ft/cB902LqR1ITylL2yqI62/zuvBO/lFVRSsyOYvzNfcyxRw1rrfBLIq3pex5BEOLYqpLnS56ctSVJgo4i4tyiswKlFJMsZYlauOIurIiWjTl1tGoYOk7wfZ88z/8S8pnB7/9CHZ7DxDSq/0v+g7NUm3U76N0dpt2DJklaLezCvXnRwru3TiafscC8XCHYIDvrMT45Iun1bi/qQvY2cbr1BfXwJebD2bLH1YJ1a95oxMB1uZaW78xHBq9l6O5wyDgcL+a8Cn8AcARXNaHFq/QAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Cloud Architecture of the project&quot;
        title=&quot;&quot;
        src=&quot;/static/aa539b50f5e04c5c97f2807e8e4de7b8/f058b/architecture.png&quot;
        srcset=&quot;/static/aa539b50f5e04c5c97f2807e8e4de7b8/c26ae/architecture.png 158w,
/static/aa539b50f5e04c5c97f2807e8e4de7b8/6bdcf/architecture.png 315w,
/static/aa539b50f5e04c5c97f2807e8e4de7b8/f058b/architecture.png 630w,
/static/aa539b50f5e04c5c97f2807e8e4de7b8/40601/architecture.png 945w,
/static/aa539b50f5e04c5c97f2807e8e4de7b8/21335/architecture.png 1082w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;This entire infrastructure was created using &lt;a href=&quot;https://aws.amazon.com/pt/cdk/&quot;&gt;AWS CDK&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;CDK Code&lt;/h2&gt;
&lt;p&gt;All the infrastructure was created using a single stack:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// stack.ts&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; Duration&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Stack&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; StackProps&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Tags &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;aws-cdk-lib&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; Construct &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;constructs&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; DockerImageFunction&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; DockerImageCode &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;aws-cdk-lib/aws-lambda&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; path &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;path&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; StringParameter &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;aws-cdk-lib/aws-ssm&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; InfraConfig &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;./config&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; Rule&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; RuleTargetInput&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Schedule &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;aws-cdk-lib/aws-events&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; LambdaFunction &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;aws-cdk-lib/aws-events-targets&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; Topic &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;aws-cdk-lib/aws-sns&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; EmailSubscription &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;aws-cdk-lib/aws-sns-subscriptions&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;EmailForwarderStack&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Stack&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;scope&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Construct&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; id&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; config&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; InfraConfig&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; props&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; StackProps&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;scope&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; props&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; topic &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Topic&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Topic&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      topicName&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;email-forwarder&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    topic&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addSubscription&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;EmailSubscription&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;config&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;targetEmail&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; lambda &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DockerImageFunction&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Lambda&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      description&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&quot;Cron-based Lambda that checks new email of Gmail accounts and forward the title and sender of the email to another Gmail email&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      functionName&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;email-forwarder&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      code&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; DockerImageCode&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fromImageAsset&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;path&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;__dirname&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;../../&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;lambda&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      environment&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token constant&quot;&gt;SNS_TOPIC_ARN&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; topic&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;topicArn&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      timeout&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Duration&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;seconds&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;config&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;lambda&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;timeoutInSeconds&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      memorySize&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; config&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;lambda&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;memoryInMiB&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      deadLetterTopic&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; topic&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      architecture&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; config&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;lambda&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;architecture&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    topic&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;grantPublish&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;lambda&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; email &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; config&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sourceEmail&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;username&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; domain&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; email&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;@&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

      &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; parameterEmail &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;StringParameter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;Email&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;username&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        parameterName&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;/email/&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;domain&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;username&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;/email&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        stringValue&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; email&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      parameterEmail&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;grantRead&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;lambda&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; parameterPassword &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;StringParameter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;Email&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;username&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;Password&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        parameterName&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;/email/&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;domain&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;username&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;/password&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        stringValue&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;ChangeMe&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      parameterPassword&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;grantRead&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;lambda&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; parameterId &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;StringParameter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;Email&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;username&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;LatestId&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        parameterName&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;/email/&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;domain&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;username&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;/latest-id&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        stringValue&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;ThisWillBeManagedByTheLambda&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      parameterId&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;grantRead&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;lambda&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      parameterId&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;grantWrite&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;lambda&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

      &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; unit &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; config&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;lambda&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;triggerRateInMinutes &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;minute&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;minutes&quot;&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; triggerRule &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Rule&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;TriggerRule&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;username&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        schedule&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Schedule&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;expression&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;rate(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;config&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;lambda&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;triggerRateInMinutes&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;unit&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      triggerRule&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addTarget&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LambdaFunction&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;lambda&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          event&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; RuleTargetInput&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fromObject&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            parameterStore&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
              lastEmailIdRead&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; parameterId&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;parameterName&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
              email&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; parameterEmail&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;parameterName&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
              password&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; parameterPassword&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;parameterName&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    Tags&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Repository&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https://github.com/felipelaptrin/email-forwarder&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    Tags&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;ManagedBy&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;CDK&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;As you can see we are deploy the following resources:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;SNS Topic&lt;/code&gt;: The topic will be used to forward the subjects of the emails to the receiver Gmail account. So in this topic there will be a single subscriber (the receiver account).&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;Lambda&lt;/code&gt;: It&apos;s the &quot;brain&quot; of the project, will contain the code responsible for reading the emails from the main/secondary Gmail account and publish this message into the SNS topic. Notice that I&apos;m using a monorepo and the application code of the lambda sits on the same repository and we are building the Docker image of the Lambda during CDK deployment.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;Parameters&lt;/code&gt;: We are going to store the credentials to the main/secondary account and the last ID read by the Lambda (more on that later).&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;EventBridge Rule&lt;/code&gt;: This allows us to trigger the Lambda passing as parameter the name of the parameter store that has the credentials of the emails.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This code is completely generic (things are not hardcoded) and you can customize it by editing the &lt;code class=&quot;language-text&quot;&gt;config.ts&lt;/code&gt; file.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; Architecture &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;aws-cdk-lib/aws-lambda&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LambdaProps&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  timeoutInSeconds&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  memoryInMiB&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  triggerRateInMinutes&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  architecture&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Architecture&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;InfraConfig&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  sourceEmail&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  targetEmail&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  lambda&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; LambdaProps&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; config&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; InfraConfig &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  sourceEmail&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;felipelaptrin@gmail.com&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;paotrindadepao@gmail.com&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  targetEmail&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;emailsfelipetrindade@gmail.com&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  lambda&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    timeoutInSeconds&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;60&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    memoryInMiB&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;256&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    triggerRateInMinutes&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    architecture&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Architecture&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;ARM_64&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// This must match your architecture (since we are building during deployment)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Notice that you can completely modify your use case.&lt;/p&gt;
&lt;h2&gt;Lambda code&lt;/h2&gt;
&lt;p&gt;I decided to go for a more OOP-oriented code and created the following classes:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;python&quot;&gt;&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# ssm.py&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; boto3
&lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; envs &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; AWS_REGION

ssm &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; boto3&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;client&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;ssm&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; region_name&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;AWS_REGION&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ParameterStore&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;get_parameter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;self&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
            response &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; ssm&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;get_parameter&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Name&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; response&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;Parameter&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;Value&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; value&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;strip&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;except&lt;/span&gt; Exception &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-interpolation&quot;&gt;&lt;span class=&quot;token string&quot;&gt;f&apos;Error fetching parameter from SSM Parameter Store: &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;update_parameter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;self&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; value&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
            ssm&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;put_parameter&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
                Name&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                Value&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                Overwrite&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;True&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;except&lt;/span&gt; Exception &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-interpolation&quot;&gt;&lt;span class=&quot;token string&quot;&gt;f&apos;Error trying to update parameter from SSM Parameter Store: &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This class is responsible for retrieving and recording data from/in the Parameter Store. I also created a class to interact with the SNS topic.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;python&quot;&gt;&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# sns.py&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; boto3
&lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; email_reader &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; Email
&lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; envs &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; AWS_REGION&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; SNS_TOPIC_ARN

sns &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; boto3&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;client&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;sns&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; region_name&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;AWS_REGION&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SNS&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;send_message&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;email&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Email&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; original_email&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
            sns&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;publish&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
                TargetArn&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;SNS_TOPIC_ARN&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                Subject&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string-interpolation&quot;&gt;&lt;span class=&quot;token string&quot;&gt;f&quot;[&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;email&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sender&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;] &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;email&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;title&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                Message&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string-interpolation&quot;&gt;&lt;span class=&quot;token string&quot;&gt;f&quot;Check email content in the &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;original_email&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; email.&quot;&lt;/span&gt;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;except&lt;/span&gt; Exception &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-interpolation&quot;&gt;&lt;span class=&quot;token string&quot;&gt;f&apos;Error publish message to topic: &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The last class I created was responsible for managing the Email read.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;python&quot;&gt;&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# email_reader.py&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; email
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; imaplib
&lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; dataclasses &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; dataclass
&lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; typing &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; List

&lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; ssm &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; ParameterStore

imap &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; imaplib&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;IMAP4_SSL&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;imap.gmail.com&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token decorator annotation punctuation&quot;&gt;@dataclass&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Email&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token builtin&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;int&lt;/span&gt;
    title&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;str&lt;/span&gt;
    sender&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;str&lt;/span&gt;

&lt;span class=&quot;token decorator annotation punctuation&quot;&gt;@dataclass&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Parameters&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    lastEmailIdRead&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;str&lt;/span&gt;
    email&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;str&lt;/span&gt;
    password&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;str&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;EmailReader&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;__init__&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;self&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; parameters&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Parameters&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;parameters &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; parameters
        self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;parameter_store &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; ParameterStore&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;email &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;parameter_store&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;get_parameter&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;parameters&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;email&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;imap &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;login&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;latest_email_read &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;get_latest_email_read&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;


    &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;login&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;self&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; imaplib&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;IMAP4_SSL&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        imap &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; imaplib&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;IMAP4_SSL&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;imap.gmail.com&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

        password &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;parameter_store&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;get_parameter&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;parameters&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;password&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        imap&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;login&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;email&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; password&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; imap

    &lt;span class=&quot;token decorator annotation punctuation&quot;&gt;@staticmethod&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;__decode_str&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;string&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token triple-quoted-string string&quot;&gt;&quot;&quot;&quot;Decode email header string.&quot;&quot;&quot;&lt;/span&gt;
        decoded_header &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; email&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;header&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;decode_header&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;string&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        result &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; decoded_part&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; charset &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; decoded_header&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;isinstance&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;decoded_part&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;bytes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; charset&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
                    decoded_part &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; decoded_part&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;decode&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;charset&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
                    decoded_part &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; decoded_part&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;decode&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            result&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;append&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;decoded_part&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;join&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;result&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;read_emails&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;self&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; List&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;Email&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        imap &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;imap
        emails &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;

        emails_to_read_ids &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
            &lt;span class=&quot;token builtin&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;id_as_int&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;encode&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;ascii&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; id_as_int
            &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;latest_email_read&lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;get_latest_email_receive&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-interpolation&quot;&gt;&lt;span class=&quot;token string&quot;&gt;f&quot;There are &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;emails_to_read_ids&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; to be read...&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; email_id &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; emails_to_read_ids&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
            _&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; msg_data &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; imap&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;fetch&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;email_id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;(RFC822)&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            imap&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;store&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;email_id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;-FLAGS&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;\\Seen&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# Mark as unread&lt;/span&gt;

            &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; response_part &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; msg_data&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;isinstance&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;response_part&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;tuple&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
                    msg &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; email&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;message_from_bytes&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;response_part&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                    sender &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;__decode_str&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;msg&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;from&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;split&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot; &amp;lt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                    read_email &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Email&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
                        &lt;span class=&quot;token builtin&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;email_id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                        title&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;__decode_str&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;msg&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;subject&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                        sender &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; sender&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;token keyword&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-interpolation&quot;&gt;&lt;span class=&quot;token string&quot;&gt;f&quot;Email ==&gt; &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;read_email&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                    emails&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;append&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;read_email&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; emails

    &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;update_latest_email_read&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;self&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; most_recent_email_id&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Updating parameter store &quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;parameter_store&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;update_parameter&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
                self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;parameters&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;lastEmailIdRead&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;token builtin&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;most_recent_email_id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;except&lt;/span&gt; Exception &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;raise&lt;/span&gt; Exception&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;get_latest_email_read&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;self&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
            value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;parameter_store&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;get_parameter&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
                name&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;parameters&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;lastEmailIdRead
            &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token comment&quot;&gt;# The below if statement will only be executed once (first run after infra is created)&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; value &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;ThisWillBeManagedByTheLambda&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Bootstraping the Parameter store with ID value&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;update_latest_email_read&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;get_latest_email_receive&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;get_latest_email_receive&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;except&lt;/span&gt; Exception &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;get_latest_email_receive&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;self&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        imap &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;imap
        imap&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;select&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;INBOX&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        _&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; email_ids &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; imap&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;search&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;ALL&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        email_ids &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; email_ids&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;split&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

        latest_email_id &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; email_ids&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;latest_email_id&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;decode&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;ascii&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Something that I think it&apos;s worth mentioning:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;IMAP reads emails by the index of the email. So the first email your Gmail got has index &lt;code class=&quot;language-text&quot;&gt;1&lt;/code&gt;, the second has index &lt;code class=&quot;language-text&quot;&gt;2&lt;/code&gt; and so on until the most recent email you have.&lt;/li&gt;
&lt;li&gt;If we store the last read email ID (I used Parameter Store for that) we can check the most recent email ID and the last email ID read and forward only those that were not read.&lt;/li&gt;
&lt;li&gt;I don&apos;t want you to deal with this ID thing manually, so the first time the code runs (after you deploy the infra), it will set the last email read as the last email you received (so the first time it runs it won&apos;t email you, it will only setup the Parameter Store).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And finally, the entrypoint of our lambda:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;python&quot;&gt;&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# main.py&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; os

&lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; email_reader &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; EmailReader&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Parameters
&lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; envs &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    PARAMETER_STORE_EMAIL&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    PARAMETER_STORE_EMAIL_PASSWORD&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    PARAMETER_STORE_LAST_EMAIL_ID_READ&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; sns &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; SNS


&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;lambda_handler&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; context&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-interpolation&quot;&gt;&lt;span class=&quot;token string&quot;&gt;f&quot;Event ==&gt; &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-interpolation&quot;&gt;&lt;span class=&quot;token string&quot;&gt;f&quot;Context ==&gt; &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;context&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    reader &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; EmailReader&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Parameters&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;**&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;parameterStore&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    emails &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; reader&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;read_emails&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; emails&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; email &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; emails&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Sending email...&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            SNS&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;send_message&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;email&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; reader&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;email&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        max_id &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;max&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;emails&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; key&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;lambda&lt;/span&gt; email&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; email&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;id&lt;/span&gt;
        reader&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;update_latest_email_read&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;max_id&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;decode&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;ascii&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;os&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;getenv&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;LOCAL_DEVELOPMENT&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;TRUE&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
   &lt;span class=&quot;token keyword&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;---- Running the code locally ----&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
   lambda_handler&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
       &lt;span class=&quot;token string&quot;&gt;&quot;parameterStore&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
           &lt;span class=&quot;token string&quot;&gt;&quot;lastEmailIdRead&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; PARAMETER_STORE_LAST_EMAIL_ID_READ&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
           &lt;span class=&quot;token string&quot;&gt;&quot;email&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; PARAMETER_STORE_EMAIL&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
           &lt;span class=&quot;token string&quot;&gt;&quot;password&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; PARAMETER_STORE_EMAIL_PASSWORD&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now that you know how the infrastructure and code works let&apos;s check how you can implement that too.&lt;/p&gt;
&lt;h2&gt;Pricing&lt;/h2&gt;
&lt;p&gt;AWS offers a generous free tier for the services we use:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;Parameter Store&lt;/code&gt;: Standard parameters are free. The interaction (retrieve/update value) with the parameter costs &lt;code class=&quot;language-text&quot;&gt;$0.05&lt;/code&gt; per 10,000 interactions.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;EventBridge Rule&lt;/code&gt;: Free for our use case.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;SNS&lt;/code&gt;: 1,000 notifications are free, after that $2 per 100,000 notifications.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;Lambda&lt;/code&gt;: This varies based on the memory and execution time of the Lambda. Using a Lambda of 512MB and 5s for each execution and running every 1 minute we will use around &lt;code class=&quot;language-text&quot;&gt;110,000GB-s/month&lt;/code&gt;. The free tier is 1 million invocations per month and 400,000Gb-s of compute time. So for us, this will be free.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So this is very likely to be free for you!&lt;/p&gt;
&lt;h2&gt;Deployment&lt;/h2&gt;
&lt;p&gt;To make it more user-friendly I will do a step-by-step guide to help you setup all of this. Follow this in order!&lt;/p&gt;
&lt;h3&gt;Gmail settings&lt;/h3&gt;
&lt;p&gt;The first thing we need to do is to access your &lt;a href=&quot;https://mail.google.com/mail/u/0/#settings/general&quot;&gt;Gmail settings&lt;/a&gt; and make sure the &lt;code class=&quot;language-text&quot;&gt;IMAP&lt;/code&gt; is enabled (check the &lt;code class=&quot;language-text&quot;&gt;Forwarding and POP/IMAP&lt;/code&gt; tab).&lt;/p&gt;
&lt;p&gt;After that let&apos;s create a new App Password for us to use with the IMAP.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Access &lt;a href=&quot;https://myaccount.google.com/?hl=en&quot;&gt;Google Settings&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/4de8099daa457627c3ba8fc019c86f93/60b3a/google-settings.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 47.46835443037975%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAYAAAAywQxIAAAACXBIWXMAAAsTAAALEwEAmpwYAAABYUlEQVR42nVSy07DMBDM/98r/oA/4II4ICpx41S1PQFqmpJn0+DEThzbGXadpgQoI61s2Z7Z8djBW67wmkvshcW+dsilhTEGfd/DWAvrnB/dMECpFuv1Bp3WcG7we4yB9hzNmROsohKr6IT3U4+wAcoOf8AERhRFWCwW0CQ4X58jqIVAdSzQqgb/gTsLOnc8HrHdbFFVFUQtYMn578aBEDXK8gRNpMEvjpt8haZp0HWdH9M0IcHCixZFgSzLoKREqx1k5y68gLtNh+q69l24fIaUJQuzk+lyU1Z87bZtKc8evXWXPANJXdjBRGSw0G4X4nA4QMoxCpU/Yx8+emF2l6YpEiqjetyvl7h5uh0FJ0dzsHAcfyBJYiLFlK9GnCzx8HJ3bqgx58VVRj8lHAWvPQK7LT/JOXVXnSEB+/09qLQZfHbG+tR/PspVQf5zeiQpKud57iLoH6Ll3IbzeeeLHX4BsM+7CG/DKwIAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Google Settings&quot;
        title=&quot;&quot;
        src=&quot;/static/4de8099daa457627c3ba8fc019c86f93/f058b/google-settings.png&quot;
        srcset=&quot;/static/4de8099daa457627c3ba8fc019c86f93/c26ae/google-settings.png 158w,
/static/4de8099daa457627c3ba8fc019c86f93/6bdcf/google-settings.png 315w,
/static/4de8099daa457627c3ba8fc019c86f93/f058b/google-settings.png 630w,
/static/4de8099daa457627c3ba8fc019c86f93/40601/google-settings.png 945w,
/static/4de8099daa457627c3ba8fc019c86f93/60b3a/google-settings.png 1179w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;Search for &lt;code class=&quot;language-text&quot;&gt;App Passwords&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/1d3ec1eb2078f716620966a416f48474/60b3a/google-app-passwords.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 47.46835443037975%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAYAAAAywQxIAAAACXBIWXMAAAsTAAALEwEAmpwYAAABVElEQVR42nVSyU7DMBDN//8Af8AfcEEcEJW4cao4gtQNZ21dJ95iO48Zh1alakd6ysTxWzxx8d0YfDUaaxUhVIA8Kqh+gPMecZoQ032EmEBbCBNSShjHEcVyu8dye8C3DNgcA6qmI7QYtEVgEmGM95Cy2GUVvVKQXQtndHbRWqPve3hKeHLGFelU/P1SkPtCqR7d/pDjJkrjnIMxJgtXdY22pbTDQO8z5l5D8x7qjYuEdPYspJToui4TOdnJlQ1CCDkhP3meicA9m1prM5wf89Ejn4QF2Y1dmRhjzItMWq1W2O12ORWXad+xWb+CgzRNjaqqUVYVghnx/LnAw9vjLHg9By4WFuIHZSmIJGCNhygXePl4+jP0/+YnZE03ZT0L3ho2p90fKTm5GxdIIJ6Hzsf2YYL1NIrIglc/5aYgfTB+JhlCyrx0FuR1bVO+i/P+lMEJfwE43b0MImguzwAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Google Setting App Passwords&quot;
        title=&quot;&quot;
        src=&quot;/static/1d3ec1eb2078f716620966a416f48474/f058b/google-app-passwords.png&quot;
        srcset=&quot;/static/1d3ec1eb2078f716620966a416f48474/c26ae/google-app-passwords.png 158w,
/static/1d3ec1eb2078f716620966a416f48474/6bdcf/google-app-passwords.png 315w,
/static/1d3ec1eb2078f716620966a416f48474/f058b/google-app-passwords.png 630w,
/static/1d3ec1eb2078f716620966a416f48474/40601/google-app-passwords.png 945w,
/static/1d3ec1eb2078f716620966a416f48474/60b3a/google-app-passwords.png 1179w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;Create a new app password and copy the password value after creation&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/b664a8db1818ab8f6348685b8023bf80/60b3a/google-app-passwords-creation.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 47.46835443037975%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAYAAAAywQxIAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAw0lEQVR42pWR2xKDIAxE/f/v7FQQ8FZvILBNoHXqi8XM7CTh4YRNKnwihIB1XbEsC7ZtSzVn7q21qeb8L6pv4ZyDEDWklJBCptw0DWohoLXGOAyYpukGcN8hG0UQlQBKqwQ0xkCp/DbPcznQh4jX6gkcEGNM4jX8qiQOICHgbN4ZW+PfeO8PGA+4BeQwbYfHs4ZimyRNfdv3MF2HkYZMNGymY4UL+Am40x4tHYf3meS4z9qsoytnxRJgiaUS6ydgqa6Ab8FdxOI5Y64OAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Creating a new App Password&quot;
        title=&quot;&quot;
        src=&quot;/static/b664a8db1818ab8f6348685b8023bf80/f058b/google-app-passwords-creation.png&quot;
        srcset=&quot;/static/b664a8db1818ab8f6348685b8023bf80/c26ae/google-app-passwords-creation.png 158w,
/static/b664a8db1818ab8f6348685b8023bf80/6bdcf/google-app-passwords-creation.png 315w,
/static/b664a8db1818ab8f6348685b8023bf80/f058b/google-app-passwords-creation.png 630w,
/static/b664a8db1818ab8f6348685b8023bf80/40601/google-app-passwords-creation.png 945w,
/static/b664a8db1818ab8f6348685b8023bf80/60b3a/google-app-passwords-creation.png 1179w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Make sure to do all these steps in the email accounts you want to secure, in my case I have two (so I did this for both accounts).&lt;/p&gt;
&lt;h3&gt;Infrastructure deployment&lt;/h3&gt;
&lt;p&gt;To make the development live easier I set up &lt;a href=&quot;https://www.jetify.com/devbox/docs/&quot;&gt;DevBox&lt;/a&gt; in the project. So you only need to have DevBox installed and run &lt;code class=&quot;language-text&quot;&gt;devbox shell&lt;/code&gt; to install all the dependencies of the project (NodeJS, Yarn...).&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Install the dependencies&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;devbox shell&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;Move to the &lt;code class=&quot;language-text&quot;&gt;infrastructure&lt;/code&gt; folder&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;cd&lt;/span&gt; infrastructure&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;Install NodeJS dependencies&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token function&quot;&gt;yarn&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ol start=&quot;4&quot;&gt;
&lt;li&gt;
&lt;p&gt;Modify the &lt;code class=&quot;language-text&quot;&gt;config.ts&lt;/code&gt; file accordingly&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Deploy the infrastructure&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token function&quot;&gt;yarn&lt;/span&gt; cdk deploy&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Make you have AWS credentials setup in place and that your account-region is already &lt;a href=&quot;https://docs.aws.amazon.com/cdk/v2/guide/bootstrapping.html&quot;&gt;CDK bootstraped&lt;/a&gt;.&lt;/p&gt;
&lt;ol start=&quot;6&quot;&gt;
&lt;li&gt;Check the receiver email and accept the subscribe email from Amazon SNS&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/0fe6b1588b5aab256b4a9a2f9e6d7d5b/33d1d/inbox.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 34.177215189873415%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAYAAAAIy204AAAACXBIWXMAAAsTAAALEwEAmpwYAAABGUlEQVR42qWQzU7EMAyE+/6vxYUrN8QFLZW23f5sk7RNnMTJrGOBQIgbkUaJ6vrzeLrxesN1nHCcCScx+tHiYzCYTMTismrd/9ZkCKsNMO7EcrfYD0L31Hu83CJ8ZJAopQxmRq0VX6f+VP1WO6VU5Mz6Zq7ont9WvA4J/co4AuMyEea7g7MG1loQkTQVHfJ7UEwVJIoxyvei8O79MmDZTtizIIhD6xlniEiRBBbkZ9KGppyzAtsAlbBV8m7+uQHn1Qgg6bpeMpxMwrQRzBHFcREXCdY5ydjrai2Sfd/hPSmcuXzeLPWCbpH1Dp8U1qBZCsY6zOuGQEkhxhiFOAG3GNpNFFVBImnQEIL0p+bQyqpFXGYpZvzntAwfspghshZliEsAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Email inbox - accepting AWS SNS topic&quot;
        title=&quot;&quot;
        src=&quot;/static/0fe6b1588b5aab256b4a9a2f9e6d7d5b/f058b/inbox.png&quot;
        srcset=&quot;/static/0fe6b1588b5aab256b4a9a2f9e6d7d5b/c26ae/inbox.png 158w,
/static/0fe6b1588b5aab256b4a9a2f9e6d7d5b/6bdcf/inbox.png 315w,
/static/0fe6b1588b5aab256b4a9a2f9e6d7d5b/f058b/inbox.png 630w,
/static/0fe6b1588b5aab256b4a9a2f9e6d7d5b/40601/inbox.png 945w,
/static/0fe6b1588b5aab256b4a9a2f9e6d7d5b/33d1d/inbox.png 1150w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;ol start=&quot;6&quot;&gt;
&lt;li&gt;Go to the AWS console and update the parameter(s) that contain your password (e.g. &lt;code class=&quot;language-text&quot;&gt;/email/gmail.com/&amp;lt;YOUR_EMAIL&gt;/password&lt;/code&gt;) to used the app password you copied.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Final result&lt;/h2&gt;
&lt;p&gt;Finally! Now we are receiving our forwarded emails as expected!&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/3b39db8fbb82a5ac4f36071e99c3583b/b5c8e/inbox-receiver.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 34.177215189873415%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAYAAAAIy204AAAACXBIWXMAABYlAAAWJQFJUiTwAAABQUlEQVR42j1RCW7EIAzM/x9Zqe3mAMKxmGMJkKlh2yJZ4xlsD8fyuVusknD6+g6q0NRgqEOHNvnUQoVwBcJE5NKQXm2i8xFSP+EcIZgnluuq6L3jb/X7Zn4j14LS6tRu1ka883fd/Vs7+gdp3HP6jOVLvSBthvEF4dVRSoHVGh/rBw6n0GoDESGlhNraNO/9jY35MBrY2PxF31h8SNgPCSFPXLUj5YxTKnghoZSG5uHGGBhr8dQncowIMU2DEAJqrdMwsmaoYBni4/HAvu/s1ufmum7YxTG14ziwbcwPAbd+IhoN4qGjznuapyPv4SnwW3csNAauo1HyZp9F27ZDiHFqhWPgby5ODcUhmRvr5pDCbzh6KES+XcPiKGOXZv5UKh3Wp38uTsdhZ66fEcp6nJagjId2AYY1Shd8LIwFMV/4AYatGgDh9l5DAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Screenshot inbox from receiver email&quot;
        title=&quot;&quot;
        src=&quot;/static/3b39db8fbb82a5ac4f36071e99c3583b/f058b/inbox-receiver.png&quot;
        srcset=&quot;/static/3b39db8fbb82a5ac4f36071e99c3583b/c26ae/inbox-receiver.png 158w,
/static/3b39db8fbb82a5ac4f36071e99c3583b/6bdcf/inbox-receiver.png 315w,
/static/3b39db8fbb82a5ac4f36071e99c3583b/f058b/inbox-receiver.png 630w,
/static/3b39db8fbb82a5ac4f36071e99c3583b/40601/inbox-receiver.png 945w,
/static/3b39db8fbb82a5ac4f36071e99c3583b/78612/inbox-receiver.png 1260w,
/static/3b39db8fbb82a5ac4f36071e99c3583b/b5c8e/inbox-receiver.png 2194w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/312b7fa457c000033830b802961e00d4/eff3b/email.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 25.316455696202528%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAFCAYAAABFA8wzAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAx0lEQVR42nWQyXLEIAxE/f+fOZdMDbuAMYuhI8mpVA4ZlV81i9w0HGNeeJ9N6X2gMSePRVvrkP21N/i7wR/FXZN7hLU2DlmotcJaC8cYYxBC4LnTNSHGqOq9h3dO5yF4OB4TEf9fMEbnICeOh934MoTX6wnPRqVkVKYUwugd13Vx8+D0XVWYcyLn/GNW1ai1pr3Hwyw8LSHyiTkTqHRYmgpVMZz4r7Y+w81aS1VKryzuKSUlJoILCSEWvhr9ppBEpRRVSfKpvgEsooTI9s9kGQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Screenshot email from receiver email&quot;
        title=&quot;&quot;
        src=&quot;/static/312b7fa457c000033830b802961e00d4/f058b/email.png&quot;
        srcset=&quot;/static/312b7fa457c000033830b802961e00d4/c26ae/email.png 158w,
/static/312b7fa457c000033830b802961e00d4/6bdcf/email.png 315w,
/static/312b7fa457c000033830b802961e00d4/f058b/email.png 630w,
/static/312b7fa457c000033830b802961e00d4/40601/email.png 945w,
/static/312b7fa457c000033830b802961e00d4/78612/email.png 1260w,
/static/312b7fa457c000033830b802961e00d4/eff3b/email.png 2072w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2&gt;Cya!&lt;/h2&gt;
&lt;p&gt;I hope you liked this new blog post! See you around! 👋&lt;/p&gt;</content:encoded></item><item><title><![CDATA[ArgoCD beyond the basics]]></title><description><![CDATA[This blog post expects you to be familiar with the basic usage of ArgoCD. I will assume you are already familiar with the following topics…]]></description><link>https://felipetrindade.com/argocd-beyond-basics/</link><guid isPermaLink="false">https://felipetrindade.com/argocd-beyond-basics/</guid><pubDate>Fri, 26 Apr 2024 11:45:32 GMT</pubDate><content:encoded>&lt;p&gt;This blog post expects you to be familiar with the basic usage of ArgoCD. I will assume you are already familiar with the following topics:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://argo-cd.readthedocs.io/en/stable/core_concepts/&quot;&gt;Core concepts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://argo-cd.readthedocs.io/en/stable/user-guide/sync-options/&quot;&gt;Sync Strategies&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://argo-cd.readthedocs.io/en/stable/operator-manual/declarative-setup/&quot;&gt;Declarative setup&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can demo almost this entire blog post! Go check out the &lt;a href=&quot;https://github.com/felipelaptrin/argocd-blog&quot;&gt;ArgoCD Demo Repository&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;GitOps&lt;/h2&gt;
&lt;p&gt;Since I will cover more advanced concepts of GitOps using ArgoCD I assume that you will already know what GitOps is. At the beginning of my DevOps career I thought that GitOps was just a buzzword to say: put everything under an SCM (most common is &lt;code class=&quot;language-text&quot;&gt;Git&lt;/code&gt;). Well, I was wrong that it is just a buzzword but I was partially right about the git part.&lt;/p&gt;
&lt;p&gt;The truth is that GitOps follows four well-defined principles:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Declarative approach&lt;/strong&gt;: You must not do things manually. All changes must be done declaratively via a manifest file (usually &lt;code class=&quot;language-text&quot;&gt;YAML&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;All artifacts should be stored in a SCM system&lt;/strong&gt;: Manifest must be versioned and stored in an SCM system (usually Git), which is considered the source of truth. A common practice is to have a repository only for storing these manifests (application code is placed in another repository).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The GitOps operator constantly pulls changes from the artifacts repository&lt;/strong&gt;: Desired state and manifests are pulled from the source of the truth repository.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The GitOps operator runs a reconciliation loop: observe, diff, act&lt;/strong&gt;: The goal is to always make sure that our system is in sync with the desired state stored under the SCM system.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This blog post will consider our GitOps infrastructure will be represented by: ArgoCD (our GitOps Operator) and the artifacts will be stored in GitHub.&lt;/p&gt;
&lt;h2&gt;App of Apps pattern&lt;/h2&gt;
&lt;p&gt;When going for the declarative setup of ArgoCD (which is something that I recommend you to do, instead of creating things manually - let&apos;s follow GitOps principles!) you can create a manifest for defining &lt;a href=&quot;https://argo-cd.readthedocs.io/en/stable/operator-manual/declarative-setup/#applications&quot;&gt;ArgoCD Applications&lt;/a&gt;. The ArgoCD Application will basically say &quot;Hey ArgoCD, I have manifests for running a new app in a given format (e.g. Helm Chart, pure YAML files, Kustomize...) and they are stored under a given Git repository. Please install this application&quot;.&lt;/p&gt;
&lt;p&gt;Some questions can show up:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;Where this ArgoCD Application manifest will be stored?&lt;/code&gt;: Let&apos;s follow GitOps principles and store this under a Git repository.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;How ArgoCD will notice that I added this ArgoCD Application manifest and it will create my application?&lt;/code&gt;: By default, it won&apos;t be aware. We have two common options: apply the ArgoCD Application manually or let ArgoCD automatically apply this application.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;How can ArgoCD automatically apply the ArgoCD Application file?&lt;/code&gt;: Using the App of Apps pattern.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Imagine you want to create a new application and want it to be deployed in a Kubernetes cluster. Here is a summary of the steps that you probably will do:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Create a pull request to add the ArgoCD Application manifest to the GitHub repository&lt;/li&gt;
&lt;li&gt;If approved by your team, merge the content to the master/main branch&lt;/li&gt;
&lt;li&gt;Connect to the Kubernetes cluster and apply the ArgoCD Application manifest (e.g. &lt;code class=&quot;language-text&quot;&gt;kubectl apply -f my-application.yaml&lt;/code&gt;) and now ArgoCD is aware of the new application you want to install&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 535px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 74.0506329113924%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAPCAYAAADkmO9VAAAACXBIWXMAAAsTAAALEwEAmpwYAAACC0lEQVR42qWTX2vTUBjG+5X8IoL4FbwebBcKG4IOb1TUmylqYSCyoYggeCFCVqZI2UVLabbaNTP9szYhSZsmMT3N//N4zunSFcdkmwdeTs5Jzu+87/PkLeCqg1IxZaaGqPR5vi5cnkNBs0w8p+ohJqu3QJ6uIfz09mJAAVgIkVWawHA8kG9f4F6/Bv/hbZBnd0HT9CwwP3QefDgcQlEU1KpVRHxzr4R44z4o8f+dYQ62bRuqqkLXdciyjFarJfY5uNvtotsfoNFsovVLxaDfPwXajgvdMlj0EMWRABJCRDamacJ1XQEPgkB8nyQJUlYiX09ZxHGMIAxRoHQm8PPHj7C0dBNrxRtQ2geYkgCO6yA7MeCiQ2ToTghev9jAg/VV7Fa+wnFG8P0JmA0zExiUZ8znvy9I2TJO2Du6AKw2Fbzaeo/N7Q/Y2+caUWhMM67beDw+PcxKzOHzyDU/icKY6aIcHWFnpwRJktButwWEH+T6lctldDodaJqGer2OSqUCy7Lml8idGG8kH/oonWXI3eofHwuI53no9XrC1ZAJzIXmwTPLDTAMg4UJyxjgsOvhzqaDJx9d3NtyELHSr9YprLaRNcCP+gjLRR/vvk+xUnTxm2TcZXqmG85zdrFb+BSHBNuShZWXOnZrNtuML5/h4hgyLRtyFT8Pamjs14Rs/wWcdcxIdIzBfn5uyh+i83Y/ASSXtgAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Developer flow with Kubernetes without App of Apps pattern and CD&quot;
        title=&quot;&quot;
        src=&quot;/static/e11ab1f9071e7b9cddfe5ac9ee855b9d/b5245/developer-flow.png&quot;
        srcset=&quot;/static/e11ab1f9071e7b9cddfe5ac9ee855b9d/c26ae/developer-flow.png 158w,
/static/e11ab1f9071e7b9cddfe5ac9ee855b9d/6bdcf/developer-flow.png 315w,
/static/e11ab1f9071e7b9cddfe5ac9ee855b9d/b5245/developer-flow.png 535w&quot;
        sizes=&quot;(max-width: 535px) 100vw, 535px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;After that, ArgoCD will fetch the manifests (Helm, Kustomize, pure YAML...) to create your application (because you pointed out where they were in the ArgoCD Application manifest) and it will apply them to be deployed in the cluster.&lt;/p&gt;
&lt;p&gt;There is nothing wrong with the above flow but we can improve it. The fact that we need to connect to the Kubernetes cluster and manually apply commands is prone to error. We can improve that and do that automatically. One way of solving this is to add all the needed steps in the CD pipeline but I&apos;m not a big fan of that because you are allowing an external agent (e.g. GitHub Actions runner) to connect directly to your cluster and apply manifest there, you already have a GitOps Agent (ArgoCD) so why not use it? We can use the &lt;code class=&quot;language-text&quot;&gt;App of Apps&lt;/code&gt; pattern to dynamically create new applications in the cluster. So the workflow will be something like this:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Create a pull request to add the ArgoCD Application manifest to the GitHub repository&lt;/li&gt;
&lt;li&gt;If approved by your team, merge the content to master/main branch&lt;/li&gt;
&lt;li&gt;App of Apps application will apply the new ArgoCD Application&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 538px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 72.15189873417721%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAOCAYAAAAvxDzwAAAACXBIWXMAAAsTAAALEwEAmpwYAAACOklEQVR42pVTSW/TQBj1L+TCmQMnDoVyAC6ckLigVuKAREOVAxIIqaoQDTRFuaBIRYDEUiJld+IkXmI7cRbHTuI1fsyM4yYpUMQnjWc8M9+b9958wy0WC9DIZjLY2b2P1Nu7ELs8m1tE8dpl4eVP4L0/XP5F4KIoYsMX6We4d+sanr/bgdaXMJ3OYJpj6LoOWZahKAo6nQ76/T5kUQQvKXBf7cG6eRWzJw/gZg8YDkc/ra6Gpy8PkCbt41mZLViWTUCnGI1GGI/HrA0GA/Y/oj05MDjNwb5xBfOH2wjPPseAruOgI8nIHGfx5ugIFZ5HEPgIw/BSqa7rwiHinC95WJ8+wKEWkRyOSmg0GugSSb1eD00yrlSrmM/nsY/EY2oLbYnfNNrtNjRVRb1D5IsS+HqNKeCSDUlS4ul6rM9TUJ6oEAQhXmOHrHK4daA/gSZj2lMfqQqH2PS3HO4Cld+YJfIoI9u2Nw7Y2JswXJ8INsjHxhuGwbxJWK17SncHYYSm6mPmJgyXJw3HJjr5Ywwn9rJsLCbR9/0N/y4yTOds3N4fYvf1BK4fxYDUVqUlQHx8B6rSxdCcsNuWJAnFYpFJVsmN1mo1FAoFMi9DaNbxsyRge99C7vsMW3sjlEV/xdDXRRiPriM0B0s2IZNMyycIgvPmeR7rHccldefh8HSOrZSJ1InFoM4BJ30dxrc8zKHxz/ebyJ2YJqqVIr7+KKFcKkLtKqtLUTUNLUWFTsrif0LTdFKXVVab9HX9AluDIQxMYJAJAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Developer flow with Kubernetes with App of Apps pattern&quot;
        title=&quot;&quot;
        src=&quot;/static/bdc2264c270e3cb372502b525ffe8a0b/9516f/developer-flow-gitops.png&quot;
        srcset=&quot;/static/bdc2264c270e3cb372502b525ffe8a0b/c26ae/developer-flow-gitops.png 158w,
/static/bdc2264c270e3cb372502b525ffe8a0b/6bdcf/developer-flow-gitops.png 315w,
/static/bdc2264c270e3cb372502b525ffe8a0b/9516f/developer-flow-gitops.png 538w&quot;
        sizes=&quot;(max-width: 538px) 100vw, 538px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Notice that the App of Apps pattern simply uses the ArgoCD Application to point to a place that contains other ArgoCD Applications. And you can use that to also organize your ArgoCD deployment in groups. You can create an App of Apps for all the applications deployed by the data team/machine learning team/devops teams...&lt;/p&gt;
&lt;p&gt;We still have a problem here: the chicken and the egg problem! Imagine a newly created cluster. I added the App of Apps manifest to the Git repository. Nothing is going to happen because this manifest was not applied to the cluster! This means that we need to apply once (or you can add this in a bootstrap pipeline) the app of apps application manifest. So the flow would be:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Create the App of Apps ArgoCD Application&lt;/li&gt;
&lt;li&gt;Apply the manifest in the cluster (manually/CD/bootstrap pipeline)&lt;/li&gt;
&lt;li&gt;ArgoCD creates the ArgoCD Application (App of Apps application)&lt;/li&gt;
&lt;li&gt;All the following ArgoCD Applications that the App of Apps application is aware (the source) will be automatically applied by it&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 72.15189873417721%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAOCAYAAAAvxDzwAAAACXBIWXMAAAsTAAALEwEAmpwYAAACrklEQVR42m1TSXLbMBDk/3+QQ16QQ45J5QGpOHHJtiRrJSlKXAFx36WY6vRA9i2smgIwGM7S3bCm6YZhvODXw2/Mnp7hHjzs9zbPDzh4HpTWWL6ucPJ9PM5mWMj+5OP5ZW7i58tXbHZ7k+PtbYKl8hphWsKNzsacUJvVDhQ2XghXzqGiT8OPNaqmhaoq7hV0lhsT35XJ2vEKS376/nOGJCsRSUBeIknva92PKLsBxyTF3lcIdYYo0dhlKdKmQ9N2JlnHOJ1XSIoa1p9XG5+//oAqK6yyGA0DqrpB2/W4XK5I6xYnld0TngucywZZ0SBO7/ucibOKMSyq2JQV6By7U2xGcjjqIU7h0mRdHQLMNi52fsKYBIH8pFNsMk2YMowX4jZNBjvBMG97WGXTm0pFO6DqRlNN9iWt4F1JX9EMxj58BWHor3/RDheDW82zzaYSdm3dbjfIF0URHNvGOAyIwhABWe27DnVdoywKZMQtjiMEQYCGvsAPoBRhYGzBe/kkl1XUHRTB3LpHPC3XcPwIL6stZosV1vYBe8/HfLPH2vGwWG3gByH8MMJ8sYTrHbG3HSRK4+3Gsa9kOaJkBDuDm8rvOCY5PLNPjclZ/KKENC+wKzJkxKsjcULeeJWxCR0JtE5Jhtn2gIgMJrqAT0aFMVlDYiIFRYuiS2E75lllFWIml8k08Rcti/xEVtbDcodPX75RQyXW55iYkaCyNPK58RW1rO5TCVuyLLIoyxp2nrLbgphNBruJ60T8GhH2kaM+rh0EDHApBU2NSdUz2XaCBC/sXjqUkb1YTONI4YuIPzp02P3T2sWJ95a0LviZJ0fb8/LDbEn0bjsKWzAsOYGXEw7qsR9G6vB21+L03qHik5HKR2L5f0vN6hlcU4pb40RICpLS9gMamhDiBjF5yPEPpfIVBCccOs0AAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;App of Apps Application&quot;
        title=&quot;&quot;
        src=&quot;/static/ff2c0269a72f066ea1f3030adb044ca5/f058b/app-of-apps.png&quot;
        srcset=&quot;/static/ff2c0269a72f066ea1f3030adb044ca5/c26ae/app-of-apps.png 158w,
/static/ff2c0269a72f066ea1f3030adb044ca5/6bdcf/app-of-apps.png 315w,
/static/ff2c0269a72f066ea1f3030adb044ca5/f058b/app-of-apps.png 630w,
/static/ff2c0269a72f066ea1f3030adb044ca5/4c42d/app-of-apps.png 896w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;In the end, the App of Apps pattern is just an ArgoCD Application that points to ArgoCD Applications and it looks like this:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; argoproj.io/v1alpha1
&lt;span class=&quot;token key atrule&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Application
&lt;span class=&quot;token key atrule&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; dev&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;app&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;of&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;apps&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;applications
  &lt;span class=&quot;token key atrule&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; argocd
&lt;span class=&quot;token key atrule&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;destination&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; in&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;cluster
    &lt;span class=&quot;token key atrule&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; argocd
  &lt;span class=&quot;token key atrule&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; argocd/applications/dev
    &lt;span class=&quot;token key atrule&quot;&gt;repoURL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; https&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;//github.com/felipelaptrin/argocd&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;blog.git
    &lt;span class=&quot;token key atrule&quot;&gt;targetRevision&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; main
  &lt;span class=&quot;token key atrule&quot;&gt;project&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; default
  &lt;span class=&quot;token key atrule&quot;&gt;syncPolicy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;automated&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;prune&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean important&quot;&gt;true&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;selfHeal&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean important&quot;&gt;true&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Some things to be aware of:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;spec.source.repoURL&lt;/code&gt;: is the link to the Git repository (source of the truth).&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;spec.source.path&lt;/code&gt;: is the path of the Git repository that the App of Apps Application will be &quot;aware&quot;.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;spec.source.targetRevision&lt;/code&gt;: is the branch of the Git repository that the App of Apps Application should take into consideration.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And the ArgoCD Applications will look like this:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; argoproj.io/v1alpha1
&lt;span class=&quot;token key atrule&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Application
&lt;span class=&quot;token key atrule&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; dev&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;dummy&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;app
  &lt;span class=&quot;token key atrule&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; argocd
&lt;span class=&quot;token key atrule&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;destination&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; cluster&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;dev
    &lt;span class=&quot;token key atrule&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; default
  &lt;span class=&quot;token key atrule&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; apps/dummy
    &lt;span class=&quot;token key atrule&quot;&gt;repoURL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; https&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;//github.com/felipelaptrin/argocd&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;blog.git
    &lt;span class=&quot;token key atrule&quot;&gt;targetRevision&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; main
  &lt;span class=&quot;token key atrule&quot;&gt;project&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; default
  &lt;span class=&quot;token key atrule&quot;&gt;syncPolicy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;automated&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;prune&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean important&quot;&gt;true&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;selfHeal&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean important&quot;&gt;true&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;Multi-Cluster Management&lt;/h2&gt;
&lt;p&gt;Before deep diving into multi-cluster management, it&apos;s important to understand an important concept in ArgoCD called &lt;code class=&quot;language-text&quot;&gt;AppProject&lt;/code&gt;. The AppProject specifies where (which cluster and namespace) ArgoCD Applications inside this AppProject can be deployed. ArgoCD default installation comes with an AppProject called &lt;code class=&quot;language-text&quot;&gt;default&lt;/code&gt; that allows all applications in this group to be installed in any namespaces.&lt;/p&gt;
&lt;p&gt;With that being said, imagine that we have a multi-environment infrastructure (dev, staging and production). We want to have completely isolated environments so each environment has its own Kubernetes Cluster. How can we use ArgoCD to help us deploy these applications in each environment? There are two very common solutions for that (none of these is better than the other, they will have pros and cons - there is no silver bullet!):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Single ArgoCD per cluster&lt;/strong&gt;: In this scenario every ArgoCD instance will deploy to its cluster, providing an extra layer of isolation but usually this will require more work time to manage.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 613px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 30.37974683544304%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAGCAYAAADDl76dAAAACXBIWXMAAAsTAAALEwEAmpwYAAABSklEQVR42j2RS07DMBRFu14YsBEqMWEB7IAJEwbMEAgmCFXlo9ImDSnNr3b8SdKmzcF2BZaebPn6Xp9nj74zxeOkIo5jmu0Wawxaa/xYF4r715LPzxlRktA01lUTtFIYnqY188USUSu6tsNay+hhIrm8sVRSsVmn9AMu0ATTy8eGi2vFT6lJoxndrscYG7T3SDpNk+QN1U/Ctt/Tti0jf+PO08QLFldjytoGwmEYkFLS7SHPcmKnrbOCttsGTQhJf4CiEs53TpausI5yZO2Rpk1mFONTDvsDtVLHQCFwwOzylPzijF6JQDkM7kxdB99O1+TjE/abDH0MPLagy5zs8ZbWEfs9H6hcsB9GlJTPd2gpaFxbXvvztUaTPdxiZeUIXcvGfYKveRTx9rUgc+39Pbyn8OskTZnO5iyXyX+QcbP35UURtHS1Cp/yC1e3woJMUT1PAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Single ArgoCD per cluster configuration&quot;
        title=&quot;&quot;
        src=&quot;/static/b171a7dfb9c6f50477b7f7d79796c7ac/5754a/single-cluster.png&quot;
        srcset=&quot;/static/b171a7dfb9c6f50477b7f7d79796c7ac/c26ae/single-cluster.png 158w,
/static/b171a7dfb9c6f50477b7f7d79796c7ac/6bdcf/single-cluster.png 315w,
/static/b171a7dfb9c6f50477b7f7d79796c7ac/5754a/single-cluster.png 613w&quot;
        sizes=&quot;(max-width: 613px) 100vw, 613px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Multi-Cluster single ArgoCD&lt;/strong&gt;: Here we have a single ArgoCD instance deploying to multiple Kubernetes clusters, leveraging features like SSO integration and RBAC. Ideally, you would have a separate cluster only for this ArgoCD (a completely separate control plane for managing this ArgoCD). But this is expensive and people usually use the production cluster to deploy this ArgoCD instance.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 548px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 106.32911392405065%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAVCAYAAABG1c6oAAAACXBIWXMAAAsTAAALEwEAmpwYAAACp0lEQVR42p1UW2sTQRTevyxaig+Cir6KlzcpbdVWfZKqYCvSomgpYlpj222um2A2m2yS7v2SvX/OmU3SpCi6DgxzPWe+c75vjoCCLcsyPqZphrdffax9cGC46eQMEIo6TNLc4ccfY1xf13B/y8KjN9bsrJDDKIowHJ7z+X4lxPK6gzsvbay893LURRG6rgtN09DpdKCdD7G508La9hA/ZRVKtwPLsv/dIaELgoCPvV4P58xxs36KVlOEbdtQFAWmaeYOJ3n+oyPP8/jl7NLFIAih6ebCnpBlxFCGJEmYcYw4jvl8akxoSqUSWq3WbC9LU84yddrKx7wLct/A3pGOkWbDcWyeJ0IThiE3JnS+7/8WfZxkCKJF1ML3uom7jClZS+HYOmzPZ2xlGI/HPGeGYSzobxq16aW498rCjacGKp3ogmXX9eD4GUZ+gvbGA7QPPoGwuSzRxgQp5ZHSkLJQ4yQX8esDDzc3DDzbZdJ5YVzo0PdyDY3Zve7DJYy+bPP1oN/nzLXbbZTLZT5SPvUJ4oYSY2nVwpXHBt598y8Q+n7ukCLxd54gbJb5mh7SdZ3nj8IlhHyfravVKtSejN39U3w+UqCqfdRqNSb6EQQigBoZH4tnaEgtJlCLh0khXpYL7ZFTQiqelNGonvBIPJZ7So8w1Rl1yzIxZpfJgAypqaqKSqWCwWCwqEM2j+KE2Y354zOW//ZDHMeBLMvodrtzTGcLzufXwuWD+XHxVwQ8innp9PUEdSVa1GGRGkiFgQoANakXY3nVwNUVHTuHE5bTgtWGUPbVAZ9vMR3eem5iY89lOrT+r3zx78aISOIQppvg9qaGawzhscQQpiGvBULR0k95FM/OUKuKODosodGUIHdaEEWR6XBYHOG0jUYjXoEkSWIFV54J/xeUgU7YvRHcfAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Single ArgoCD per cluster configuration&quot;
        title=&quot;&quot;
        src=&quot;/static/e396bcdd13f8c9c5dd8d6ee84578d81e/a58fe/multi-cluster-single-instance.png&quot;
        srcset=&quot;/static/e396bcdd13f8c9c5dd8d6ee84578d81e/c26ae/multi-cluster-single-instance.png 158w,
/static/e396bcdd13f8c9c5dd8d6ee84578d81e/6bdcf/multi-cluster-single-instance.png 315w,
/static/e396bcdd13f8c9c5dd8d6ee84578d81e/a58fe/multi-cluster-single-instance.png 548w&quot;
        sizes=&quot;(max-width: 548px) 100vw, 548px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;These are only two common configurations that we see DevOps Engineers doing. Of course, there are several other possible configurations (e.g. multiple ArgoCD instances that control multiple deployment clusters).&lt;/p&gt;
&lt;h3&gt;Managing external clusters&lt;/h3&gt;
&lt;p&gt;If we want to go for the single ArgoCD per cluster configuration we need to create some resources on the cluster that has ArgoCD installed and the external cluster (cluster that does not have ArgoCD installed and will be managed by ArgoCD).&lt;/p&gt;
&lt;p&gt;On the &lt;strong&gt;external cluster&lt;/strong&gt; we are going to:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Create a &lt;code class=&quot;language-text&quot;&gt;ServiceAccount&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; v1
&lt;span class=&quot;token key atrule&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ServiceAccount
&lt;span class=&quot;token key atrule&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; argocd&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;manager
  &lt;span class=&quot;token key atrule&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; kube&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;system
&lt;span class=&quot;token key atrule&quot;&gt;secrets&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; argocd&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;manager&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;token&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;Create a &lt;code class=&quot;language-text&quot;&gt;Secret&lt;/code&gt; of type &lt;code class=&quot;language-text&quot;&gt;kubernetes.io/service-account-token&lt;/code&gt; and link it with the &lt;code class=&quot;language-text&quot;&gt;ServiceAccount&lt;/code&gt; created&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; v1
&lt;span class=&quot;token key atrule&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Secret
&lt;span class=&quot;token key atrule&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; kubernetes.io/service&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;account&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;token
&lt;span class=&quot;token key atrule&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;annotations&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;kubernetes.io/service-account.name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; argocd&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;manager
  &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; argocd&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;manager&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;token
  &lt;span class=&quot;token key atrule&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; kube&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;system&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;Associate a &lt;code class=&quot;language-text&quot;&gt;ClusterRole&lt;/code&gt; with required ArgoCD permissions (you can use &lt;code class=&quot;language-text&quot;&gt;cluster-admin&lt;/code&gt; instead) using a &lt;code class=&quot;language-text&quot;&gt;ClusterRoleBinding&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; rbac.authorization.k8s.io/v1
&lt;span class=&quot;token key atrule&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ClusterRole
&lt;span class=&quot;token key atrule&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; argocd&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;manager&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;role
&lt;span class=&quot;token key atrule&quot;&gt;rules&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;apiGroups&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;*&apos;&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;resources&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;*&apos;&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;verbs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;*&apos;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;nonResourceURLs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;*&apos;&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;verbs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;*&apos;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; rbac.authorization.k8s.io/v1
&lt;span class=&quot;token key atrule&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ClusterRoleBinding
&lt;span class=&quot;token key atrule&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; argocd&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;manager&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;role&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;binding
&lt;span class=&quot;token key atrule&quot;&gt;roleRef&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;apiGroup&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; rbac.authorization.k8s.io
  &lt;span class=&quot;token key atrule&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ClusterRole
  &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; argocd&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;manager&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;role
&lt;span class=&quot;token key atrule&quot;&gt;subjects&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ServiceAccount
  &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; argocd&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;manager
  &lt;span class=&quot;token key atrule&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; kube&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;system&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;When the secret gets created you can inspect its value (&lt;code class=&quot;language-text&quot;&gt;kubectl get secret argocd-manager-token -n kube-system -o yaml&lt;/code&gt;), because the cluster that contains ArgoCD installed will need to know the &lt;code class=&quot;language-text&quot;&gt;ca.crt&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;token&lt;/code&gt; value. Since these are secret values we will create a &lt;code class=&quot;language-text&quot;&gt;Secret&lt;/code&gt; and add the label &lt;code class=&quot;language-text&quot;&gt;argocd.argoproj.io/secret-type&lt;/code&gt; (with value &lt;code class=&quot;language-text&quot;&gt;cluster&lt;/code&gt;) on the &lt;strong&gt;management cluster&lt;/strong&gt;, so ArgoCD will understand that this secret is related to an external cluster.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; v1
&lt;span class=&quot;token key atrule&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Secret
&lt;span class=&quot;token key atrule&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Opaque
&lt;span class=&quot;token key atrule&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;labels&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;argocd.argoproj.io/secret-type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; cluster
  &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; cluster&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;dev
  &lt;span class=&quot;token key atrule&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; argocd
&lt;span class=&quot;token key atrule&quot;&gt;stringData&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token scalar string&quot;&gt;
    {
      &quot;bearerToken&quot;: &quot;eyJhbGciOiJSUzI1NiIsImtpZCI6InNJZlBrMXkxTkVpM1EtZzBKYmdsd0JldjBQZ1JBWktxQ1BmNHRjTmstMTQifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJrdWJlLXN5c3RlbSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJhcmdvY2QtbWFuYWdlci10b2tlbiIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50Lm5hbWUiOiJhcmdvY2QtbWFuYWdlciIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50LnVpZCI6IjI2ZTc4NDBhLTg2M2UtNDE4YS1iNzZhLTk2YjE3YzhiYmQwYyIsInN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDprdWJlLXN5c3RlbTphcmdvY2QtbWFuYWdlciJ9.UcSVUwaTMf91V7S8p-8V1DCnXcDXodvtyeXeLkWEkTYQjtDk52SNJE2xofBQhNUVKLoIivSzX5mBcFpgS2V-iLOrqv-4Y9A47OBwXx-fNWkCifquz0c7hnqilGOYW6fI9X5PrmTL4-f6VVddmPX0vYbC-o-rf-G5HXbXJUgggTTZ7BKSHApRFNov8mX6RYxjMyVnNbYGHSIDnA8TtPpiibt13onOAZULnv-jDj23PVqtkk-RvxeqvtVNkyxxF4jpQzHF9uVIvOB-GDGj7iuDXZG-_CdTTar02dA9hLTpX6cn1gDhsq6iNGEMrLJtjZUKJJedyQHytG5QDu5H-VioCg&quot;,
      &quot;tlsClientConfig&quot;: {
        &quot;insecure&quot;:false,
        &quot;caData&quot;:&quot;LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURCVENDQWUyZ0F3SUJBZ0lJYmYxMDRmNUZYLzB3RFFZSktvWklodmNOQVFFTEJRQXdGVEVUTUJFR0ExVUUKQXhNS2EzVmlaWEp1WlhSbGN6QWVGdzB5TkRBME1qTXlNRFUxTURkYUZ3MHpOREEwTWpFeU1UQXdNRGRhTUJVeApFekFSQmdOVkJBTVRDbXQxWW1WeWJtVjBaWE13Z2dFaU1BMEdDU3FHU0liM0RRRUJBUVVBQTRJQkR3QXdnZ0VLCkFvSUJBUUR5RmxvZjBSN0lIWUI1cWRBVGFhNnc0eXN2MlNwekdEdk5oM3I2UnluL1FhZEtvYkUrblF3Q3BVV1gKTHJ6NVNJYlYvSWlWUGV0WEliVjBVTktEdXRxaW0zajRzaDd4cUd6MmlQeU5JU1p6N3RqUGg5YXVkcVdvbzlFcQpQcWR2eXpHd1ZncDVCYytKaW1kTGlGOVFTSXVOMnFMMTAyUjdjVHNBZ2Q3QjcwWjVuWkhhVHd1eWw4QmNHVGw4ClFla0lyZTBUYm9ndGJzaGdWQkZxejMxM0hEaDJadExSYkEzN3VHZjY0NGdVdkNnWUpQQjV1cEZrcFN5dGZodUgKUG9TSkFTYkNablNRR1IrV1d3YzJuVVZPZ0plU3BWZTZRMks0eUR5azVqTWdEc2wzVVRwL0JSTDh6aXhMTXVjeApXTkFtOEo1RWdTOVJGK2VrcFFISFdVa05SeHRQQWdNQkFBR2pXVEJYTUE0R0ExVWREd0VCL3dRRUF3SUNwREFQCkJnTlZIUk1CQWY4RUJUQURBUUgvTUIwR0ExVWREZ1FXQkJTRWd3NnQ3akJ0VHpUWjhnU2xwTVpUaHJhc0xqQVYKQmdOVkhSRUVEakFNZ2dwcmRXSmxjbTVsZEdWek1BMEdDU3FHU0liM0RRRUJDd1VBQTRJQkFRQzlZcXUzRTdnOQpaZ2VkL3FTN052cmtGK05taUpKUXE3VStka3BuU0N1V3BiZVN5ZWZWaGZ6U1p6bE9sTFRsUFN0NFNnKzRqUnJRCjdrdDFsM0FHcEtJdHJob0sycUZWZktYZmMyc0RER2ovZEJTMFpUSGw5ZEJGVDNwZjMxSU9XQ1o4SFg3M080U0wKN0s1bXhIek1NYnRWbGZNZTlNckJUUVlMQzNFK0RoSy81RW80VkhCV1p2SnBDOTFlVWdSNTUyT1YwV2xtNmpTZQoyekRMc1hjVzhvQk43dytZNzYvNWUwVWJCSXpENUlEU3owZ1ZWa05od0trR3lXS3luWmljcG5kY2ltZE51SDhhCm9IQVptSEM3bytObk9weWV4dmJFcFEzRmZPa0wxODN3ckJORWo1T1dUOHFoakd2YmVSWW5RaER3MVQ2aHRFOGEKdWNKUG11UkFkK2VtCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K&quot;
      }
    }&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; cluster&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;dev
  &lt;span class=&quot;token key atrule&quot;&gt;server&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; https&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;//cluster&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;dev&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;control&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;plane&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;6443&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Some points that I would like to comment on:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;bearerToken&lt;/code&gt;: The value that is being used is the base64 decoded value of the output of the &lt;code class=&quot;language-text&quot;&gt;kubectl get secret argocd-manager-token -n kube-system -o yaml&lt;/code&gt; command.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;server&lt;/code&gt;: We are using two Kind clusters and since it works with docker under the hood I&apos;m leveraging the fact that Kind creates a docker bridge network to run the clusters. That&apos;s why I can reference the dev cluster (the one that does not contain ArgoCD installed) by its name. In case you are still confused run &lt;code class=&quot;language-text&quot;&gt;docker inspect cluster-dev-control-plane&lt;/code&gt; and check the network section.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;caData&lt;/code&gt;: That&apos;s the exact value of &lt;code class=&quot;language-text&quot;&gt;ca.crt&lt;/code&gt; field returned by the &lt;code class=&quot;language-text&quot;&gt;kubectl get secret argocd-manager-token -n kube-system -o yaml&lt;/code&gt; command.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;ApplicationSet&lt;/h2&gt;
&lt;p&gt;When using an ArgoCD Application we create a single deployment in a given cluster. What would happen if we have multiple Kubernetes clusters or different namespaces? We would have to create a single ArgoCD Application for each deployment, which can be repetitive and prone to error. In these cases, we can use an &lt;code class=&quot;language-text&quot;&gt;ApplicationSet&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;An ApplicationSet automates the creation of multiple ArgoCD Applications at once using a template. The easiest way to comprehend this is to simply see the manifest:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; argoproj.io/v1alpha1
&lt;span class=&quot;token key atrule&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ApplicationSet
&lt;span class=&quot;token key atrule&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; common
  &lt;span class=&quot;token key atrule&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; argocd
&lt;span class=&quot;token key atrule&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;generators&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;elements&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; dev
        &lt;span class=&quot;token key atrule&quot;&gt;cluster&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; cluster&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;dev
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; prod
        &lt;span class=&quot;token key atrule&quot;&gt;cluster&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; in&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;cluster
  &lt;span class=&quot;token key atrule&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;{{env}}-common-app&apos;&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; argocd
    &lt;span class=&quot;token key atrule&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;project&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; default
      &lt;span class=&quot;token key atrule&quot;&gt;destination&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;{{cluster}}&apos;&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; default
      &lt;span class=&quot;token key atrule&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; apps/common
        &lt;span class=&quot;token key atrule&quot;&gt;repoURL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; https&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;//github.com/felipelaptrin/argocd&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;blog.git
        &lt;span class=&quot;token key atrule&quot;&gt;targetRevision&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; feat/generator
      &lt;span class=&quot;token key atrule&quot;&gt;syncPolicy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;automated&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;prune&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean important&quot;&gt;true&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;selfHeal&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean important&quot;&gt;true&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Notice that the &lt;code class=&quot;language-text&quot;&gt;spec.template&lt;/code&gt; section is a templated ArgoCD Application that replaces the variables with the corresponding variable in the list of generators (&lt;code class=&quot;language-text&quot;&gt;spec.generators&lt;/code&gt;). There are several generators that you can use, such as &lt;code class=&quot;language-text&quot;&gt;list&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;git&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;cluster&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;matrix&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;merge&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;pull request&lt;/code&gt;... I don&apos;t think it&apos;s worth it to deep dive into all of them in this blog post, in case you need more info you can directly check in the &lt;a href=&quot;https://argo-cd.readthedocs.io/en/stable/operator-manual/applicationset/Generators/&quot;&gt;documentation&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Releases Promotion&lt;/h3&gt;
&lt;p&gt;A golden rule of DevOps is to never deploy directly to production before testing this in other environments. It&apos;s likely to have more than one environment, such as dev, staging, qa, production... Different companies will have different environments, some will have only dev and prod, others dev, staging and prod, and so on. But the important thing to understand is that first we deploy to one environment, check how it goes and only after we validate we can deploy to the following environment.&lt;/p&gt;
&lt;p&gt;How can we organize our repository (source of truth) to be a well-organized multi-environment GitOps repository? There are three common approaches to this question:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Environment per folder&lt;/strong&gt;: Each folder of the repository represents an environment, i.e. the &lt;code class=&quot;language-text&quot;&gt;dev&lt;/code&gt; folder contains all the manifests related to the development environment and so on. This is a really good and highly recommended approach.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 53.16455696202532%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAAsTAAALEwEAmpwYAAABkklEQVR42qVSO08cQQze3xwpNU2atEmXiiYRZYhSIqUAQgQFp+MC4g6hWyHY1+zsY/Yxz/3w7N2BQFeBpdGMx/Zn+7MDvEGGYaDjXw4JV9g9aLG4N6MteA8gZzH+TSN8+Jbjz6Qmi10Djg4D8jxHURQoy3LUN8GvpW1bhGEIxhg4xcyubpFmDKKuEQyNAGQP6wza6C+BcfQ96da+ALFuoOPWiTmapnmySW2fEgfd54/Qv/fgwy07RcVC9FITaAfOOSqqtrcEMp8hPT9BowxVUkEpjcFp3GUKX38JXCzVClDs/0B7dgTt2+sjyPSYMgJaSUgp6VZEPVDPp6gnRyiaDmkSI6MWC55hcp3h016Bk0tfsUHAa4GcquikGQHb6BApq2C0esFhVVUjv14XQsAYA6U1WJZiMp0hjqLxn4ayIZ9aNhJG1mM7zrmt0/Xi+VsuQ/JbJa0aBWPd89psm+T2VXn2c/RuRYmrUGDnu8DZXL1vD71o1eH/TYIvPzOcX5dwVr4NcCOexySJsFjMEccP6LoOj7hpUmlv9yXLAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Environment per folder diagram&quot;
        title=&quot;&quot;
        src=&quot;/static/b74624a290ea12736fd47532324bf9ba/f058b/environment-per-folder.png&quot;
        srcset=&quot;/static/b74624a290ea12736fd47532324bf9ba/c26ae/environment-per-folder.png 158w,
/static/b74624a290ea12736fd47532324bf9ba/6bdcf/environment-per-folder.png 315w,
/static/b74624a290ea12736fd47532324bf9ba/f058b/environment-per-folder.png 630w,
/static/b74624a290ea12736fd47532324bf9ba/40601/environment-per-folder.png 945w,
/static/b74624a290ea12736fd47532324bf9ba/8ecb0/environment-per-folder.png 989w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;&lt;strong&gt;Environment per repository&lt;/strong&gt;: Each git repository represents a single environment. This is a more isolated scenario, especially when you only want a special group to be able to deploy in production. When using this approach, it&apos;s common to have only two repositories, one for non-production environments and another one for production environments (that only a special team can work with).&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 55.06329113924051%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAAsTAAALEwEAmpwYAAAB00lEQVR42pVTu27UUBD1z/IBNFQU0FAgKopUdCyioYCkjYBQsKJKlEUpiETI7ma9vl6/ru/7dTK7doJAgMJYI2vuHc+ZM3OcJSVg3x8gsBV2ltLgd7KEaB1eHyl8mpndSRYX36Ef3kM4/owoFZK1dy6cpECzYng8KTE5rJGCRdZ2HMvZCVieI4SA2PcIXUeX4dfCKdJrANjlVBW8Meh6josfc6wLhp7OsxAjOs7hvf+JrBUifZCkHIkB/qZbYpCaGqCcG5Mmwro0Uq4Y3ItncOdnEHGbHyB9gnIBtmnhGg5Rb5B/+QitHVTVglMOtxHaRXAd8Xxf4O10AMjs1SXk0wfgs2Ns7BbNo5UeglAldV0pCZHPsTl8Q0U8il6iFp48kFvM1y329gscTGt4p5B1xPtisQTbUhxpueigvYIiT/RYotnQrCONxyePzlLn0exiKTgWl+co2RUEjSjbFujLAn7criSUWrdUzPx1u53pkQsGG9wuXlURTR/HGRJl9+Q+9OkUZRAwTv9ZIr9JaBtz24HRlh+94ph8EENBwdYoX+6h+vYV7lauCemfch5uQ/BYsRzvjpaYnhZQkmSjtMaCNNTy/j/+ENzKSdHc2HqJasNgjME1RaFNuVfV8S0AAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Environment per repository&quot;
        title=&quot;&quot;
        src=&quot;/static/be45e84e19ade7b859e4ee62084d2687/f058b/environment-per-repository.png&quot;
        srcset=&quot;/static/be45e84e19ade7b859e4ee62084d2687/c26ae/environment-per-repository.png 158w,
/static/be45e84e19ade7b859e4ee62084d2687/6bdcf/environment-per-repository.png 315w,
/static/be45e84e19ade7b859e4ee62084d2687/f058b/environment-per-repository.png 630w,
/static/be45e84e19ade7b859e4ee62084d2687/40601/environment-per-repository.png 945w,
/static/be45e84e19ade7b859e4ee62084d2687/84a90/environment-per-repository.png 982w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;&lt;strong&gt;Environment per branch&lt;/strong&gt;: It&apos;s very similar to Git flow. Each branch corresponds to a given environment, i.e. the &lt;code class=&quot;language-text&quot;&gt;develop&lt;/code&gt; branch deploys to the dev environment and so on. This is an approach that is NOT recommended. There are several problems using this approach such as: pull requests and merges between branches are painful and very problematic (git conflicts, unwanted changes, wrong order of changes...), having a large number of environments requires a lot of maintenance, it&apos;s easy to create environment-specific code, creating drifts between environments, this approach per branch goes against Kubernetes ecosystem (such as Helm/Kustomize).&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 53.16455696202532%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAAsTAAALEwEAmpwYAAAB70lEQVR42oWS22rUUBSG55HFJ/BCfAK980ZQ8cILoeAB9EIrKBWL1NHBoQdt55RkkiZ757CP+VwzHa3WFhckgbV//vXvL2vAr+r79Semmrj7kV6nf/UvVr/u91TKce9Vw+jYrfuDP0W5rmi+HMPrHWJTEpb5labrTpExHs25fnfJiw+VNLwYbsQhBBZpwnx8RDFJUF1LyHP6uj632Gh7awmTCW2SkJenDEeHJEmK1koMxWhVVSXpmhrnHLbr8M7Se0/MMhnszlPJOxYyqCp/pzVOdJthA7v1EPvpPXmlcH0gzCr67W2iSjGiMdbjdEOrFMl4iGsMptS0kiPEyOI0cGdLs7tvzwzrm9donj1GtR1lo+lGJ/Buj04OF6eGTKAXradOZhTP71PKgHnVkipPpjuG3zW3HhW82avPGC5nE5bCopSEPohotiDPC9SKnVwjREHQW4zgWByM5WoR5RQ2GsFjWWYJn4cjZtOpMNQMVrFXK2CMWXP0RgwEdDQdrTckdbY+j7HHCtNVddL/UU5pXHvGv7Z4Hy5ZG1WixlN4+pKmOCS3+so9FH+aoDhIa2480Lz9av81jPK0R3Ps3gGt8AjBX77Um//tfMe3k5TbTzJ2RiVRkg8uimulKYpCVqjhf+UFQZYs2N8fM59NaduWn9WwTRbq0j3oAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Environment per branch&quot;
        title=&quot;&quot;
        src=&quot;/static/48c87db3aa870ec1266534daf8e53fdc/f058b/environment-per-branch.png&quot;
        srcset=&quot;/static/48c87db3aa870ec1266534daf8e53fdc/c26ae/environment-per-branch.png 158w,
/static/48c87db3aa870ec1266534daf8e53fdc/6bdcf/environment-per-branch.png 315w,
/static/48c87db3aa870ec1266534daf8e53fdc/f058b/environment-per-branch.png 630w,
/static/48c87db3aa870ec1266534daf8e53fdc/40601/environment-per-branch.png 945w,
/static/48c87db3aa870ec1266534daf8e53fdc/75609/environment-per-branch.png 994w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Of course, some companies might use a combination of these approaches, but in general, these are the most commonly used ways to have a GitOps repository.&lt;/p&gt;
&lt;h3&gt;CI integration with GitOps&lt;/h3&gt;
&lt;p&gt;The CI (Continuous Integration) pipeline is usually responsible for linting, running tests, checking code coverage, building the docker image, push the docker image to a registry (such as DockerHub or EKS). How does this integrate with GitOps?&lt;/p&gt;
&lt;p&gt;Remember one of the most important GitOps principles is that everything needs to be declaratively stored (commonly) in a Git repository. Our CI must then make modifications in our repository, i.e. committing changes. There are two common ways of achieving that: asynchronously (which is a pure GitOps way of doing it) and synchronously.&lt;/p&gt;
&lt;h4&gt;Async GitOps pipeline&lt;/h4&gt;
&lt;p&gt;Using an async GitOps pipeline means that ArgoCD Applications have auto-sync enabled, which means that if a change is detected in the manifests it will auto-apply this change (remember that the repository is considered the source of truth).&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 44.30379746835443%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAYAAAAywQxIAAAACXBIWXMAAAsTAAALEwEAmpwYAAABiUlEQVR42n2RX2/TMBTF+8n5BohHJCRAvE5IaEhIG5sKmobWB+jWaaqyhbZbSRlJ47hO2vxpHP9w0gyG1HKV6DrnHp8TH3fA2Kdia60SzMSBdLX5NmY7r8bbWecx0dhurLipNLVFLgJ+9U7IbDfNXDfz/wpW3oT1wVtMkZPnOd+cO3rDKQNnzOfBmP71bdOH36d8sbgz9lhbbpqmlp9RWpFyeMH6eH8jmPbPWDx7wkrMUQvJfu+GvbMRn/our46vOD2/5vnBJacXLm9ORnS/Oigp8H2fSAjCeIk8fEfy8imFNegopQjtIM0yqqoikqp9JfNQIITEdUfNOowkcqH+OW29L7RcbzZDxbHN8FHQZkvoNRJGUWO2q7TWzK1Zc+TNRZh26998G7wGiozlvUdp802SJUYXdAc5R+cZuqxzL1iXJfPJDaUV7uy0bf9Wq4ii+57kfsZPPyBPY14fSl58EMQL0USQ2QsKPu5RWuHdgm0lSUIwcokCn4dAPO8H0+ntn4i0FQq9u6b/Bn0Qr1cPwy2lAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Async GitOps pipeline&quot;
        title=&quot;&quot;
        src=&quot;/static/140a13fe0c616d27c07bc774bfb5be2f/f058b/async-ci.png&quot;
        srcset=&quot;/static/140a13fe0c616d27c07bc774bfb5be2f/c26ae/async-ci.png 158w,
/static/140a13fe0c616d27c07bc774bfb5be2f/6bdcf/async-ci.png 315w,
/static/140a13fe0c616d27c07bc774bfb5be2f/f058b/async-ci.png 630w,
/static/140a13fe0c616d27c07bc774bfb5be2f/40601/async-ci.png 945w,
/static/140a13fe0c616d27c07bc774bfb5be2f/67fe0/async-ci.png 1101w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;So here is the flow:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Tests run to ensure code quality&lt;/li&gt;
&lt;li&gt;The docker image is created&lt;/li&gt;
&lt;li&gt;The docker image is pushed to the registry (e.g. Dockerhub, ECR)&lt;/li&gt;
&lt;li&gt;The new docker image tag is committed in the manifests repository&lt;/li&gt;
&lt;li&gt;ArgoCD detects that there is a change in the repository and will apply the changes in the application (remember that auto-sync needs to be enabled). Notice that this is an async event.&lt;/li&gt;
&lt;li&gt;Changes are applied in the Kubernetes cluster&lt;/li&gt;
&lt;li&gt;Kubernetes cluster pulls new docker image from registry&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;Sync GitOps pipeline&lt;/h4&gt;
&lt;p&gt;Some companies rather have a more synchronous approach, where developers can see the end-to-end process of deployment (because in the previous scenario, you would have to open ArgoCD to check if a new deployment was finished). This pipeline also allows us to perform actions after the deployment is done such as running smoke tests, sending a Slack message, and executing a script (this can also be achieved using ArgoCD Posy-Sync hooks/sync waves or a pipeline tool such as Argo Workflows - more on that later).&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 46.835443037974684%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAYAAAAywQxIAAAACXBIWXMAAAsTAAALEwEAmpwYAAABqElEQVR42oWS208TURDG+6f7ZPwPjO8mvsiDLwhYpMiDwbZcBKy0CxWltux995y9X37OniKJIZFJZk92Zs53vvlmerQtf12+/1p7H3FXtLMLKAuqpqGua8qyoijkv1qfjcQ76xkgKVzfbymyhFTH6DhCxSFporDnFvPRZ+5uf7FaLgg8B99ziXyXNMsIPQ8tMQNYX03IXz6nlKTv+7wfX7N3vmBrOGVrNGN7bLF9/IMPZ7dsjq4YSG4wsdk/u2Hj4JyOSrOzQfbqxRowWfzEf/eGTBjlecbuscXg9JqPRzN2j6b0x5fsjC7ZP7FMbk9883BC/8s3iU/xggD3+wX24QGRUgKYpuiiJBcdupYrAS3Srm1Flmh818a5W5KnmkRFJq7CAHu1NC13GpaiqS3AnY49zCgejePBtE7ENY/n1aKUJhZWWh6JwvB+KE9YLsVJHBsmZrpyfp2X2GHVqSexhkDAXGHcPgkoLJrhJ/L5FDeMZLIOK1fx7LUv2gakyieKFYXI5PTfyqP1/wEL2TVPVsYRr8yetcKoYnhq8XvpPtTVwtq7sUwHfwCYkarkV1cXEQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Sync GitOps pipeline&quot;
        title=&quot;&quot;
        src=&quot;/static/d94e56cd4242ab23be21d0848ec62b6a/f058b/sync-ci.png&quot;
        srcset=&quot;/static/d94e56cd4242ab23be21d0848ec62b6a/c26ae/sync-ci.png 158w,
/static/d94e56cd4242ab23be21d0848ec62b6a/6bdcf/sync-ci.png 315w,
/static/d94e56cd4242ab23be21d0848ec62b6a/f058b/sync-ci.png 630w,
/static/d94e56cd4242ab23be21d0848ec62b6a/40601/sync-ci.png 945w,
/static/d94e56cd4242ab23be21d0848ec62b6a/8802b/sync-ci.png 1135w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;The flow is similar to the previous one but now we monitor and run commands in ArgoCD:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Tests run to ensure code quality&lt;/li&gt;
&lt;li&gt;The docker image is created&lt;/li&gt;
&lt;li&gt;The docker image is pushed to the registry (e.g. Dockerhub, ECR)&lt;/li&gt;
&lt;li&gt;The new docker image is committed in the manifests repository&lt;/li&gt;
&lt;li&gt;Using ArgoCD CLI the CI forces the ArgoCD Application to sync (remember that auto-sync is disabled).&lt;/li&gt;
&lt;li&gt;Changes are applied in the Kubernetes cluster&lt;/li&gt;
&lt;li&gt;Kubernetes cluster pulls the new docker image from the registry&lt;/li&gt;
&lt;li&gt;We monitor the status of the ArgoCD Application until it gets healthy. If it doesn&apos;t get healthy the CI pipeline fails and the DevOps team needs to troubleshoot the error.&lt;/li&gt;
&lt;li&gt;Extra steps can be performed after a successful deployment (e.g. send a Slack message to the team to let them know that a new release was deployed).&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;ArgoCD Image Updater&lt;/h4&gt;
&lt;p&gt;The pipelines presented above are usually different from company to company depending on the needs of the company. Extra steps can exist and tools can change. A common example of that is to use special tools for updating the image tag of the applications such as &lt;a href=&quot;https://argocd-image-updater.readthedocs.io/en/stable/&quot;&gt;ArgoCD Image Updater&lt;/a&gt;. Be aware that in the period that I&apos;m writing this blog post (April 2024), this tool is still under active development, with possibly a lot of breaking changes from release to release and should be tested on non-critical environments.&lt;/p&gt;
&lt;p&gt;ArgoCD Image Updater works by monitoring new image tags in the registry and committing the image tag change in the repository.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 15.18987341772152%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAADCAYAAACTWi8uAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAxElEQVR42i2PTU/CQBCG+6f9Ef4OTg0XQ8JNbyacuJgYA6EfgKit3e1aS9l2t32cBefyzjtvZvJMhJR7XWOXMd5emMR3XUfTNCFi6HtMUXBWiqYs8c7d5sOA9/7aT6ecqVZM4qMwsA8z7P0d9fseZX7IspRkt0MbQ5GmbOZzXuKY/WKBOh5RdU2SJORZxtd3xe/zI271xDiORFrCUgiqQ34Ne2tp2xYjxwLtKESd1rRVxUXU/ZOF5aBn+ab4OPG5fUMrxR/IUOD55/bdpAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;ArgoCD Image Updater flow&quot;
        title=&quot;&quot;
        src=&quot;/static/c6b9cb7ffe3df6ec5bbd93f5adf5ef7a/f058b/argocd-image-updater.png&quot;
        srcset=&quot;/static/c6b9cb7ffe3df6ec5bbd93f5adf5ef7a/c26ae/argocd-image-updater.png 158w,
/static/c6b9cb7ffe3df6ec5bbd93f5adf5ef7a/6bdcf/argocd-image-updater.png 315w,
/static/c6b9cb7ffe3df6ec5bbd93f5adf5ef7a/f058b/argocd-image-updater.png 630w,
/static/c6b9cb7ffe3df6ec5bbd93f5adf5ef7a/40601/argocd-image-updater.png 945w,
/static/c6b9cb7ffe3df6ec5bbd93f5adf5ef7a/36c33/argocd-image-updater.png 946w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Some important things to know about this tool:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It only works integrated with ArgoCD&lt;/li&gt;
&lt;li&gt;Your manifests need to be written using Kustomize or Helm Chart&lt;/li&gt;
&lt;li&gt;By default, the image updater will not manage the image update unless you explicitly tell him to do (configuring labels)&lt;/li&gt;
&lt;li&gt;You can specify image version constraints based on the application&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Sync Operations&lt;/h2&gt;
&lt;p&gt;As we commented before, a common scenario is the need to run actions (e.g. database migration, sending email/message, running integration tests...) before and after the deployment. We can easily achieve this using Sync Phases and/or Sync Waves.&lt;/p&gt;
&lt;h3&gt;Hook Phases and Lifecycle&lt;/h3&gt;
&lt;p&gt;There are five (5) sync phases. They are executed in the following order:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;PreSync&lt;/strong&gt;: Executed before syncing the ArgoCD Application.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Sync&lt;/strong&gt;: Executed at the same time as the ArgoCD Application sync.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Skip&lt;/strong&gt;: Tells ArgoCD to not apply the manifest.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;PostSync&lt;/strong&gt;: Executed after the success (healthy state) of the sync operation.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SyncFail&lt;/strong&gt;: Executed after the failed sync operation.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 43.67088607594937%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAYAAAAywQxIAAAACXBIWXMAAAsTAAALEwEAmpwYAAABNklEQVR42pVR226CQBTk/7/EN41GjW9eY4yJCeqLFwQKSLkvDRQowvQsoQ22vrDJBLJnds6ZOQJanrIsURZF9c8cB/fVCsZyCWuxgHe9QuDFisC/RP4nUNd/UXOyPMdhu4U5GkHqdqESHFGE0HbCJEmgaRpkWUaapoh9H7HnIXJdJFEEwTkewc5nsNOpuvixxQ9/4F8ucA4H+JxHIqZlwbbtqvbKkfDe6+Hc6cAfj6GTsGGaeFNVyIoCcbfDx3QKg+wEwyHs2Qza/f7U9G8cQnC7IaDOjARSssMvi8eDIi2QU06hroNxDiGiJQSMISQ0RZ8mbJsht6pQc0mS8JVlSEg8C0N8UpZJHJMg71Lj5ZYb9SbHpUXs1mvYkwmUfh/mYAB3v28/YdOqS3k7mw20+RweifNovgEMnLIU7dPBrAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;ArgoCD Sync Phases&quot;
        title=&quot;&quot;
        src=&quot;/static/21a13987fcbd6cac1cb79cf21999e382/f058b/sync-phases.png&quot;
        srcset=&quot;/static/21a13987fcbd6cac1cb79cf21999e382/c26ae/sync-phases.png 158w,
/static/21a13987fcbd6cac1cb79cf21999e382/6bdcf/sync-phases.png 315w,
/static/21a13987fcbd6cac1cb79cf21999e382/f058b/sync-phases.png 630w,
/static/21a13987fcbd6cac1cb79cf21999e382/0f79a/sync-phases.png 689w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;When configuring hooks we can define a deletion policy, i.e. when we want this hook to be deleted. The are three (3) deletion policies:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;HookSucceeded&lt;/strong&gt;: The hook is deleted after the hook succeeded.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;HookFailed&lt;/strong&gt;: The hook is deleted after the hook fails.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;BeforeHookCreation&lt;/strong&gt;: Any existing hook resource is deleted before the new one is created.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There is nothing &quot;special&quot; with the hook, it&apos;s just a Kubernetes resource that will run in a given period (sync phase) and the lifecycle is controlled by ArgoCD. We configure all of this in the annotations of the resource (&lt;code class=&quot;language-text&quot;&gt;metadata.annotations&lt;/code&gt;). Let&apos;s check one example of a hook that runs before the sync (PreSync) and should be deleted (to not run again in the future) if it ran successfully:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; batch/v1
&lt;span class=&quot;token key atrule&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Job
&lt;span class=&quot;token key atrule&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;generateName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; database&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;migration
  &lt;span class=&quot;token key atrule&quot;&gt;annotations&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;argocd.argoproj.io/hook&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; PreSync
    &lt;span class=&quot;token key atrule&quot;&gt;argocd.argoproj.io/hook-delete-policy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; HookSucceeded&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;One good thing about using the &lt;code class=&quot;language-text&quot;&gt;argocd.argoproj.io/hook-delete-policy&lt;/code&gt; annotation instead of using the &lt;code class=&quot;language-text&quot;&gt;ttlSecondsAfterFinished&lt;/code&gt; (Kubernetes Job field) is that if the resource is destroyed ArgoCD won&apos;t &quot;complain&quot; showing a drift!&lt;/p&gt;
&lt;h4&gt;Sync Waves&lt;/h4&gt;
&lt;p&gt;Sync waves allow us to modify the order in which the resources/hooks are applied. A wave is simply a number that defines when the resource should be applied (defined by the &lt;code class=&quot;language-text&quot;&gt;argocd.argoproj.io/sync-wave&lt;/code&gt; annotation). The wave is applied from the lowest value (you can even use negative numbers) to the highest value. By default, resources and hooks have wave set to zero (0). So using sync waves we can modify the order the resources/hooks will be applied.&lt;/p&gt;
&lt;p&gt;We can combine sync waves and hooks to achieve maximum flexibility. Imagine the following scenario: we want to run a database migration and only after the migration is done we want to send a message to Slack saying the the deployment will start. In this case, we can set a sync wave value of the database migration job lower than the slack message job (e.g. &lt;code class=&quot;language-text&quot;&gt;-1&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;0&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;1&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;2&lt;/code&gt;). You can use sync phase annotations in most Kubernetes resources, but usually, they are assigned to Kubernetes Job and/or Argo Workflows.&lt;/p&gt;
&lt;p&gt;ArgoCD uses the following order of precedence when applying the resources:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Phase (i.e. PreSync phase happens before the Sync Phase)&lt;/li&gt;
&lt;li&gt;The sync wave (i.e. from lowest to highest value)&lt;/li&gt;
&lt;li&gt;By Kubernetes kind (e.g. namespace should be created first than other Kubernetes resources, followed by custom resources)&lt;/li&gt;
&lt;li&gt;By name&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;After the order is established ArgoCD will apply wave by wave in order. There is a delay between the sync of waves (default is 2 seconds) that can be configured by the &lt;code class=&quot;language-text&quot;&gt;ARGOCD_SYNC_WAVE_DELAY&lt;/code&gt; environment variable.&lt;/p&gt;
&lt;p&gt;Let&apos;s give some examples so you understand exactly how ArgoCD orders theses resources. Imagine we want to deploy an ArgoCD Application with the following resources:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;Job&lt;/code&gt; (Slack message before deployment): Wave set to -5.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;Job&lt;/code&gt; (database migration): Wave set to -10.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;Namespace&lt;/code&gt;: Wave default (0).&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;Deployment&lt;/code&gt; (application): Wave default (0).&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;Job&lt;/code&gt; (Slack message after deployment): Wave set to 5.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;ArgoCD would first create the namespace (ArgoCD is smart enough to understand that namespace is required to be applied first because the following jobs will be deployed on this namespace), then run the database migration job, then Slack message, then deployment and finally the slack message after deployment.&lt;/p&gt;
&lt;p&gt;We can have more flexibility using sync waves combined with sync phases:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;Job&lt;/code&gt; (database migration): Presync wave set to 1.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;Job&lt;/code&gt; (Slack message before deployment): PreSync with wave set to 2.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;Namespace&lt;/code&gt;: Wave default (0).&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;Deployment&lt;/code&gt; (application): Wave default (0).&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;Job&lt;/code&gt; (Slack message after deployment): Posy Sync with default wave (0) - because the is no order job to run in this phase.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Below you can see the above example in a YouTube video. Pay attention to the order of execution. Also, notice that resources with hooks have an anchor symbol.&lt;/p&gt;
&lt;div align=&quot;center&quot;&gt;
  &lt;a href=&quot;https://www.youtube.com/watch?v=aSGL8cFXzJ0&quot;&gt;&lt;img src=&quot;https://img.youtube.com/vi/aSGL8cFXzJ0/0.jpg&quot; alt=&quot;IMAGE ALT TEXT&quot;&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;h4&gt;Sync Windows&lt;/h4&gt;
&lt;p&gt;Sync windows define the period that you want to enable/disable sync on applications/cluster/namespace. A common example is to block deployments/sync during the weekend, allow only deployments/syncs during weekdays, or allow only deployment during late night (e.g. 4AM) to not impact users.&lt;/p&gt;
&lt;p&gt;When defining a sync window you need to specify when the sync window starts (cron expression), how long should it last, if it should allow or deny the sync operations, the timezone, if manual syncs are still allowed... In case of having more than one sync window, the deny has always preference.&lt;/p&gt;
&lt;p&gt;Sync windows are defined on the project level (&lt;code class=&quot;language-text&quot;&gt;AppProject&lt;/code&gt; object). Check the example below.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; argoproj.io/v1alpha1
&lt;span class=&quot;token key atrule&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; AppProject
&lt;span class=&quot;token key atrule&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; default
&lt;span class=&quot;token key atrule&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;syncWindows&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; allow
    &lt;span class=&quot;token key atrule&quot;&gt;schedule&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;10 1 * * *&apos;&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;duration&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1h
    &lt;span class=&quot;token key atrule&quot;&gt;applications&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;*-prod&apos;&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;manualSync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean important&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; deny
    &lt;span class=&quot;token key atrule&quot;&gt;schedule&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;0 22 * * *&apos;&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;timeZone&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Europe/Amsterdam&quot;&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;duration&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1h
    &lt;span class=&quot;token key atrule&quot;&gt;namespaces&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; default
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; allow
    &lt;span class=&quot;token key atrule&quot;&gt;schedule&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;0 23 * * *&apos;&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;duration&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1h
    &lt;span class=&quot;token key atrule&quot;&gt;clusters&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; in&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;cluster
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; cluster1&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The rules for sync windows are summarized below:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 43.67088607594937%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAYAAAAywQxIAAAACXBIWXMAAAsTAAALEwEAmpwYAAAB0UlEQVR42k2SaW/TQBCG/f9/Ah+QoEKISvChFRIE1EgJMTmai6ROqkJsJ/WR9bmO7Rg7zsvstg0ZabXX7DvzzKzyaXGNi7tLGPEawupjjePxiLquUR0Oci1MzFHMke/3T37yvkKR7/Hz8RZXegPL6DeUub1Ae9EBL5LTQ8d1MRxP0BsMYTsOgiDAxrLRH06gLR/g096hc8YYeJygo7Xw7ttrzIwJlKqqwCMuxdI0hed5SJIEQRhhyzyZUVmWSLMMluOSWIiyKqXvjvxCzrFutqG9fQ9GARXm+eRIWYShFDohEpIcZ8jM95HsdifkA5UkjmJ8XTXxanKBqXcHZfxrjk53AMPcgFO0kIQN04Ta7aP1Q4XvB/Js/WhB7d1iOtNg2TYMQ5fYEQn2x018v7nEw2oGJSOUkA55wiHWInJRFPBIiBHyX8IVmQjsQPoJilr65XmOiMew1B7M6y8ItXsoeDaBJJB3hCTW50OYEA2ofjx5uj88/4AszdDQW3iz/Ih5SILnj8QsmmQTUrc/lNiMUd0o+z8rHYPRlDo/grneyOb5VNOQmtcdNdG4+UDI8/8ZnpuILr6Gu2US/yWQ425l51/+aUadFs3ctFXoV58RLO/xD/dJoTgCV/pgAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Sync Windows flow chart&quot;
        title=&quot;&quot;
        src=&quot;/static/14a9018b541135ad0f385521de09fb31/f058b/sync-window.png&quot;
        srcset=&quot;/static/14a9018b541135ad0f385521de09fb31/c26ae/sync-window.png 158w,
/static/14a9018b541135ad0f385521de09fb31/6bdcf/sync-window.png 315w,
/static/14a9018b541135ad0f385521de09fb31/f058b/sync-window.png 630w,
/static/14a9018b541135ad0f385521de09fb31/40601/sync-window.png 945w,
/static/14a9018b541135ad0f385521de09fb31/71ee9/sync-window.png 1081w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2&gt;Cya!&lt;/h2&gt;
&lt;p&gt;I hope you liked this new blog post! ArgoCD is an amazing tool that allows us to achieve pure GitOps principles and it&apos;s extremely used in the DevOps world.&lt;/p&gt;
&lt;p&gt;See you around! 👋&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Hands-on OpenTelemetry with Python and NodeJS]]></title><description><![CDATA[The goal of this blog post is to demystify OpenTelemetry using applications written in Python and NodeJS (Typescript). We will check how to…]]></description><link>https://felipetrindade.com/opentelemetry-hands-on/</link><guid isPermaLink="false">https://felipetrindade.com/opentelemetry-hands-on/</guid><pubDate>Mon, 04 Mar 2024 16:10:32 GMT</pubDate><content:encoded>&lt;p&gt;The goal of this blog post is to demystify OpenTelemetry using applications written in Python and NodeJS (Typescript). We will check how to add telemetry (logs, metrics, traces) to our application and we will use Opentelemetry when possible.&lt;/p&gt;
&lt;p&gt;I coded two simple APIs:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;Date Service&lt;/code&gt;: Written in Python (using fastapi) that runs on port &lt;code class=&quot;language-text&quot;&gt;3000&lt;/code&gt;. Expose the route &lt;code class=&quot;language-text&quot;&gt;/date&lt;/code&gt; that returns the current day, &lt;code class=&quot;language-text&quot;&gt;0&lt;/code&gt; for Monday, &lt;code class=&quot;language-text&quot;&gt;1&lt;/code&gt; for Tuesday, and it keeps going until &lt;code class=&quot;language-text&quot;&gt;6&lt;/code&gt; for Sunday.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;Gym Service&lt;/code&gt;: Written in NodeJS and Typescript (using express) that runs on port &lt;code class=&quot;language-text&quot;&gt;8000&lt;/code&gt;. Expose the route &lt;code class=&quot;language-text&quot;&gt;/gym&lt;/code&gt; that returns a JSON saying if you should or should not go to the gym based on the current day. It calls the &lt;code class=&quot;language-text&quot;&gt;Date Service&lt;/code&gt; to know what&apos;s the current day. In case you are curious the business rule applied here is simple: we should go to the gym when it&apos;s a weekday.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I will try to link everything I&apos;m doing with the documentation so I can teach you where to look when you need to develop yourself.&lt;/p&gt;
&lt;p&gt;Oh, and something very important, if you are not familiar with OpenTelemetry I highly recommend you to check my &lt;a href=&quot;https://www.felipetrindade.com/opentelemetry/&quot;&gt;previous blog post&lt;/a&gt; that goes into detail about what OTel is, its goal, concepts and much more.&lt;/p&gt;
&lt;h2&gt;Applications&lt;/h2&gt;
&lt;h3&gt;Python API: Date Service&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;python&quot;&gt;&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; logging
&lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; datetime &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; datetime

&lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; fastapi &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; FastAPI&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; HTTPException

&lt;span class=&quot;token comment&quot;&gt;# Logger&lt;/span&gt;
logging&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;basicConfig&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;level&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;logging&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;DEBUG&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;%(asctime)s - %(levelname)s - %(message)s&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
logger &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; logging&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;getLogger&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;__name__&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# App&lt;/span&gt;
app &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; FastAPI&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Routes&lt;/span&gt;
&lt;span class=&quot;token decorator annotation punctuation&quot;&gt;@app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/health&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;healthcheck&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    logger&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;info&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Checking status of application...&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&quot;App Name&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Date Service&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&quot;status&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;success&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&quot;message&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;OK&quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;


&lt;span class=&quot;token decorator annotation punctuation&quot;&gt;@app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/date&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;get_date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        day &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; datetime&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;today&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;weekday&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        logger&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;info&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-interpolation&quot;&gt;&lt;span class=&quot;token string&quot;&gt;f&quot;Successfully able to get current day (&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;day&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;)&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&quot;status&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;success&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&quot;date&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; day
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;except&lt;/span&gt; Exception &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        logger&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;error&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-interpolation&quot;&gt;&lt;span class=&quot;token string&quot;&gt;f&quot;Not able to get current day: &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; HTTPException&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;500&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&quot;status&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;failed&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&quot;message&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Something went wrong... Try again&quot;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;As you can see a straightforward code. Notice that we are not logging in JSON format (this is important because in the NodeJS API, I will use JSON so we will see the differences in the following steps).&lt;/p&gt;
&lt;h3&gt;NodeJS API: Gym Service&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; express &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;express&apos;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; bodyParser &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;body-parser&apos;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; cors &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;cors&apos;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; createLogger&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; format&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; transports &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;winston&apos;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; axios &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;axios&apos;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Logger&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; combine&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; timestamp&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; json &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; format
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; logger &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;createLogger&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  format&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;combine&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;timestamp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  transports&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;transports&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// App&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; app &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;express&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;express&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;cors&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;bodyParser&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;listen&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;8000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Routes&lt;/span&gt;
app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;/health&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;req&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; res&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  logger&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Checking status of application...&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  res&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token string-property property&quot;&gt;&quot;App Name&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Gym Service&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string-property property&quot;&gt;&quot;status&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;success&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string-property property&quot;&gt;&quot;message&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;OK&quot;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;/gym&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;req&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; res&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; dateResponse&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;any&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; axios&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
      &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;http://&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;process&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;env&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;DATE_SERVICE_HOSTNAME&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;process&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;env&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;DATE_SERVICE_PORT&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;/date&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    logger&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Able to get the date from date service&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;dateResponse&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;date &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; dateResponse&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;date &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      res&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        status&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;success&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        message&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Day off! No need to go to the gym today...&quot;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      res&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        status&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;success&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        message&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Let&apos;s work out! You need to go to the gym today...&quot;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;error&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    logger&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;Unable to get current date: &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;error&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    res&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;500&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      status&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;failed&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      message&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Something went wrong... Try again later.&quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;A bit bigger code, when compared to the &lt;code class=&quot;language-text&quot;&gt;Date Service&lt;/code&gt; but still small and easy to understand. Notice that here we are logging in JSON format.&lt;/p&gt;
&lt;h3&gt;Deployment&lt;/h3&gt;
&lt;p&gt;We can easily deploy these applications using Docker and orchestrate this deployment locally using Docker Compose. A simple &lt;code class=&quot;language-text&quot;&gt;docker compose up --build&lt;/code&gt; will run all the things we need!&lt;/p&gt;
&lt;h4&gt;Dockerfile&lt;/h4&gt;
&lt;p&gt;For the &lt;code class=&quot;language-text&quot;&gt;Date Service&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;FROM python&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;3.11.8&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;slim
WORKDIR /app
RUN pip install pipenv
COPY Pipfile .
COPY Pipfile.lock .
RUN pipenv install &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;deploy
COPY . .
CMD &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;pipenv&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;run&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;uvicorn&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;main:app&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;--host&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;0.0.0.0&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;--port&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;3000&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;--no-access-log&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;For the &lt;code class=&quot;language-text&quot;&gt;Gym Service&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;FROM node&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;20&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;alpine as build
WORKDIR /app
COPY package.json ./
RUN yarn
COPY . .
RUN yarn build


FROM node&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;20&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;alpine as runner
WORKDIR /app
COPY package.json ./
RUN yarn &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;prod
COPY &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;from=build /app/dist ./
EXPOSE 8000
CMD &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;node&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;index.js&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4&gt;Docker Compose&lt;/h4&gt;
&lt;p&gt;We can deploy these services in the same bridge network in a Docker Compose as follows:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;3&quot;&lt;/span&gt;

&lt;span class=&quot;token key atrule&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;container_name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; date&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;service
    &lt;span class=&quot;token key atrule&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ./services/date
    &lt;span class=&quot;token key atrule&quot;&gt;networks&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; observability
    &lt;span class=&quot;token key atrule&quot;&gt;ports&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; 3000&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3000&lt;/span&gt;

  &lt;span class=&quot;token key atrule&quot;&gt;gym&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;container_name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; gym&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;service
    &lt;span class=&quot;token key atrule&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ./services/gym
    &lt;span class=&quot;token key atrule&quot;&gt;networks&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; observability
    &lt;span class=&quot;token key atrule&quot;&gt;environment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;DATE_SERVICE_HOSTNAME&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; date&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;service
      &lt;span class=&quot;token key atrule&quot;&gt;DATE_SERVICE_PORT&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3000&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;ports&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; 8000&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;8000&lt;/span&gt;

&lt;span class=&quot;token key atrule&quot;&gt;networks&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;observability&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; observability&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;Instrumentation&lt;/h2&gt;
&lt;p&gt;Ok, so far I only explained the applications and how they are being deployed locally. Now let&apos;s start the interesting part: instrumentation.&lt;/p&gt;
&lt;p&gt;OpenTelemetry only exposes SDKs, APIs and specs to standardize observability. It is not part of OpenTelemetry to deal with storage and visualization (called observability backend, or simply backend). There are several tools in the market, I will point some that we can use:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;Logs&lt;/code&gt;: &lt;a href=&quot;https://grafana.com/oss/loki/&quot;&gt;Grafana Loki&lt;/a&gt;, &lt;a href=&quot;https://www.elastic.co/guide/en/elasticsearch/reference/current/elasticsearch-intro.html&quot;&gt;Elasticsearch&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;Traces&lt;/code&gt;: &lt;a href=&quot;https://zipkin.io/&quot;&gt;Zipkin&lt;/a&gt;, &lt;a href=&quot;https://www.jaegertracing.io/&quot;&gt;Jaeger&lt;/a&gt;, &lt;a href=&quot;https://grafana.com/oss/tempo/&quot;&gt;Grafana Tempo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;Metrics&lt;/code&gt;: &lt;a href=&quot;https://prometheus.io/&quot;&gt;Prometheus&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There are two other categories of tools (that are not used for instrumentation) but are usually used in observability projects that I would like to mention:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;Visualization&lt;/code&gt;: Help you visualize all the metrics, logs, and traces in a single place. It consumes (data connection) the other observability backends used. Popular open-source tools are &lt;a href=&quot;https://grafana.com/oss/grafana/&quot;&gt;Grafana&lt;/a&gt; and &lt;a href=&quot;https://www.elastic.co/guide/en/kibana/current/index.html&quot;&gt;Kibana&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;Alerts&lt;/code&gt;: Let developers know when issues are happening in the applications using alert tools such as &lt;a href=&quot;https://prometheus.io/docs/alerting/latest/alertmanager/&quot;&gt;AlertManager&lt;/a&gt;, &lt;a href=&quot;https://www.keephq.dev/&quot;&gt;Keep&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;Collectors&lt;/code&gt; (or &lt;code class=&quot;language-text&quot;&gt;Agents&lt;/code&gt;): These tools are used to collect the instrumentation data (logs, metrics, metrics). The idea is that your application shouldn&apos;t worry manage the retries, process (transformations), and export telemetry to the backend service, it should only expose and another tool should manage this: this is where collectors come in. Some open-source collectors are &lt;a href=&quot;https://fluentbit.io/&quot;&gt;Fluentbit&lt;/a&gt; (for collecting logs), &lt;a href=&quot;https://www.elastic.co/logstash&quot;&gt;Logstash&lt;/a&gt; (for collecting logs), &lt;a href=&quot;https://opentelemetry.io/docs/collector/&quot;&gt;OpenTelemetry Collector&lt;/a&gt; (for collecting Logs, Metrics and Traces), &lt;a href=&quot;https://vector.dev/&quot;&gt;Vector&lt;/a&gt; (Logs, metrics), and &lt;a href=&quot;https://grafana.com/docs/agent/latest/&quot;&gt;Grafana Agent&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;APM Platforms&lt;/code&gt;: APM (Application Performance Monitoring) in simple words is just a fancy word to say &quot;observability&quot;, commonly it&apos;s used to talk about tools/services that in a single place can help you understand how your application is performing (using traces, logs, metrics). The most famous APM platforms in the market are &lt;a href=&quot;https://www.datadoghq.com/&quot;&gt;Datadog&lt;/a&gt;, &lt;a href=&quot;https://newrelic.com/&quot;&gt;New Relic&lt;/a&gt;, and &lt;a href=&quot;https://www.dynatrace.com/&quot;&gt;Dynatrace&lt;/a&gt;. There is also an open-source APM that it&apos;s an alternative to all these paid services: &lt;a href=&quot;https://signoz.io/&quot;&gt;SigNoz&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As you can see, there are infinite ways to set up an observability stack. The community even likes to create acronyms for popular stacks: such as ELK (Elasticsearch - Logstash - Kibana).&lt;/p&gt;
&lt;p&gt;For this blog post, I chose to use the following backends:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Collector&lt;/strong&gt;: &lt;code class=&quot;language-text&quot;&gt;OpenTelemetry Collector&lt;/code&gt; (as a collector)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Logs Collector&lt;/strong&gt;: &lt;code class=&quot;language-text&quot;&gt;Fluentbit&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Logs&lt;/strong&gt;: &lt;code class=&quot;language-text&quot;&gt;Grafana Loki&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Metrics&lt;/strong&gt;: &lt;code class=&quot;language-text&quot;&gt;Prometheus&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Traces&lt;/strong&gt;: &lt;code class=&quot;language-text&quot;&gt;Grafana Tempo&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Visualization&lt;/strong&gt;: &lt;code class=&quot;language-text&quot;&gt;Grafana&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So without further ado, let&apos;s start!&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/a470de0edf6a99c8849e06e65922eedc/c50e3/architecture.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 28.48101265822785%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAGCAYAAADDl76dAAAACXBIWXMAAAsTAAALEwEAmpwYAAABHUlEQVR42oWR3U6EMBCFef9X8EV8CS9M1GiQVdmFpT+0LLTQlpbjlN0rNXGSSdPJzPQ7pwX+iZQilhBhDi+I1SPgLLyZ4Ly/NmzbNW9RlPU7YvA0mPbCOE6YxwaRruuaMAwaUg8Igmr8uA9H76C1RgjhF0DRlHdoWoG2bdA2J0jOINkJNfOYTIBSCoJzOFqO8YxNV8g882zBhITXHRKvMDtHACuK6lDifO7AGMOxrhFIyqcOsCQzR4xxJ7HzAt7U4F9vuEwGFyJnQsHwEkk8QPaK1I0o2q4lWcM+ZKyFkhL3z6/46EkSvZoJs7zJWKS5xxoWTLQwW/Gn5J+F3DSoHpfFko8JnohzbXEB4vQE0VVEYsjKdPuTbMC2nzm/AdQFzuhjwWHzAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;A diagram of the used tools for this blog post: Python, NodeJS, OpenTelemetry Collector, fluentbit, Prometheus, Granafa Tempo, Grafana Loki and Grafana.&quot;
        title=&quot;&quot;
        src=&quot;/static/a470de0edf6a99c8849e06e65922eedc/f058b/architecture.png&quot;
        srcset=&quot;/static/a470de0edf6a99c8849e06e65922eedc/c26ae/architecture.png 158w,
/static/a470de0edf6a99c8849e06e65922eedc/6bdcf/architecture.png 315w,
/static/a470de0edf6a99c8849e06e65922eedc/f058b/architecture.png 630w,
/static/a470de0edf6a99c8849e06e65922eedc/40601/architecture.png 945w,
/static/a470de0edf6a99c8849e06e65922eedc/78612/architecture.png 1260w,
/static/a470de0edf6a99c8849e06e65922eedc/c50e3/architecture.png 2442w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Notice that we are using &lt;code class=&quot;language-text&quot;&gt;Fluentbit&lt;/code&gt; to collect logs of the applications. Why? Because at the moment I&apos;m writing this blog post the &lt;a href=&quot;https://opentelemetry.io/status/#language-sdks&quot;&gt;SDK status&lt;/a&gt; for Python is in an experimental state and the Javascript is under development. Although the &lt;a href=&quot;https://opentelemetry.io/docs/specs/status/#logging&quot;&gt;SDK spec&lt;/a&gt; is stable, the open-source community is still implementing the spec in the programming languages. I could have used OpenTelemetry to export logs in Python but I wanted to apply the same collect strategy for the NodeJS application, which is not possible at the moment I&apos;m writing this blog post.&lt;/p&gt;
&lt;h3&gt;Logging&lt;/h3&gt;
&lt;p&gt;Actually, we have already instrumented our code with logs, but now we need to set up the infrastructure to forward them to the backend. This will be done in three (3) steps: deploy the logs backend (Grafana Loki), deploy fluentbit for collecting the logs and configure Grafana to visualize these logs.&lt;/p&gt;
&lt;h4&gt;Grafana Loki&lt;/h4&gt;
&lt;p&gt;Loki can be deployed in several &lt;a href=&quot;https://grafana.com/docs/loki/latest/get-started/deployment-modes/&quot;&gt;modes&lt;/a&gt;. For simplicity, we will deploy in the monolithic mode using a &lt;a href=&quot;https://github.com/grafana/loki/blob/main/cmd/loki/loki-local-config.yaml&quot;&gt;local configuration&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;loki&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; grafana/loki&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;2.9.0
  &lt;span class=&quot;token key atrule&quot;&gt;container_name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; loki
  &lt;span class=&quot;token key atrule&quot;&gt;networks&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; observability
  &lt;span class=&quot;token key atrule&quot;&gt;ports&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; 3100&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3100&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; 7946&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;7946&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; 9095&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;9095&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;command&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;config.file=/etc/loki/local&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;config.yaml
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;print&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;config&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;stderr=true
  &lt;span class=&quot;token key atrule&quot;&gt;healthcheck&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; wget &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;no&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;verbose &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;tries=1 &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;spider http&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;//localhost&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;3100/ready &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; exit 1
    &lt;span class=&quot;token key atrule&quot;&gt;interval&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 15s
    &lt;span class=&quot;token key atrule&quot;&gt;timeout&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 5s
    &lt;span class=&quot;token key atrule&quot;&gt;retries&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4&gt;Fluentbit&lt;/h4&gt;
&lt;p&gt;Currently, Date service is logging to &lt;code class=&quot;language-text&quot;&gt;STDOUT&lt;/code&gt; in a non-serialized format (not JSON) and Gym service in JSON format to &lt;code class=&quot;language-text&quot;&gt;STDOUT&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;To collect logs with Fluentbit we will need to:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Deploy Fluentbit&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This can be easily achievable by adding a new service to our Docker Compose file.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;fluent-bit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; fluent/fluent&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;bit&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;2.2.0
  &lt;span class=&quot;token key atrule&quot;&gt;container_name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; fluent&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;bit
  &lt;span class=&quot;token key atrule&quot;&gt;networks&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; observability
  &lt;span class=&quot;token key atrule&quot;&gt;volumes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; ./config/fluent&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;bit.conf&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;/fluent&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;bit/etc/fluent&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;bit.conf
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; ./config/fluent&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;bit&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;parsers.conf&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;/fluent&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;bit/etc/fluent&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;bit&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;parsers.conf
  &lt;span class=&quot;token key atrule&quot;&gt;ports&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;24224:24224/tcp&quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;24224:24224/udp&quot;&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;command&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;c /fluent&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;bit/etc/fluent&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;bit.conf &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;log.level=debug&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Notice that the &lt;code class=&quot;language-text&quot;&gt;fluent-bit.conf&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;fluent-bit-parsers.conf&lt;/code&gt; are two configuration files for the fluent-bit. I will comment about these files in the next section.&lt;/p&gt;
&lt;p&gt;Probably the most common way to collect logs with Fluentbit is to read logs from a file. Even if your application is configured to send logs to STDOUT, depending on where your application is running, the tool that manages the deployment (e.g. Docker and Kubernetes) will get the STDOUT logs and dump them in a file, so you can read this file using fluentbit.&lt;/p&gt;
&lt;p&gt;I&apos;m using a Mac and the Docker logs folder of the containers is not available easily (in Linux you could easily do this by collecting the log files inside the &lt;code class=&quot;language-text&quot;&gt;/var/lib/docker/containers&lt;/code&gt; folder). So instead of reading a file, I will &lt;a href=&quot;https://docs.docker.com/compose/compose-file/compose-file-v3/#logging&quot;&gt;configure&lt;/a&gt; the logs of the Docker to use fluentbit as the logging driver. Even though fluentbit is not present in the list of &lt;a href=&quot;https://docs.docker.com/config/containers/logging/configure/#supported-logging-drivers&quot;&gt;logging drivers&lt;/a&gt; we can use the &lt;code class=&quot;language-text&quot;&gt;fluentd&lt;/code&gt; logging driver. This works because both projects are very similar, the main differences are the fact that fluentbit is written only in C (instead of C and Ruby for fluentd) and it uses way less memory. So we can configure fluentd driver with the following &lt;a href=&quot;https://docs.docker.com/config/containers/logging/fluentd/#options&quot;&gt;configuration&lt;/a&gt; (this configuration will be added to Date and Gym Service):&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;driver&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; fluentd
  &lt;span class=&quot;token key atrule&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; localhost&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;24224&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;fluentd-async&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean important&quot;&gt;true&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;Configure Fluentbit pipeline&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Fluentbit configuration of pipelines can be done via the &lt;a href=&quot;https://docs.fluentbit.io/manual/administration/configuring-fluent-bit/classic-mode/configuration-file&quot;&gt;classic way&lt;/a&gt; (conf files) or the &quot;&lt;a href=&quot;https://docs.fluentbit.io/manual/administration/configuring-fluent-bit/yaml/configuration-file&quot;&gt;modern&lt;/a&gt;&quot; way (YAML files), and I went for the first approach.&lt;/p&gt;
&lt;p&gt;Let&apos;s first create a dummy pipeline to inspect how the logs will be seen by fluentbit:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;conf&quot;&gt;&lt;pre class=&quot;language-conf&quot;&gt;&lt;code class=&quot;language-conf&quot;&gt;# fluent-bit.conf

# https://docs.fluentbit.io/manual/pipeline/inputs/forward
[INPUTS]
  Name            forward
  Listen          0.0.0.0
  Port            24224

# https://docs.fluentbit.io/manual/pipeline/filters/standard-output
[OUTPUT]
  name            stdout
  match           *&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We got the following sample logs:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Date Service&lt;/strong&gt;:&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;[[1709125948.000000000, {}], {&quot;container_name&quot;=&gt;&quot;/date-service&quot;, &quot;source&quot;=&gt;&quot;stderr&quot;, &quot;log&quot;=&gt;&quot;2024-02-28 13:12:28,235 - INFO - Successfully able to get current day (2)&quot;, &quot;container_id&quot;=&gt;&quot;a58d767d02d37feaf384733808e879a4980c7cce6ac8524addacb58ad4e09253&quot;}]&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Gym Service&lt;/strong&gt;:&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;[[1709125948.000000000, {}], {&quot;source&quot;=&gt;&quot;stdout&quot;, &quot;log&quot;=&gt;&quot;{&quot;level&quot;:&quot;info&quot;,&quot;message&quot;:&quot;Able to get the date from date service&quot;,&quot;timestamp&quot;:&quot;2024-02-28T13:12:28.241Z&quot;}&quot;, &quot;container_id&quot;=&gt;&quot;c402b0f01f439e6d76b94b71757894f974dc43ab3250907dbd930c90f26389a3&quot;, &quot;container_name&quot;=&gt;&quot;/gym-service&quot;}]&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We can clearly see the Fluentbit &lt;a href=&quot;https://docs.fluentbit.io/manual/concepts/key-concepts#event-format&quot;&gt;event format&lt;/a&gt; in these logs: &lt;code class=&quot;language-text&quot;&gt;[[TIMESTAMP, METADATA], MESSAGE]&lt;/code&gt;. Notice that the &lt;code class=&quot;language-text&quot;&gt;message&lt;/code&gt; contains some keys to identify where the logs came from, such as &lt;code class=&quot;language-text&quot;&gt;container_id&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;container_name&lt;/code&gt;. Also, notice that the &lt;code class=&quot;language-text&quot;&gt;log&lt;/code&gt; key is the real log message that we were expecting. We need to transform the &lt;code class=&quot;language-text&quot;&gt;message&lt;/code&gt; into more structured (correctly parse it) data to send to the backend and would also need to forward these logs to the Loki backend.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;conf&quot;&gt;&lt;pre class=&quot;language-conf&quot;&gt;&lt;code class=&quot;language-conf&quot;&gt;# fluent-bit.conf

# https://docs.fluentbit.io/manual/administration/configuring-fluent-bit/classic-mode/configuration-file#config_section
[SERVICE]
  Parsers_File   /fluent-bit/etc/fluent-bit-parsers.conf

# https://docs.fluentbit.io/manual/pipeline/inputs/forward
[INPUTS]
  Name           forward
  Tag            docker
  Listen         0.0.0.0
  Port           24224

# https://docs.fluentbit.io/manual/pipeline/filters/parser
[FILTER]
  Name           parser
  Match          docker
  Key_Name       log
  Parser         date-service
  Reserve_Data   True

[FILTER]
  Name           parser
  Match          docker
  Key_Name       log
  Parser         gym-service
  Reserve_Data   True

# https://docs.fluentbit.io/manual/pipeline/filters/standard-output
[OUTPUT]
  name           stdout
  match          docker

# https://docs.fluentbit.io/manual/pipeline/outputs/loki
[OUTPUT]
  name loki
  match *
  host loki
  port 3100
  labels collector=fluentbit, container_name=$container_name&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;conf&quot;&gt;&lt;pre class=&quot;language-conf&quot;&gt;&lt;code class=&quot;language-conf&quot;&gt;# fluent-bit-parsers.conf

# https://docs.fluentbit.io/manual/v/1.3/parser/json
[PARSER]
  Name           gym-service
  Format         json
  Time_Key       timestamp
  Time_Format    %Y-%m-%dT%H:%M:%S %z

# https://docs.fluentbit.io/manual/pipeline/parsers/regular-expression
[PARSER]
  Name           date-service
  Format         regex
  Regex          ^(?&amp;lt;time&amp;gt;[^ ]*) - (?&amp;lt;level&amp;gt;[^ ]*) - (?&amp;lt;message&amp;gt;.*)$
  Time_Key       time
  Time_Format    %Y-%m-%d %H:%M:%S
  Time_Keep      On&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now we can see in fluentbit the following events:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Date Service&lt;/strong&gt;:&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;[[1709130293.000000000, {}], {&quot;container_name&quot;=&gt;&quot;/date-service&quot;, &quot;source&quot;=&gt;&quot;stderr&quot;, &quot;log&quot;=&gt;&quot;2024-02-28 14:24:53,777 - INFO - Successfully able to get current day (2)&quot;, &quot;container_id&quot;=&gt;&quot;b2d627623047133424981c15f67381c66c57f661f6869f60b7b0241e71500e59&quot;}]&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Gym Service&lt;/strong&gt;:&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;[[1709130293.000000000, {}], {&quot;level&quot;=&gt;&quot;info&quot;, &quot;message&quot;=&gt;&quot;Able to get the date from date service&quot;, &quot;timestamp&quot;=&gt;&quot;2024-02-28T14:24:53.782Z&quot;, &quot;container_id&quot;=&gt;&quot;c402b0f01f439e6d76b94b71757894f974dc43ab3250907dbd930c90f26389a3&quot;, &quot;container_name&quot;=&gt;&quot;/gym-service&quot;, &quot;source&quot;=&gt;&quot;stdout&quot;}]&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Good job!	😀&lt;/p&gt;
&lt;h4&gt;Grafana&lt;/h4&gt;
&lt;p&gt;As you probably expect... we will also deploy Grafana via Docker Compose. We are going to deploy Grafana and automatically set up a &lt;a href=&quot;https://grafana.com/docs/grafana/latest/administration/provisioning/#data-sources&quot;&gt;data source&lt;/a&gt; to point to Loki.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;grafana&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; grafana/grafana&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;10.2.1
  &lt;span class=&quot;token key atrule&quot;&gt;container_name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; grafana
  &lt;span class=&quot;token key atrule&quot;&gt;networks&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; observability
  &lt;span class=&quot;token key atrule&quot;&gt;environment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; GF_PATHS_PROVISIONING=/etc/grafana/provisioning
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; GF_AUTH_ANONYMOUS_ENABLED=true
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; GF_AUTH_ANONYMOUS_ORG_ROLE=Admin
  &lt;span class=&quot;token key atrule&quot;&gt;ports&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;3001:3000&quot;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# Using 3001 because 3000 is already being used by Date Service&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;volumes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; ./config/grafana.yaml&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;/etc/grafana/provisioning/datasources/ds.yaml
  &lt;span class=&quot;token key atrule&quot;&gt;entrypoint&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; sh
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;euc
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; /run.sh&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Where the config file is the data source file pointing to loki:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;datasources&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;# https://grafana.com/docs/grafana/latest/datasources/loki/#provision-the-loki-data-source&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Loki
    &lt;span class=&quot;token key atrule&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; loki
    &lt;span class=&quot;token key atrule&quot;&gt;access&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; proxy
    &lt;span class=&quot;token key atrule&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; http&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;//loki&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3100&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;basicAuth&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean important&quot;&gt;false&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now you should be seeing logs from Grafana in the explorer page.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/f4375468b1a455a341b5f659d8bb565f/d9b5d/logs.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 74.68354430379746%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAPCAYAAADkmO9VAAAACXBIWXMAAAsTAAALEwEAmpwYAAACJUlEQVR42pVTyY7UMBTMN9B0Z3M2x04cZ+slPWFaggMHxB3+gv8/F2WHHmmGBmkOJdtvKb9Xfg4aM6CsNUQhcYgFPoaJx+4QY7ePENKWVQpZUSPn6uIc8lIhzQoU5hPU9Qfy/itjJIK2mzGOJwhR4hBmiJMScVpC5DUSIRmk0NkTDOOG+QrDfWO4nxaoxqI9f8fy7Re69SfPA4JCNphON5hhhbYT7Hz2DmNnuMuk7lEp+xDex/yiqLhqdtohyCuNbjwTC/rjFcv6GfP5meRXNP2Kup2YuCU/BC+vm5HryEtImJU1tBmpR0ldNKbj6qHtBQ1JffC/yN7AE7qWB1bntWGL2ky+dOd0+G91jwhdy3Z6RkvSdjyS+Miq+j9E70OlDAJlekznFf18wXRZ0Q0nZBwJPyrvhKB8gdQdq5pQ8JU0Z9LN2j4SfiYd9vF9n9Ke+nWD86V/gYQG7mF2YYwPh8gbwyRDmObEfc0RieIF/vzGdkdQsG8z3zCeFmrJGTQWDTV1Umg+VN0v1KflWB2h2t53UKqGaNlmjTjjZ8hyHBLhCwicQ3eDJ7PTxQc6JBzWlF/pro0oJROLV4jE1kXKWMdRUb5AdZav2hHWG2RjfAW5VISmbbvAJUUi8yRvEXOGexY0scsgq+WLXpLET7cvdFz5W5580J3M+R+RbVUKklbIa45NWlVeYJfgNNGm98R1az3cfjt3/OOb3VX/6gISJqVBIkf8BvIRqBIIh9BFAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;A screenshot of logs in Grafana.&quot;
        title=&quot;&quot;
        src=&quot;/static/f4375468b1a455a341b5f659d8bb565f/f058b/logs.png&quot;
        srcset=&quot;/static/f4375468b1a455a341b5f659d8bb565f/c26ae/logs.png 158w,
/static/f4375468b1a455a341b5f659d8bb565f/6bdcf/logs.png 315w,
/static/f4375468b1a455a341b5f659d8bb565f/f058b/logs.png 630w,
/static/f4375468b1a455a341b5f659d8bb565f/40601/logs.png 945w,
/static/f4375468b1a455a341b5f659d8bb565f/d9b5d/logs.png 1224w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3&gt;Traces&lt;/h3&gt;
&lt;p&gt;Traces SDKs are &lt;a href=&quot;https://opentelemetry.io/status/&quot;&gt;stable&lt;/a&gt; for Python and Javascript so we will definitely use OpenTelemetry here! We can divide this into five (5) steps: deploy trace backend (Grafana Tempo), add Tempo data source to Grafana, deploy OpenTelemetry Collector, instrument Date service, and instrument Gym service.&lt;/p&gt;
&lt;h4&gt;Grafana Tempo&lt;/h4&gt;
&lt;p&gt;We are going to deploy Grafana Tempo in the &lt;a href=&quot;https://grafana.com/docs/tempo/latest/setup/deployment/#monolithic-mode&quot;&gt;monolithic mode&lt;/a&gt; and &lt;a href=&quot;https://grafana.com/docs/tempo/latest/configuration/&quot;&gt;configure&lt;/a&gt; some things such as: allowing it to receive traces via OTLP (via HTTP) and configuring Grafana Tempo storage to be local (you don&apos;t want to do that in production, I&apos;m just using a simple configuration since Tempo configuration is not the goal of this blog post).&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;tempo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; grafana/tempo&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;2.3.1
  &lt;span class=&quot;token key atrule&quot;&gt;container_name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; tempo
  &lt;span class=&quot;token key atrule&quot;&gt;networks&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; observability
  &lt;span class=&quot;token key atrule&quot;&gt;ports&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; 3200&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3200&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; 4318&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;4318&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# OTLP http receiver&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;volumes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; ./config/tempo.yaml&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;/etc/tempo/config.yaml
  &lt;span class=&quot;token key atrule&quot;&gt;command&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;config.file=/etc/tempo/config.yaml&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And &lt;code class=&quot;language-text&quot;&gt;tempo.yaml&lt;/code&gt; is the configuration file for Grafana Tempo:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;stream_over_http_enabled&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean important&quot;&gt;true&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# https://grafana.com/docs/tempo/latest/configuration/#server&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;server&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;http_listen_port&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3200&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;log_level&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; info

&lt;span class=&quot;token comment&quot;&gt;# https://grafana.com/docs/tempo/latest/configuration/#query-frontend&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;query_frontend&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;search&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;duration_slo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 5s
  &lt;span class=&quot;token key atrule&quot;&gt;trace_by_id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;duration_slo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 5s

&lt;span class=&quot;token comment&quot;&gt;# https://grafana.com/docs/tempo/latest/configuration/#distributor&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;distributor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;receivers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;otlp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;protocols&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;http&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# https://grafana.com/docs/tempo/latest/configuration/#storage&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;storage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;trace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;backend&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; local
    &lt;span class=&quot;token key atrule&quot;&gt;wal&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; /tmp/tempo/wal             &lt;span class=&quot;token comment&quot;&gt;# where to store the the wal locally&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;local&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; /tmp/tempo/blocks&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4&gt;Grafana Tempo data source&lt;/h4&gt;
&lt;p&gt;To add a new data source, as we saw earlier, we should add a new entry in the &lt;code class=&quot;language-text&quot;&gt;grafana.yaml&lt;/code&gt; file:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;datasources&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;# https://grafana.com/docs/grafana/latest/datasources/loki/#provision-the-loki-data-source&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Loki
    &lt;span class=&quot;token key atrule&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; loki
    &lt;span class=&quot;token key atrule&quot;&gt;access&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; proxy
    &lt;span class=&quot;token key atrule&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; http&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;//loki&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3100&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;basicAuth&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean important&quot;&gt;false&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;# https://grafana.com/docs/grafana/latest/datasources/tempo/configure-tempo-data-source/#provision-the-data-source&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Tempo
    &lt;span class=&quot;token key atrule&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; tempo
    &lt;span class=&quot;token key atrule&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; http&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;//tempo&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3200&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4&gt;OpenTelemetry Collector&lt;/h4&gt;
&lt;p&gt;We can deploy the OTel Collector in &lt;a href=&quot;https://opentelemetry.io/docs/collector/installation/#docker-compose&quot;&gt;Docker Compose&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;otel-collector&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; otel/opentelemetry&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;collector&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;0.95.0
    &lt;span class=&quot;token key atrule&quot;&gt;container_name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; otel&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;collector
    &lt;span class=&quot;token key atrule&quot;&gt;command&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;--config=/etc/otel-collector.yaml&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;networks&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; observability
    &lt;span class=&quot;token key atrule&quot;&gt;volumes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; ./config/otel&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;collector.yaml&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;/etc/otel&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;collector.yaml&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;otel-collector.yaml&lt;/code&gt; contains the &lt;a href=&quot;https://opentelemetry.io/docs/collector/configuration/&quot;&gt;OTel Collector configuration&lt;/a&gt;. The idea is that we want to receive data from the applications (Date and Gym service) via HTTP using OTLP protocol (there are other protocols, such as gRPC), and then we want to forward these traces to Grafana Tempo.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# https://opentelemetry.io/docs/collector/configuration/#receivers&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;receivers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;otlp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;protocols&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;http&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;endpoint&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0.0.0.0&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;4318&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# https://opentelemetry.io/docs/collector/configuration/#exporters&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;exporters&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;otlphttp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;endpoint&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; http&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;//tempo&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;4318&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# https://opentelemetry.io/docs/collector/configuration/#pipelines&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;service&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;pipelines&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;traces&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;receivers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;otlp&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;exporters&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;otlphttp&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4&gt;Date Service (Python)&lt;/h4&gt;
&lt;p&gt;Finally! Now that all the infrastructure is in place we can start to work on the application. As I promised I will instrument the code automatically and manually. Let&apos;s start with the &lt;a href=&quot;https://opentelemetry.io/docs/languages/python/automatic/&quot;&gt;automatic instrumentation&lt;/a&gt; for Python.&lt;/p&gt;
&lt;p&gt;We need to install the packages &lt;code class=&quot;language-text&quot;&gt;opentelemetry-distro&lt;/code&gt; (installs API, SDK and Bootstrap packages) and &lt;code class=&quot;language-text&quot;&gt;opentelemetry-exporter-otlp&lt;/code&gt;. We will also need to add other packages to allow auto instrumentation of the packages we are using in our project. We could check these packages in the &lt;a href=&quot;https://opentelemetry.io/ecosystem/registry&quot;&gt;registry&lt;/a&gt; and install them or simply run &lt;code class=&quot;language-text&quot;&gt;opentelemetry-bootstrap -a &lt;/code&gt;install`, which will automatically check our dependencies and install these packages.&lt;/p&gt;
&lt;p&gt;We need to run this Opentelemetry auto instrumentation agent with our application so instead of running &lt;code class=&quot;language-text&quot;&gt;uvicorn main:app&lt;/code&gt; to run the application we will need to call the agent first &lt;code class=&quot;language-text&quot;&gt;opentelemetry-instrument uvicorn main:app&lt;/code&gt;. So our new Dockefile will be (just modified the CMD):&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;FROM python&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;3.11.8&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;slim
WORKDIR /app
RUN pip install pipenv
COPY Pipfile .
COPY Pipfile.lock .
RUN pipenv install &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;deploy
COPY . .
CMD &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;pipenv&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;run&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;opentelemetry-instrument&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;uvicorn&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;main:app&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;--host&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;0.0.0.0&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;--port&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;3000&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;--no-access-log&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We should also configure the name of the service in the OpenTelemetry SDK by configuring the &lt;a href=&quot;https://opentelemetry.io/docs/languages/sdk-configuration/general/&quot;&gt;environment variables&lt;/a&gt; &lt;code class=&quot;language-text&quot;&gt;OTEL_SERVICE_NAME&lt;/code&gt;. We should also configure the application to send data to the collector by configuring the &lt;code class=&quot;language-text&quot;&gt;OTEL_TRACES_EXPORTER&lt;/code&gt; (by default it&apos;s already OTLP, which is the exporter that we are going to use to communicate with the OTel Collector), the &lt;code class=&quot;language-text&quot;&gt;OTEL_EXPORTER_OTLP_PROTOCOL&lt;/code&gt; (to specify that the &lt;a href=&quot;https://opentelemetry.io/docs/languages/sdk-configuration/otlp-exporter/#otel_exporter_otlp_protocol&quot;&gt;protocol&lt;/a&gt; used is HTTP), and the &lt;code class=&quot;language-text&quot;&gt;OTEL_EXPORTER_OTLP_ENDPOINT&lt;/code&gt; (configuration of the &lt;a href=&quot;https://opentelemetry.io/docs/languages/sdk-configuration/otlp-exporter/#otel_exporter_otlp_endpoint&quot;&gt;OTLP exporter&lt;/a&gt;).&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# docker-compose.yaml (inside date-service)&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;environment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;OTEL_SERVICE_NAME&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; date&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;service
  &lt;span class=&quot;token key atrule&quot;&gt;OTEL_TRACES_EXPORTER&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; otlp
  &lt;span class=&quot;token key atrule&quot;&gt;OTEL_EXPORTER_OTLP_PROTOCOL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; http/protobuf
  &lt;span class=&quot;token key atrule&quot;&gt;OTEL_EXPORTER_OTLP_ENDPOINT&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; http&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;//otel&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;collector&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;4318&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now you can see traces in the explorer session in Grafana. Now let&apos;s &lt;a href=&quot;https://opentelemetry.io/docs/languages/python/getting-started/#add-manual-instrumentation-to-automatic-instrumentation&quot;&gt;manually&lt;/a&gt; instrument the Python application.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;python&quot;&gt;&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; logging
&lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; datetime &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; datetime

&lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; fastapi &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; FastAPI&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; HTTPException
&lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; opentelemetry &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; trace

&lt;span class=&quot;token comment&quot;&gt;# Logger&lt;/span&gt;
logging&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;basicConfig&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;level&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;logging&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;DEBUG&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;%(asctime)s - %(levelname)s - %(message)s&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
logger &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; logging&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;getLogger&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;__name__&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Opentelemetry&lt;/span&gt;
tracer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; trace&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;get_tracer&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;diceroller.tracer&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# App&lt;/span&gt;
app &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; FastAPI&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Routes&lt;/span&gt;
&lt;span class=&quot;token decorator annotation punctuation&quot;&gt;@app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/health&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;healthcheck&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    logger&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;info&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Checking status of application...&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&quot;App Name&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Date Service&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&quot;status&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;success&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&quot;message&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;OK&quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;


&lt;span class=&quot;token decorator annotation punctuation&quot;&gt;@app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/date&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;get_date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;with&lt;/span&gt; tracer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;start_as_current_span&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;date&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; span&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
            day &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; datetime&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;today&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;weekday&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            span&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;set_attribute&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;date.value&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; day&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            logger&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;info&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-interpolation&quot;&gt;&lt;span class=&quot;token string&quot;&gt;f&quot;Successfully able to get current day (&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;day&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;)&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token string&quot;&gt;&quot;status&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;success&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;token string&quot;&gt;&quot;date&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; day
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;except&lt;/span&gt; Exception &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
            logger&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;error&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-interpolation&quot;&gt;&lt;span class=&quot;token string&quot;&gt;f&quot;Not able to get current day: &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            span&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;set_attribute&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;date.value&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; HTTPException&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;500&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token string&quot;&gt;&quot;status&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;failed&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;token string&quot;&gt;&quot;message&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Something went wrong... Try again&quot;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now we can see this new span from Grafana:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/8dd994581f57755f674435667cd7cc88/76823/trace-date-service.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 84.17721518987341%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAARCAYAAADdRIy+AAAACXBIWXMAAAsTAAALEwEAmpwYAAAC2UlEQVR42o2Ua5bbRBCFtQbGlqy3uqVWtyTrYUm2xwyQ5EDw5DGEFWQ5bPtyuz2BzOFA+PGdapVK5apb1fZyoaGEQEF7t4lI6Ox3dzvHxo/RtBOa7gDTHjBOZxzmC3Qzoqr3UGaA1i2a4ycMH/6Al0sN062o+UJUDbp+dcGiMpB1i0wopEX1guSZL89SNeinEzR/2MtEhfXyA4bliJ6cHl5hPn2PbljQ7Odbgrz8JoeV3/QzvF2SoWwbVF0L2RgIoyG0RlEaZEWNJJOI02+TZCWiRMIrSo3l+MDsC/rxiMnq046uDVk3yCRbZhf/B1upZ3Uz7YCcHyrdUc/RnTdBiO0ugh/G/8o2iBybr/BKCp9LK27pCClBEKXO/hdxVrj4JJeUp0ZO7AC9dpgxrhcM8wl7tmvWe2raYpcKbOMUfpz9A+sPM4GEbUZMmFG2vNJsmwnrmhrOBzR2GEWBspSoVYmK9mtU9dJXOgT6fYc9CzBacR81vCTcIQnuEPs3stBHHgWIg40joi+NfGTEnp3f3yBlnI1N+DzIEHUSQCQhNaxqqNqg5rYLahlGMaI4QZLmyPICeSH4fPPJSiFO0tu5VM7+dWa8pI7eeV3w8e1r/HSe8bCMWPcak+GiNlz4rsbj0eDdWuP9UePpvsN1UXgkH8+Ns49893TZ4/ObHh8Y6/W1ZGCLK19cnwMsNsnbWWGZeupXQVGvcegxTxNa6j32PTpehK7RWBcW9csbnBfelJHVfLo37hefaJ/ON34jtirT8BaZvVvw0MpAG3PCjly4VtNCIpXaLbdL+PulYcK/k31J+I4Ja6W4+D310wi46HfbLXwufUjttsEOWz9wvq3PoXE/vV6XeM/qfl019WpwpQ72fCU/zzVEWXLPaJV21SWsJuPwhDLuueCgKtPC7rPmjfMUHa/vV/x4mvHqvOA8thyK5FBKTMROO+Ay2z+RFzz7bMuWnJPu9gP+BM7E55bQbCOsAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;A screenshot of Date Service trace in Grafana.&quot;
        title=&quot;&quot;
        src=&quot;/static/8dd994581f57755f674435667cd7cc88/f058b/trace-date-service.png&quot;
        srcset=&quot;/static/8dd994581f57755f674435667cd7cc88/c26ae/trace-date-service.png 158w,
/static/8dd994581f57755f674435667cd7cc88/6bdcf/trace-date-service.png 315w,
/static/8dd994581f57755f674435667cd7cc88/f058b/trace-date-service.png 630w,
/static/8dd994581f57755f674435667cd7cc88/40601/trace-date-service.png 945w,
/static/8dd994581f57755f674435667cd7cc88/76823/trace-date-service.png 1038w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Gym Service (NodeJS)&lt;/h4&gt;
&lt;p&gt;Here, we are going to do the same thing we did in the Python service above: add automatic instrumentation and add manual instrumentation.&lt;/p&gt;
&lt;p&gt;For the &lt;a href=&quot;https://opentelemetry.io/docs/languages/js/automatic/&quot;&gt;automatic&lt;/a&gt; instrumentation, we need to install &lt;code class=&quot;language-text&quot;&gt;@opentelemetry/api&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;@opentelemetry/auto-instrumentations-node&lt;/code&gt;. To auto instrument &lt;a href=&quot;@opentelemetry/auto-instrumentations-node&quot;&gt;express&lt;/a&gt; framework we will install &lt;code class=&quot;language-text&quot;&gt;@opentelemetry/instrumentation-http&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;@opentelemetry/instrumentation-express&lt;/code&gt;. To run the auto instrumentation agent we will also need to modify the command that starts the application to add the flag &lt;code class=&quot;language-text&quot;&gt;--require @opentelemetry/auto-instrumentations-node/register&lt;/code&gt;. This means that our new Dockerfile will be:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;FROM node&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;20&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;alpine as build

WORKDIR /app
COPY package.json ./
RUN yarn
COPY . .
RUN yarn build


FROM node&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;20&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;alpine as runner

WORKDIR /app
COPY package.json ./
RUN yarn &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;prod
COPY &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;from=build /app/dist ./
EXPOSE 8000
CMD &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;node&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;--require&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;@opentelemetry/auto-instrumentations-node/register&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;index.js&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The environment variables are also needed:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# docker-compose.yaml (inside gym-service)&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;environment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;OTEL_SERVICE_NAME&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; gym&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;service
  &lt;span class=&quot;token key atrule&quot;&gt;OTEL_TRACES_EXPORTER&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; otlp
  &lt;span class=&quot;token key atrule&quot;&gt;OTEL_EXPORTER_OTLP_PROTOCOL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; http/protobuf
  &lt;span class=&quot;token key atrule&quot;&gt;OTEL_EXPORTER_OTLP_ENDPOINT&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; http&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;//otel&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;collector&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;4318&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now let&apos;s &lt;a href=&quot;https://opentelemetry.io/docs/languages/js/getting-started/nodejs/#instrumentation&quot;&gt;manually&lt;/a&gt; instrument the application. The following packages need to be installed: &lt;code class=&quot;language-text&quot;&gt;@opentelemetry/sdk-node&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;@opentelemetry/api&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;@opentelemetry/sdk-trace-node&lt;/code&gt;. And since we want to export the traces via OTLP we will also need to install &lt;code class=&quot;language-text&quot;&gt;@opentelemetry/exporter-trace-otlp-proto&lt;/code&gt; to use the &lt;a href=&quot;https://opentelemetry.io/docs/languages/js/exporters/#otlp-dependencies&quot;&gt;OTLP exporter&lt;/a&gt; to the OpenTelemetry Collector. We can &lt;a href=&quot;https://opentelemetry.io/docs/languages/js/instrumentation/#create-spans&quot;&gt;add a new span&lt;/a&gt; to the trace manually:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; express &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;express&apos;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; bodyParser &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;body-parser&apos;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; cors &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;cors&apos;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; createLogger&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; format&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; transports &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;winston&apos;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; axios &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;axios&apos;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; trace &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;@opentelemetry/api&apos;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Logger&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; combine&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; timestamp&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; json &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; format
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; logger &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;createLogger&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  format&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;combine&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;timestamp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  transports&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;transports&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// OpenTelemetry&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; tracer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; trace&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getTracer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;gym.tracer&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// App&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; app &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;express&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;express&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;cors&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;bodyParser&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;listen&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;8000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Routes&lt;/span&gt;
app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;/health&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;req&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; res&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  logger&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Checking status of application...&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  res&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token string-property property&quot;&gt;&quot;App Name&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Gym Service&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string-property property&quot;&gt;&quot;status&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;success&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string-property property&quot;&gt;&quot;message&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;OK&quot;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;/gym&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;req&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; res&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; span &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; tracer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;startSpan&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;gym&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; dateResponse&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;any&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; axios&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
      &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;http://&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;process&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;env&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;DATE_SERVICE_HOSTNAME&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;process&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;env&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;DATE_SERVICE_PORT&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;/date&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    logger&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Able to get the date from date service&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;dateResponse&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;date &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; dateResponse&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;date &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      span&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;gym.shouldGo&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      res&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        status&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;success&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        message&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Day off! No need to go to the gym today...&quot;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      span&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;gym.shouldGo&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      res&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        status&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;success&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        message&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Let&apos;s work out! You need to go to the gym today...&quot;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;error&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    logger&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;Unable to get current date: &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;error&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    res&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;500&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      status&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;failed&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      message&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Something went wrong... Try again later.&quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;finally&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    span&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now, when checking Grafana we can see two cool things: a new span being added manually and the full trace showing spans of our two services!&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/14231a478b616b7c2a6f21927b0fb486/75609/trace-gym-service.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 83.54430379746836%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAARCAYAAADdRIy+AAAACXBIWXMAAAsTAAALEwEAmpwYAAADEUlEQVR42nWUaZbbNhCEeYTEHmtEStwXcN93kaI0M7EzSe5/nUoR8uTZecmP73UTEgoNdAFKWjTIyhZ51SKreoRFDdPzoOomjmf9fzFdnwQSL0wkThBBCdMSed3DjxIYbggjiGEFAppu46Ce8azpP3MyJMezwUUtuPy/YfvQbU+iGI4NN+RqnkNBC0Ei4MUCTuhJbOJxzE9C2MKD5TvfcVmRx/9Hcr4jXIr7UFRNg6oe4VDQ42BaxwgzAZEEqMsEbRGjyiP0zIssgnbWJLZr42ycoZ6OMCyD6NANA0rTdej6AW3XI81z2LYDx3UlcUzBYT/fDHVbYlomlIxFU2BaZ1R9LfOR48NlICMFxx73r68YOLjcr2imHllTIi4zSTV3aJYBKYXiOpfxI08o9mMuuAs2JcV8WzBwxV1wui4o2prbThDE3KJhIclLnM1Hkw7ag71B/5UrMbe5vmwUusi4C0d5iqhIIbJYdjNMM5wMG0/PJ3w5EgpLmB9+yPeoxFmKCyvs5h711KIe6cs6Y3MyJFVKfwnMLxfMbxd0Wy9prx3atZOxWx/f/W1A1hVQ8r7C9ucNl28Lpt84cefrwu8V/X1k5Vd5liG7nfKc8q7iwh3G7YLl9YaVNCymXydM2xVKRsErBZf3K9Y/NsmyR44NrzNsX9C4AU6mKw19MhzeDsHxiGN0hBdKc4dJgTitHhV+CEp+f7C+8zxfJliOiyDk7bFdHA/P+PT0hTzh189PMt/jL58+y/ygahQcKPjXzxXu7IuMrNAVIUSUwuddtXm1VDbJcnya2mK1FsdjvgUNwjiD7Qk2pcxxeVsx3mb024hqaiT1TEOzes205EMRJLz8glsVARGSMy+Bzsr3h8IVMUwupPhRhHlb0NLQRVshLh6W+bCNapg8Ox3DfZILN8ve4V7Gvfrdpx8elD50vADfbne8LayyrNDRl2UYIeeLk/gBXx1TelF3HE62UXs+3osSG6+lxcrU778/oglln9CXNV4nGrvtMVcNcp5F6niIHe+fCceTLuOB0QlCZHkhz/P4L/4Gfkv7Xq/HhQ4AAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;A screenshot of Gym Service trace in Grafana.&quot;
        title=&quot;&quot;
        src=&quot;/static/14231a478b616b7c2a6f21927b0fb486/f058b/trace-gym-service.png&quot;
        srcset=&quot;/static/14231a478b616b7c2a6f21927b0fb486/c26ae/trace-gym-service.png 158w,
/static/14231a478b616b7c2a6f21927b0fb486/6bdcf/trace-gym-service.png 315w,
/static/14231a478b616b7c2a6f21927b0fb486/f058b/trace-gym-service.png 630w,
/static/14231a478b616b7c2a6f21927b0fb486/40601/trace-gym-service.png 945w,
/static/14231a478b616b7c2a6f21927b0fb486/75609/trace-gym-service.png 994w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3&gt;Metrics&lt;/h3&gt;
&lt;p&gt;Metrics SDKs are also &lt;a href=&quot;https://opentelemetry.io/status/&quot;&gt;stable&lt;/a&gt; for Python and Javascript. Just like what we did in the traces section, we are going to perform five (5) steps: deploy metric backend (Prometheus), add Prometheus data source to Grafana, configure the OpenTelemetry Collector, instrument Date service, and instrument Gym service.&lt;/p&gt;
&lt;h4&gt;Prometheus&lt;/h4&gt;
&lt;p&gt;Prometheus &lt;a href=&quot;https://prometheus.io/docs/prometheus/latest/installation/#using-docker&quot;&gt;deployment&lt;/a&gt; will be done via Docker Compose and it&apos;s very easy for basic configuration:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;prometheus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; prom/prometheus&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;v2.50.0
  &lt;span class=&quot;token key atrule&quot;&gt;container_name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; prometheus
  &lt;span class=&quot;token key atrule&quot;&gt;networks&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; observability
  &lt;span class=&quot;token key atrule&quot;&gt;ports&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; 9090&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;9090&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;volumes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; ./config/prometheus.yaml&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;/etc/prometheus/config.yaml
  &lt;span class=&quot;token key atrule&quot;&gt;command&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;config.file=/etc/prometheus/config.yaml &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;enable&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;feature=otlp&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;write&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;receiver&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Prometheus is best used as a pull-based system, which means, he is best used when it goes directly to the applications and collects the metrics instead of the system/collector sending the metrics to Prometheus. But, since we want to use the OTLP protocol we can configure the &lt;a href=&quot;https://prometheus.io/docs/prometheus/latest/feature_flags/#otlp-receiver&quot;&gt;OTLP Receiver&lt;/a&gt; in Prometheus, allowing it to receive metrics via an HTTP request (this is considered an &lt;a href=&quot;https://prometheus.io/docs/prometheus/latest/querying/api/#otlp-receiver&quot;&gt;inefficient&lt;/a&gt; way of ingesting metrics).&lt;/p&gt;
&lt;p&gt;The config file used was simple, just to allow Prometheus to auto-collect its logs.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;global&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;scrape_interval&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 15s

&lt;span class=&quot;token key atrule&quot;&gt;scrape_configs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;job_name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; prometheus
    &lt;span class=&quot;token key atrule&quot;&gt;static_configs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;targets&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; localhost&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;9090&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4&gt;OpenTelemetry Collector&lt;/h4&gt;
&lt;p&gt;The OpenTelemetry Collector needs to be configured to forward metrics to the OTLP endpoint of Prometheus. This is easily done by editing the Otel Collector configuration file:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# https://opentelemetry.io/docs/collector/configuration/#receivers&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;receivers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;otlp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;protocols&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;http&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;endpoint&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0.0.0.0&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;4318&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# https://opentelemetry.io/docs/collector/configuration/#exporters&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;exporters&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;otlphttp/tempo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;endpoint&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; http&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;//tempo&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;4318&lt;/span&gt;

  &lt;span class=&quot;token key atrule&quot;&gt;otlphttp/prometheus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;endpoint&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; http&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;//prometheus&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;9090/api/v1/otlp

&lt;span class=&quot;token comment&quot;&gt;# https://opentelemetry.io/docs/collector/configuration/#pipelines&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;service&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;pipelines&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;traces&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;receivers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;otlp&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;exporters&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;otlphttp/tempo&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;metrics&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;receivers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;otlp&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;exporters&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;otlphttp/prometheus&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Notice that we can use several exporters/receivers, if we want to use the same type of exporter/receiver we just need the &lt;code class=&quot;language-text&quot;&gt;/&amp;lt;NAME&gt;&lt;/code&gt; suffix. The &lt;code class=&quot;language-text&quot;&gt;&amp;lt;NAME&gt;&lt;/code&gt; can be anything you want: a number, or a name... but I used the name of the service to be more meaningful and easy to understand.&lt;/p&gt;
&lt;h4&gt;Date Service (Python)&lt;/h4&gt;
&lt;p&gt;Adding a metric is as easy as adding a trace, but before adding the metric we need to properly configure the application to export the metrics correctly via OTLP to the OpenTelemetry Collect with the following &lt;a href=&quot;https://opentelemetry.io/docs/languages/sdk-configuration/otlp-exporter/#otel_exporter_otlp_metrics_protocol&quot;&gt;environment variables&lt;/a&gt;:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# docker-compose.yaml (inside date-service)&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;environment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;OTEL_EXPORTER_OTLP_METRICS_PROTOCOL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; http/protobuf&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now, let&apos;s manually create the metrics in our application. It will be similar to what we did in the traces session: get a &lt;code class=&quot;language-text&quot;&gt;meter&lt;/code&gt;, create a metric instrument (I chose a &lt;code class=&quot;language-text&quot;&gt;counter&lt;/code&gt; to count the number of requests the &lt;code class=&quot;language-text&quot;&gt;/date&lt;/code&gt; endpoint received), modify the metric instrument (adding one to the metric when the &lt;code class=&quot;language-text&quot;&gt;/date&lt;/code&gt; endpoint is called).&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;python&quot;&gt;&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; logging
&lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; datetime &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; datetime

&lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; fastapi &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; FastAPI&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; HTTPException
&lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; opentelemetry &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; metrics&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; trace

&lt;span class=&quot;token comment&quot;&gt;# Logger&lt;/span&gt;
logging&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;basicConfig&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;level&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;logging&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;DEBUG&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;%(asctime)s - %(levelname)s - %(message)s&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
logger &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; logging&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;getLogger&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;__name__&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Opentelemetry&lt;/span&gt;
tracer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; trace&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;get_tracer&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;date.tracer&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
meter &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; metrics&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;get_meter&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;date.meter&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

date_counter &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; meter&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;create_counter&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;requests_counter_get_date&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    description&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Counts the number of requests that the /date endpoint received&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# App&lt;/span&gt;
app &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; FastAPI&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Routes&lt;/span&gt;
&lt;span class=&quot;token decorator annotation punctuation&quot;&gt;@app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/health&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;healthcheck&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    logger&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;info&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Checking status of application...&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&quot;App Name&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Date Service&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&quot;status&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;success&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&quot;message&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;OK&quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;


&lt;span class=&quot;token decorator annotation punctuation&quot;&gt;@app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/date&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;get_date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    date_counter&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;add&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;with&lt;/span&gt; tracer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;start_as_current_span&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;date&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; span&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
            day &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; datetime&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;today&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;weekday&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            span&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;set_attribute&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;date.value&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; day&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            logger&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;info&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-interpolation&quot;&gt;&lt;span class=&quot;token string&quot;&gt;f&quot;Successfully able to get current day (&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;day&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;)&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token string&quot;&gt;&quot;status&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;success&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;token string&quot;&gt;&quot;date&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; day
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;except&lt;/span&gt; Exception &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
            logger&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;error&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-interpolation&quot;&gt;&lt;span class=&quot;token string&quot;&gt;f&quot;Not able to get current day: &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            span&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;set_attribute&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;date.value&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; HTTPException&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;500&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token string&quot;&gt;&quot;status&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;failed&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;token string&quot;&gt;&quot;message&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Something went wrong... Try again&quot;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If we deploy the stack using Docker Compose and make some requests to &lt;code class=&quot;language-text&quot;&gt;localhost:3000/date&lt;/code&gt; we will see the metric in Grafana, as you can see below:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/506397a2624fc5afecc258f280674d5b/1628f/metric-date-service.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 65.82278481012659%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAYAAACpUE5eAAAACXBIWXMAAAsTAAALEwEAmpwYAAABrklEQVR42pWTSZLTQBBFdYm2pa55kkpT23K7uwlYEKxYcRLOwNk/mSUjuwMTwOLFV00/szJLVeqe0MaM5FsIHbCrJfaNJlUbPM79AXk4Ypyf0Y8L2vxE32ek1COfvuLl2w9MX76j8mnA8vIJ8/KK1M/w7QBpIxFuiGiUxaNyhYZVe2JVZR3GaYKxBpUNHUU+QJlIkyf40EPSpjbPSN1U1MeBsjoi0tjSuqOxi/07YjfD0Fql6arnt8+U5UdYSt9RxoX4Hj4Q2hF8o3u4NML4DpWiKw2HV+TDCXGc0c5HdEQiA84o5bmYcRZ/MmM4WDGMecJy/lCK/UxZDtMCaQK0S1AuFtWka72ucE1LXS/frLU03BSqmaEC06QiI158aAR2Fx7qlV0jN3hs/BpIkBEnwGcFNauS1mMv1EYtNWrFmBs16/wNwpCR81c8d92yYcBeqt8O/AuNuQZqKCgnVPF1t8z+x0ybwu3cXui/G95dExdDdceQa3HfbD0g6C94tHZVc1XpuLu61I33sxZDExK9sxGho8ebxwJ/h7ZfO+hC2ax93JQDrUov49c6dZxr+BM0MGdlDA1GrwAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;A screenshot of Date Service trace in Grafana.&quot;
        title=&quot;&quot;
        src=&quot;/static/506397a2624fc5afecc258f280674d5b/f058b/metric-date-service.png&quot;
        srcset=&quot;/static/506397a2624fc5afecc258f280674d5b/c26ae/metric-date-service.png 158w,
/static/506397a2624fc5afecc258f280674d5b/6bdcf/metric-date-service.png 315w,
/static/506397a2624fc5afecc258f280674d5b/f058b/metric-date-service.png 630w,
/static/506397a2624fc5afecc258f280674d5b/40601/metric-date-service.png 945w,
/static/506397a2624fc5afecc258f280674d5b/1628f/metric-date-service.png 1232w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Gym Service (NodeJS)&lt;/h4&gt;
&lt;p&gt;As you can already tell, we are going to perform the exact same things: add the environment variable, create a &lt;a href=&quot;https://opentelemetry.io/docs/languages/js/instrumentation/#initializing-metrics-with-sdk-metrics&quot;&gt;meter&lt;/a&gt;, create a metric instrument, and modify the metric instrument.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# docker-compose.yaml (inside gym-service)&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;environment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;OTEL_EXPORTER_OTLP_METRICS_PROTOCOL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; http/protobuf&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Before adding the code we will also need to add &lt;code class=&quot;language-text&quot;&gt;@opentelemetry/sdk-metrics&lt;/code&gt; &lt;a href=&quot;https://opentelemetry.io/docs/languages/js/getting-started/nodejs/&quot;&gt;package&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; express &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;express&apos;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; bodyParser &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;body-parser&apos;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; cors &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;cors&apos;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; createLogger&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; format&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; transports &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;winston&apos;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; axios &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;axios&apos;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; trace&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; metrics &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;@opentelemetry/api&apos;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  MeterProvider&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  PeriodicExportingMetricReader
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;@opentelemetry/sdk-metrics&apos;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; Resource &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;@opentelemetry/resources&apos;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; opentelemetry &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;@opentelemetry/api&apos;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; OTLPMetricExporter &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;@opentelemetry/exporter-metrics-otlp-proto&apos;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; SemanticResourceAttributes &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;@opentelemetry/semantic-conventions&apos;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// Logger&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; combine&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; timestamp&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; json &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; format
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; logger &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;createLogger&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  format&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;combine&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;timestamp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  transports&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;transports&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// OpenTelemetry&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; myServiceMeterProvider &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MeterProvider&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  resource&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Resource&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;merge&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Resource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;SemanticResourceAttributes&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;SERVICE_NAME&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; process&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;env&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;OTEL_SERVICE_NAME&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  readers&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PeriodicExportingMetricReader&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    exporter&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;OTLPMetricExporter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Set this MeterProvider to be global to the app being instrumented.&lt;/span&gt;
opentelemetry&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;metrics&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setGlobalMeterProvider&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;myServiceMeterProvider&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; tracer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; trace&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getTracer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;gym.tracer&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; meter &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; metrics&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getMeter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;gym.meter&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; gymCounter &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; meter&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createCounter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;requests_counter_get_gym&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  description&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Counts the number of requests that the /gym endpoint received&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// App&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; app &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;express&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;express&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;cors&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;bodyParser&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;listen&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;8000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Routes&lt;/span&gt;
app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;/health&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;req&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; res&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  logger&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Checking status of application...&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  res&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token string-property property&quot;&gt;&quot;App Name&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Gym Service&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string-property property&quot;&gt;&quot;status&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;success&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string-property property&quot;&gt;&quot;message&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;OK&quot;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;/gym&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;req&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; res&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; span &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; tracer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;startSpan&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;gym&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  gymCounter&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; dateResponse&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;any&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; axios&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
      &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;http://&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;process&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;env&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;DATE_SERVICE_HOSTNAME&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;process&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;env&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;DATE_SERVICE_PORT&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;/date&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    logger&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Able to get the date from date service&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;dateResponse&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;date &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; dateResponse&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;date &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      span&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;gym.shouldGo&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      res&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        status&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;success&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        message&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Day off! No need to go to the gym today...&quot;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      span&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;gym.shouldGo&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      res&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        status&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;success&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        message&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Let&apos;s work out! You need to go to the gym today...&quot;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;error&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    logger&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;Unable to get current date: &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;error&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    res&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;500&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      status&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;failed&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      message&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Something went wrong... Try again later.&quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;finally&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    span&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And we can visualize this metric in Grafana.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/57ffcaceea3f7d5d4d88ec070e74b907/b5dee/metric-gym-service.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 65.82278481012659%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAYAAACpUE5eAAAACXBIWXMAAAsTAAALEwEAmpwYAAABsElEQVR42o2TybLaMBBF/ROAsax5QB4wj0dIqjIts8l35B/y9zfdMjawoPIWt25r8OlWS65cHBBDh+gihPLY1i12tcJ2JymWq6c84dCd0A3vyP0bQhpLHGJGevuFT7//Yvj5B5XxGefrdxzPX5C6CT71aE2AJLU3cdxIi0bZ2UlCuXlMrozDME7QxqJSNpTMrQ6U8QxP1ba0KR6OCKSYRziay7xGVXEBlsaPMiRPe7XLqFrtcfn8g6r8BhvnRbNu7m/qCtynAS72LzQQMM3A7nhFns4IPVU0npCGEwHGUhGDuF9cpSP4kx6AnKwAefB+/YpxuuBCVbJzz3hR2QjtE7RNDz177uUak9etJmDI1D+HvTSrb+imN7XAZn9z0pbiRTzmZMrwy7Dlu9b4Aq5EIatVe6n/L6qEvxOaYNZBGHZb1grwI5BGmftY3eaMofllXWMn5MeBC4gh7ELfYUtCPmH1lPnF8Vi8udHz3lctKsDWuHsGqdajsbPmHhlI7yHsfAE8z/1rbn1cvBYENPEAl+hf7ujN5QGpP5bfj2PeKK0vAOVCcUm3WW52cUqwOL+Af6pNZ+zQAmQxAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;A screenshot of Gym Service trace in Grafana.&quot;
        title=&quot;&quot;
        src=&quot;/static/57ffcaceea3f7d5d4d88ec070e74b907/f058b/metric-gym-service.png&quot;
        srcset=&quot;/static/57ffcaceea3f7d5d4d88ec070e74b907/c26ae/metric-gym-service.png 158w,
/static/57ffcaceea3f7d5d4d88ec070e74b907/6bdcf/metric-gym-service.png 315w,
/static/57ffcaceea3f7d5d4d88ec070e74b907/f058b/metric-gym-service.png 630w,
/static/57ffcaceea3f7d5d4d88ec070e74b907/40601/metric-gym-service.png 945w,
/static/57ffcaceea3f7d5d4d88ec070e74b907/b5dee/metric-gym-service.png 1237w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2&gt;Cya!&lt;/h2&gt;
&lt;p&gt;I hope you liked this new blog post! OpenTelemetry is the hype topic of the moment about observability and the hype is well deserved!&lt;/p&gt;
&lt;p&gt;You can check the &lt;a href=&quot;https://github.com/felipelaptrin/opentelemetry-blog&quot;&gt;entire code repository&lt;/a&gt; or check the individual PR below:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/felipelaptrin/opentelemetry-blog/pull/1&quot;&gt;Adding Loki&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/felipelaptrin/opentelemetry-blog/pull/2&quot;&gt;Adding Fluentbit&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/felipelaptrin/opentelemetry-blog/pull/3&quot;&gt;Adding Grafana&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/felipelaptrin/opentelemetry-blog/pull/4&quot;&gt;Adding Tempo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/felipelaptrin/opentelemetry-blog/pull/5&quot;&gt;Adding OTelCollector&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/felipelaptrin/opentelemetry-blog/pull/6&quot;&gt;Adding Tempo as a new data source to Grafana&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/felipelaptrin/opentelemetry-blog/pull/7&quot;&gt;Adding Traces to Date Service (python)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/felipelaptrin/opentelemetry-blog/pull/8&quot;&gt;Adding Traces to Gym Service (nodejs)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/felipelaptrin/opentelemetry-blog/pull/9&quot;&gt;Adding Prometheus&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/felipelaptrin/opentelemetry-blog/pull/10&quot;&gt;Adding OTel Collector config to forward metrics to Prometheus&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/felipelaptrin/opentelemetry-blog/pull/11&quot;&gt;Adding Prometheus as a new data source to Grafana&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/felipelaptrin/opentelemetry-blog/pull/12&quot;&gt;Adding Metrics to Date Service (python)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/felipelaptrin/opentelemetry-blog/pull/13&quot;&gt;Adding Metrics to Gym Service (nodejs)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;See you around! 👋&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Get started with OpenTelemetry right now!]]></title><description><![CDATA[OpenTelemetry (as known as OTel) is an open-source project (maintained by CNCF, the same foundation that maintains Kubernetes) that aims to…]]></description><link>https://felipetrindade.com/opentelemetry/</link><guid isPermaLink="false">https://felipetrindade.com/opentelemetry/</guid><pubDate>Fri, 23 Feb 2024 10:40:32 GMT</pubDate><content:encoded>&lt;p&gt;OpenTelemetry (as known as OTel) is an open-source project (maintained by CNCF, the same foundation that maintains Kubernetes) that aims to help developers instrument their code in a more standardized way. This project is a merge of two other projects: &lt;a href=&quot;https://opentracing.io/&quot;&gt;OpenTracing&lt;/a&gt; and &lt;a href=&quot;https://opencensus.io/&quot;&gt;OpenCensus&lt;/a&gt;. It provides several components, most notably:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;APIs and SDK&lt;/strong&gt;: per programming language for generating and emitting telemetry&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Collector&lt;/strong&gt;: a component that receives, processes and export telemetry to the observability backend&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;OTLP&lt;/strong&gt;: a protocol for transmitting telemetry data&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This documentation was divided into the following parts (in order):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Concepts&lt;/strong&gt;: Some frequently used terms in observability and OTel&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Signals&lt;/strong&gt;: We will talk about the main components (traces, metrics, logs) used to make an application observable&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Instrumentation&lt;/strong&gt;: Here we will cover what needs to be done in your application to emit signals&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;OpenTelemetry&lt;/strong&gt;: Architecture, reasons to exist, components...&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Concepts&lt;/h2&gt;
&lt;p&gt;OTel requires you to know several important words used in observability, the most important ones for you to know are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Observability&lt;/strong&gt;: Observability lets us observe our system, making it easier to answer the question &quot;Why is this happening?&quot;. It&apos;s very helpful to troubleshoot and handle problems. To have observability your code must be instrumented.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Observability Backend&lt;/strong&gt;: OTel does not provide storage and visualization for collected telemetry data, that&apos;s where an observability backend is needed. Examples of backends: &lt;a href=&quot;https://www.jaegertracing.io/&quot;&gt;Jaeger&lt;/a&gt;, &lt;a href=&quot;https://prometheus.io/&quot;&gt;Prometheus&lt;/a&gt;, &lt;a href=&quot;https://zipkin.io/&quot;&gt;Zipkin&lt;/a&gt;, &lt;a href=&quot;https://grafana.com/docs/loki/latest/&quot;&gt;Loki&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Instrumentation&lt;/strong&gt;: The application code emits signals such as logs, metrics and traces.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Signals&lt;/strong&gt;: Categories of telemetry supported. At the moment it supports traces, metrics, logs and baggage.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Logs&lt;/strong&gt;: Timestamped messages that help you understand at a code level what happened. Since they lack contextual information (such as where they were called from in a distributed system), they are not extremely useful for tracking code alone but are way more useful when included as part of a span.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Span&lt;/strong&gt;: Represents a unit of work or operation such as a request made, or query made to the database. It contains name, log message and attributes.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Trace&lt;/strong&gt;: It&apos;s the &quot;path&quot; or &quot;route&quot; made by requests that propagate among services. Traces are what improve visibility in distributed systems/microservice architectures. Traces are composed of one or more spans.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Metrics&lt;/strong&gt;: Aggregations over time about your infrastructure/application. It can be related to the infrastructure itself, like CPU usage, memory usage, and requests per second or it can be related to the business, like sales per second.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Signals&lt;/h2&gt;
&lt;h3&gt;Traces&lt;/h3&gt;
&lt;p&gt;Traces are super important to understand the full &quot;path&quot; that a request takes in your application, especially if you are using a microservice, mesh or multiple-service architecture. Technically, a &lt;code class=&quot;language-text&quot;&gt;trace&lt;/code&gt; is just a collection of JSONs (each one represents a &lt;code class=&quot;language-text&quot;&gt;span&lt;/code&gt;) where each JSON contains a name, log, attributes and timestamps. You can think of spans as structured logs on steroids (with context).&lt;/p&gt;
&lt;p&gt;Example of a span JSON:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Hello-Salutations&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;context&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;trace_id&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;0x5b8aa5a2d2c872e8321cf37308d69df2&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;span_id&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;0x93564f51e1abe1c2&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;parent_id&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;0x051581bf3cb55c13&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;start_time&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;2022-04-29T18:52:58.114492Z&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;end_time&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;2022-04-29T18:52:58.114631Z&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;attributes&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;http.route&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/cart&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;http.server_name&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;frontend&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;http.scheme&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;http&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;http.status_code&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;200&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;net.peer.port&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;10243&quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;events&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;hey there!&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;&quot;timestamp&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;2022-04-29T18:52:58.114561Z&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;&quot;attributes&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token property&quot;&gt;&quot;event_attributes&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The &quot;magic&quot; that allows us to trace the requests is the &lt;code class=&quot;language-text&quot;&gt;context&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;parent_id&lt;/code&gt; field. With these fields, the backend tracing tool can organize the spans and create the full trace of the request. Some points about this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If the &lt;code class=&quot;language-text&quot;&gt;parent_id&lt;/code&gt; is empty it means that it is the initial span (root)&lt;/li&gt;
&lt;li&gt;If two spans have the same &lt;code class=&quot;language-text&quot;&gt;trace_id&lt;/code&gt; it means they belong to the same trace&lt;/li&gt;
&lt;li&gt;One span is a child of another span if the &lt;code class=&quot;language-text&quot;&gt;parent_id&lt;/code&gt; of the first span is the same as the &lt;code class=&quot;language-text&quot;&gt;span_id&lt;/code&gt; of the latter.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Traces should be stored in a proper backend, popular open-source tools for that are Jaeger, Zipkin, Grafana Tempo and SigNoz. Some of these tools are just the backend for storing the traces and do not provide a graphical interface to see the traces. In these scenarios, you can always use a dashboard tool that can integrate with the backend to display the traces, a popular tool for that is &lt;a href=&quot;https://grafana.com/docs/grafana/latest/&quot;&gt;Grafana&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;A visual example of a trace can be seen below (please click on the picture to open in a separate tab and be able to visualize the image properly):&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/56e92a04f76503741ad3e2e79488d89f/f2f8c/trace.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 55.69620253164557%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAAsTAAALEwEAmpwYAAABxElEQVR42nWT23KbMBRF+YTGDncQFwE2wog7OC7uNM5M+/9ftHskp3bqcR/WgEBa2ugcjKKsUYga6a6En6RwAgbbDx9g/yWIuSbOCrAkg5EVAqJuEfE9EpIykvoshhdGcB+hze5jBo9FmiTb0fqcxCkM2/FgWja2ryY2my1eTROu68K2bTiOQ2ML31421/dbk+63GvVcjV9ojWXTPHrvuD4MKSXmeYSUB/A0QcZTcJ4gzznKfYHT0mIllrGm+w7zcMBxkjifBpxmia6pkPAMIWOIEw7jcKjw88eb5nTs0cqSJgn07ZUojiGbGl0nUew4RFkgzejcSBBGkb46fkCEsFRCUVW4vK9Yvy/ouxot7fiXhlDn5UcxpqlFPzSoacMky2EF16KoIjpB9FlMBqOsBD7ez5inDg1N/ipsKWlAMtMN6PBjsDTHxvZgURpVlKvsjuoILVQJl7knofg3oay0UAlU9Xmx1wtV6kfZTag++eNyxttx1Im6trqhpFrokTDlWqgWPZPdE4pSJzwuw+cnixsqsU99ZnmB7s003z1p+jtqniGbBr9/XbCuC8ZBYhqbG2qsCqD+BJWwrCVdqUWoPZ4Sc/wBGP03jzktV68AAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;A screenshot of the Grafana Explorer page showing what a trace looks like.&quot;
        title=&quot;&quot;
        src=&quot;/static/56e92a04f76503741ad3e2e79488d89f/f058b/trace.png&quot;
        srcset=&quot;/static/56e92a04f76503741ad3e2e79488d89f/c26ae/trace.png 158w,
/static/56e92a04f76503741ad3e2e79488d89f/6bdcf/trace.png 315w,
/static/56e92a04f76503741ad3e2e79488d89f/f058b/trace.png 630w,
/static/56e92a04f76503741ad3e2e79488d89f/40601/trace.png 945w,
/static/56e92a04f76503741ad3e2e79488d89f/78612/trace.png 1260w,
/static/56e92a04f76503741ad3e2e79488d89f/f2f8c/trace.png 1490w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;In the above example, you can see a trace of a &lt;code class=&quot;language-text&quot;&gt;POST&lt;/code&gt; HTTP request in the &lt;code class=&quot;language-text&quot;&gt;/poll&lt;/code&gt; endpoint. The request took 42.04ms and is composed of 130 spans (you would see the rest if you scroll down the UI of the trace). You have complete visibility of how long each process took and can tackle bottlenecks and understand the behavior of the request. One of the last traces there is a query to insert data to the database, let&apos;s check what this span looks like:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/4b9a87a5a65d73e8768e5ef4ff81c9f4/5e6a4/span.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 56.32911392405063%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAAsTAAALEwEAmpwYAAABlklEQVR42o2S607bQBBG8wbgy168V19iO3bUxCaQNglCAgSlUFDf/2W+jp22qYSQ+XG047X3jPXtzNrVBu3qAnn9Ba6skdgULDHgyh7RFtK4EaHdqSZi+k6aFDYr4Yoavqgw6/s1rg9brFcN+m6Jbt3+Y9irFxWyPEOWpSjmBXKqiyIf92+uL3F7c4WHux3ub79h0dSYHfYdfr0c8Pq0x+uPE2/PBzw9fEW/uUK5WEI7j3Rewc9Lem5RN0usux79xSXJF5iXx/3ZftfhjQTPjzu8fD/xkxo83m/hUw+lNGSWgxMip9V7MKUQSTkS/6mFTD4nDM7OIGrKt2kQ0WGeUs7OgVl7ZGhgDISQ08IhMxYzcOsQxTFCkofn5+8JAiitJ4R3W5RVhZxyk9ojEgIR4wgHMWNjfYJBKj39h867sTOnA4bCTzcbKMpRJAk4NeCcH6H3Sptp4TAunjIylJEqCthlO66CmjASMRL9xdIkfCpDQ5053eIQfESSgPIKKLdx/Q9Nwz4pHIbYkkjS7QqfIgzDD7EuxW8/30Y/Duoo5wAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;A screenshot of a span of a SQL query in a trace.&quot;
        title=&quot;&quot;
        src=&quot;/static/4b9a87a5a65d73e8768e5ef4ff81c9f4/f058b/span.png&quot;
        srcset=&quot;/static/4b9a87a5a65d73e8768e5ef4ff81c9f4/c26ae/span.png 158w,
/static/4b9a87a5a65d73e8768e5ef4ff81c9f4/6bdcf/span.png 315w,
/static/4b9a87a5a65d73e8768e5ef4ff81c9f4/f058b/span.png 630w,
/static/4b9a87a5a65d73e8768e5ef4ff81c9f4/40601/span.png 945w,
/static/4b9a87a5a65d73e8768e5ef4ff81c9f4/78612/span.png 1260w,
/static/4b9a87a5a65d73e8768e5ef4ff81c9f4/5e6a4/span.png 1469w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;The span contains several useful pieces of information to help us debug and understand of what is going on: query made, the user used to connect to the database, how long the query took...&lt;/p&gt;
&lt;h4&gt;Span telemetry components&lt;/h4&gt;
&lt;p&gt;Four parts are necessary to instrument our code to use traces:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Tracer&lt;/strong&gt;: Creates spans containing more information about what&apos;s happening for a given operation (e.g. query to the database). They are created by a tracer provider.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Tracer Provider&lt;/strong&gt;: It&apos;s the factory that creates a &lt;code class=&quot;language-text&quot;&gt;Tracer&lt;/code&gt;. Usually, it&apos;s a class that needs to be instantiated once but in some language SDKs, a global tracer is already initialized automatically.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Trace Exporter&lt;/strong&gt;: Send traces to a consumer such as STDOUT, OTel Collector or open source/vendor backend.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Trace Context&lt;/strong&gt;: As we mentioned, this is what makes it possible to correlate spans no matter where they were generated, enabling distributed tracing. A context (the &lt;code class=&quot;language-text&quot;&gt;context&lt;/code&gt; object in the span example) contains IDs that are related to the service that called/calls the request. Each context is associated with a span!&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;Span Information&lt;/h4&gt;
&lt;p&gt;Span contains the following information:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Name&lt;/li&gt;
&lt;li&gt;Parent ID (empty for root spans)&lt;/li&gt;
&lt;li&gt;Start and End timestamps&lt;/li&gt;
&lt;li&gt;Context&lt;/li&gt;
&lt;li&gt;Attributes (key-value pairs containing metadata)&lt;/li&gt;
&lt;li&gt;Events (logs about a meaningful event that happened during the span execution)&lt;/li&gt;
&lt;li&gt;Links (way to associate spans with one another)&lt;/li&gt;
&lt;li&gt;Status (set when there is a known error in the app code, accept values &lt;code class=&quot;language-text&quot;&gt;Unset&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;Ok&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;Error&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Kind (accept values &lt;code class=&quot;language-text&quot;&gt;Client&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;Server&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;Internal&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;Producer&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;Consumer&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Metrics&lt;/h3&gt;
&lt;p&gt;A metric is a measurement of a service in a given time that is extremely useful for monitoring the performance and availability of applications. OTel measurements are captured by &lt;code class=&quot;language-text&quot;&gt;metric instruments&lt;/code&gt; that are defined by:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Name&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Kind&lt;/strong&gt;: Here are the following types of metric instruments:
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Counter&lt;/strong&gt;: A value that accumulates over time and only goes up. E.g. Count the number of HTTP requests with 5XX status code&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Asynchronous Counter&lt;/strong&gt;: An asynchronous counter&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;UpDownCounter&lt;/strong&gt;: A value that accumulates over time but can also decrease. E.g. Number of items in a queue, Number of active requests&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Asynchronous UpDownCounter&lt;/strong&gt;: An asynchronous updowncounter&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Gauge&lt;/strong&gt;: Measures a current value at the time it is read. Notice that this is similar to UpDownCounter but the difference is that here you don&apos;t care about the rate of change. E.g. Amount of memory being used&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Histogram&lt;/strong&gt;: Aggregation of values, useful when you are interested in statistic values. E.g. Request latency/duration&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Unit&lt;/strong&gt; (Optional)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Description&lt;/strong&gt; (Optional)&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Metric telemetry components&lt;/h4&gt;
&lt;p&gt;The instrument our code to support metrics the following components are required (you will note a clear similarity with the Span components):&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Meter&lt;/strong&gt;: Creates metrics instruments, capturing measurements about the service at runtime (e.g. CPU usage). It is created by a &lt;code class=&quot;language-text&quot;&gt;Meter Provider&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Meter Provider&lt;/strong&gt;: Factory to create &lt;code class=&quot;language-text&quot;&gt;Meter&lt;/code&gt;. Usually, it&apos;s a class that needs to be instantiated once but in some language SDKs, a global meter provider is already initialized automatically.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Metric Exporter&lt;/strong&gt;: Send metrics to a consumer such as STDOUT, OTel Collector or open source/vendor backend.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;Logs&lt;/h3&gt;
&lt;p&gt;Logs are just timestamped text records with metadata. It&apos;s recommended to be structured (like a JSON) but it can be unstructured. They may also be attached to spans for extra visibility. The rule is: if it&apos;s not part of a trace or is not a metric then it is a log. A sample log (not in a JSON format):&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;I, [2021-02-23T13:26:23.505892 #22473]  INFO -- : [6459ffe1-ea53-4044-aaa3-bf902868f730] Started GET &quot;/&quot; for ::1 at 2021-02-23 13:26:23 -0800&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Another example of a standardized log (JSON format) is:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;timestamp&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;2024-02-23 12:58:30.949&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;level&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;info&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;message&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Creating a new poll&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;container_name&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/poll&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;source&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;stdout&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;container_id&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;21aa4fa96624ee698e319e7e8b6571de58&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;OTel approach to logs is a bit different than the approach to traces or metrics. Because logging solutions are widespread in all programming languages, OTel will be &quot;hidden&quot; behind these logging solutions. In other words, as a developer, you will choose your favorite logger library (it needs to be compatible with OpenTelemetry) and configure it to use a log appender/bridge to emit logs to OpenTelemetry LogRecordExporter. All the log components, such as &lt;code class=&quot;language-text&quot;&gt;Logger&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;Logger Provider&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;Log Record Exporter&lt;/code&gt; will be managed by the logger library that you use.&lt;/p&gt;
&lt;h3&gt;Baggage&lt;/h3&gt;
&lt;p&gt;Allow a way to propagate information across traces/signals. Since spans are immutable once created and can be exported before you need information on them you can use baggage to solve this problem. It should be used for storing non-sensitive data (it will be passed in HTTP headers). Common use cases are for account identification, user IDs, product IDs, and origin IPs. Baggage is not the same as span attributes!&lt;/p&gt;
&lt;h2&gt;Instrumentation&lt;/h2&gt;
&lt;p&gt;Once again: we need to instrument our code to emit signals and have observability in the system... but how can we do that? We can do that in two ways: automatic or manual. In the automatic way you don&apos;t even need to touch the code base of your application, while manually you will need to change it.&lt;/p&gt;
&lt;p&gt;At first sight, the automatic way looks way better, this is not true, it&apos;s just different. Of course, there is a trade-off but in the manual configuration, you can have a way more granular approach, which can help with your observability. For most languages you can use both approaches at the same time, gaining direct insights from automatic and granular observability with the manual instrumentation! Let&apos;s take a closer look.&lt;/p&gt;
&lt;h3&gt;Automatic Instrumentation&lt;/h3&gt;
&lt;p&gt;The automatic configuration will require you to add dependencies/packages to your application (API, SDK and instrumentation tools), add new environment variables to the application (name of the application, an endpoint to export the signals...) and modify the way the application starts (e.g. in Python instead of &lt;code class=&quot;language-text&quot;&gt;python main.py&lt;/code&gt; it will be &lt;code class=&quot;language-text&quot;&gt;opentelemetry-instrument python main.py&lt;/code&gt;, in JavaScript instead of &lt;code class=&quot;language-text&quot;&gt;node index.js&lt;/code&gt; it will be &lt;code class=&quot;language-text&quot;&gt;node --require @opentelemetry/auto-instrumentations-node/register index.js&lt;/code&gt;). So it&apos;s a change you will make in the Dockerfile and dependencies of the application, you won&apos;t need to change a single line of code!&lt;/p&gt;
&lt;h3&gt;Manual Instrumentation&lt;/h3&gt;
&lt;p&gt;If you decide to use the manual configuration you will need to manually instrument the application to call OpenTelemetry APIs. It turns out that most of the applications don&apos;t need this because the code mostly relies on some dependencies (e.g. web frameworks such as Express, Flask, Spring Boot) so you won&apos;t even need to deal with OTel APIs. In these scenarios, there are two possibilities:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Natively Instrumented&lt;/strong&gt;: the dependency you use can directly call OTel APIs (e.g. &lt;a href=&quot;https://nextjs.org/docs/app/building-your-application/optimizing/open-telemetry&quot;&gt;NextJS&lt;/a&gt;). There are only a few libraries that are natively instrumented, you will likely have to go for the next possibility.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Integration Libraries&lt;/strong&gt;: The open-source community created a library that integrates your dependency and OTel API. In case you want to check these libraries your best friend is the &lt;a href=&quot;https://opentelemetry.io/ecosystem/registry/&quot;&gt;OpenTelemetry Registry&lt;/a&gt;. Search for the name of the dependency you are using and check if the open-source community created the integration library!&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;OpenTelemetry&lt;/h2&gt;
&lt;h3&gt;What is the point of Open Telemetry?&lt;/h3&gt;
&lt;p&gt;It&apos;s better to start by understanding how code was instrumented before OTel, so we can understand why OTel exists.&lt;/p&gt;
&lt;p&gt;As commented before, you can only have observability if your code is instrumented. To do so your code must send data (logs, metrics, traces) to an observability backend. There are several observability backend tools (open sources like Jaeger and Zipkin or paid services like Datadog and New Relic) and for each tool, there will be specific libraries/agents for emitting the data. It means that if we instrumented our code using Jaeger but now we need to change to Zipkin we would have to re-instrument the code again using different libraries, different codes, different collectors, and different agent. That&apos;s where OpenTelemetry comes in.&lt;/p&gt;
&lt;p&gt;OpenTelemetry goal is to provide standard SDKs, APIs and tools for sending data to an observability backend. Otel is not an observability backend service, it supports exporting data to the observability backend tools (open source or not). In other words, OTel wants to standardize the way we instrument the application code and standardize the data format of generated telemetry data. Once the telemetry data is in a consistent format we can send it to any observability backend (such as Jaeger, Prometheus, Signoz...).&lt;/p&gt;
&lt;p&gt;So OTel does two important things: allows you to own the data you generate rather than be stuck with a proprietary data format or tool and allows you to learn a single set of APIs and conventions.&lt;/p&gt;
&lt;h3&gt;The OpenTelemetry Architecture&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/d302d2ae9e3f5cc1d051d6c759b3c275/3f8cf/otel-architecture.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 106.32911392405065%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAVCAYAAABG1c6oAAAACXBIWXMAAAsTAAALEwEAmpwYAAAC9UlEQVR42pVU2U7bQBTNd7ePfWnLS/fHVkKF8gRSJdQNCUFVVCiqgDQ7DZjEjslix54Z21lP75mSABWVYKTjO8v1mbscO2dMgiQxiKIQWkXQOrZWxX/n2WCA4XCIgdgwUuiFEYLYXECjF2koY6xPkiTIaa2gvFUM/EXUD1+iuv8UTv4VtLuIzF+GUT443MBgpTjA24MIT9YP8fxzGS82qlg8SvChEovHFGmaIqcYTfMN3PwDHG3fE9xH3uIeksYzpNq1hGddjdeHKRY+lvF4vYhHgofvj7Dw6TfWClcIE3kMswCJagiacJ08VOjAxE0h8zGZjC3haDRCkIxQcVoonXg4jzJ4gUIr0AhVeknIB8d0OrW23enal2djts+RSq2NlGgyHuOmcY2QN4xGQ7jNJozRczIiDEP4vo80y+Yv93oBPK+FdruDOFbodrvWL2ekQyQl4jhGtVq1h+xaJgSdTgf1et1artlJdvz09BTHx8c4OzsT0rad00e6rK1DlqU2VW7ypTRNxPF8Tk4f7v89S+0e/XnJbG5lww3rnGYYSW3CsG+dekFgMb6hXrO6RlFkS+F53mUNyVqrO/j24wBbO3v4urOPz5tfsfVtD3sHBXj+uSVgafr9vq0TA+Cac17earWuEhqUanVsf/+Jja0dbArRh41tfJH5zv4hWn577swaMyqmyDUJiWsRsnbsntLGpk1d0ppECJS6JqGr8nAcB41GA7VazTaNa2ZwRTa3G/RnRJPJxKZOyzqzLPMa2i7/i+wSPKczO+667lw+pVIJ+XzeSqhYLNooc6yLktT+B54n0gASVSoVdERzbAjPaCm7meXerVIeSEqpNOI25bhOeKGvgdykJQUtKelyGXGhACPRqV+/oOXLuDNhJkX3V1dRW15GdWkJv9+9g7e2BmdlBfHuLqZ3JUxFa7FEGElkfbFELNEa4k4R3nJQItQnBU5QNgSbY38Os49/Bvt9y0VEdmEtRCpDOWfnKZOy1JfSocC5Pjk5wR9yyDw5V72EiAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;A diagram presenting the three layers of the OpenTelemetry Architecture.&quot;
        title=&quot;&quot;
        src=&quot;/static/d302d2ae9e3f5cc1d051d6c759b3c275/f058b/otel-architecture.png&quot;
        srcset=&quot;/static/d302d2ae9e3f5cc1d051d6c759b3c275/c26ae/otel-architecture.png 158w,
/static/d302d2ae9e3f5cc1d051d6c759b3c275/6bdcf/otel-architecture.png 315w,
/static/d302d2ae9e3f5cc1d051d6c759b3c275/f058b/otel-architecture.png 630w,
/static/d302d2ae9e3f5cc1d051d6c759b3c275/40601/otel-architecture.png 945w,
/static/d302d2ae9e3f5cc1d051d6c759b3c275/78612/otel-architecture.png 1260w,
/static/d302d2ae9e3f5cc1d051d6c759b3c275/3f8cf/otel-architecture.png 1293w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;The diagram above uses generic elements to represent each category and this is the important thing to understand. You can easily modify the blocks to real tools, e.g.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/52422c505dbef37a95c215f085ad4726/0e149/otel-architecture-real.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 28.48101265822785%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAGCAYAAADDl76dAAAACXBIWXMAAAsTAAALEwEAmpwYAAABBElEQVR42m1RW07EMAzs/S/DKYALUFYI8cGylD7yapNsHk0yOGWFVIqlyIo9Gc84zZpxjFJ21woJfEBsHwFvEa2Gc+4GLTt889TeIcWEGONP809YYzBMDJaNKJ9vxJ6QY4CaZ4QQDvjGiXtoE7AsM5ynrASC6bEm0JAVSikYreFThF09cs5IKcN7j2FkcDND7k/b20iDmsqaUoK1FsusMPYduq8L3ocIrT04Z+CMoaN8lhzOXkmAhpQSExNYpjMRPoBxScMlmtPL89ashJUYhRSQ83yzb8iyEKSa1CIoavhNjeB8W9PBsrHmn/8ou0KmvS3miv6jxdS/Qsh5q/1+CuFrrucb5jXSRadDz6EAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;A diagram presenting the three layers of the OpenTelemetry Architecture with real components.&quot;
        title=&quot;&quot;
        src=&quot;/static/52422c505dbef37a95c215f085ad4726/f058b/otel-architecture-real.png&quot;
        srcset=&quot;/static/52422c505dbef37a95c215f085ad4726/c26ae/otel-architecture-real.png 158w,
/static/52422c505dbef37a95c215f085ad4726/6bdcf/otel-architecture-real.png 315w,
/static/52422c505dbef37a95c215f085ad4726/f058b/otel-architecture-real.png 630w,
/static/52422c505dbef37a95c215f085ad4726/40601/otel-architecture-real.png 945w,
/static/52422c505dbef37a95c215f085ad4726/78612/otel-architecture-real.png 1260w,
/static/52422c505dbef37a95c215f085ad4726/0e149/otel-architecture-real.png 2466w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3&gt;OTel Components&lt;/h3&gt;
&lt;p&gt;The main components of OTel are:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Specification&lt;/strong&gt;: Defines cross-language requirements for all implementations, defining API, SDK and OTLP (OpenTelemetry Protocol).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Collector&lt;/strong&gt;: Proxy that receives, processes and exports telemetry data to a backend. It supports data in multiple formats (OTLP, Jaeger, Prometheus...).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Language SDKs&lt;/strong&gt;: It lets you use OTel API to generate telemetry data for your favorite language and export to your favorite backend.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Instrumentation Libraries&lt;/strong&gt;: Supports popular libraries and frameworks for generating telemetry.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Automatic Instrumentation&lt;/strong&gt;: Provides a way to instrument the code without changing a single line of code. It will add a minimum telemetry for you.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;K8S Operator&lt;/strong&gt;: It&apos;s a Kubernetes Operator that manages the OTel Collector and auto-instrumentation.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;Collector&lt;/h3&gt;
&lt;p&gt;The OpenTelemetry Collector is a vendor-agnostic proxy that can receive, process, and export telemetry data. It supports receiving telemetry data in multiple formats (for example, OTLP, Jaeger, Prometheus, as well as many commercial/proprietary tools) and sending data to one or more backends. It also supports processing and filtering telemetry data before it gets exported.&lt;/p&gt;
&lt;p&gt;The OTel Collector is NOT necessary. You can configure your application to emit signals and export to the backend tools directly or you can configure it to send to the collector and then the collector forwards to the backend tools.&lt;/p&gt;
&lt;p&gt;Well, if it&apos;s optional, why would you want to use the collector then? Using a collector is more scalable!&lt;/p&gt;
&lt;h4&gt;Configuration&lt;/h4&gt;
&lt;p&gt;The configuration of the collector is done through a YAML file. In the collector, we can configure a pipeline with the following elements:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Receivers&lt;/strong&gt;: Define the sources of the telemetry&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Processors&lt;/strong&gt;: Applies transformations to the collected data. This includes actions such as filtering, renaming, dropping, recalculating...&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Exporters&lt;/strong&gt;: Define where the telemetry (after the processor is applied) will be sent&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Connectors&lt;/strong&gt;: Acts as an exporter and receiver because it will connect two pipelines (consuming data as an exporter and emitting data as a receiver)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All these components must be enabled in the &lt;code class=&quot;language-text&quot;&gt;service&lt;/code&gt; session of the YAML manifest. An example of a valid configuration is the following:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;receivers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;otlp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;protocols&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;grpc&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;endpoint&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0.0.0.0&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;4317&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;http&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;endpoint&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0.0.0.0&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;4318&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;processors&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;batch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;

&lt;span class=&quot;token key atrule&quot;&gt;exporters&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;otlp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;endpoint&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; otelcol&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;4317&lt;/span&gt;

&lt;span class=&quot;token key atrule&quot;&gt;extensions&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;health_check&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;pprof&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;zpages&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;

&lt;span class=&quot;token key atrule&quot;&gt;service&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;extensions&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;health_check&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; pprof&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; zpages&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;pipelines&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;traces&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;receivers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;otlp&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;processors&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;batch&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;exporters&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;otlp&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;metrics&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;receivers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;otlp&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;processors&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;batch&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;exporters&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;otlp&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;logs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;receivers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;otlp&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;processors&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;batch&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;exporters&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;otlp&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;OTLP&lt;/h3&gt;
&lt;p&gt;The OpenTelemetry Protocol (OTLP) defines how the telemetry data will be transported between the source (application) and destination (observability backend). It will define the encoded, protocol (it accepts HTTP and gRPC) of the payloads.&lt;/p&gt;
&lt;p&gt;In simple terms, the idea is to define a specification (common language) that can be spoken by all parts: the application, the collector (that can be an OpenTelemetry Colletor or any other alternative, such as Grafana Agent) and the observability backend (Loki, Prometheus, Grafana Tempo...). If all parts follow the same specification (language) you can simply change the elements (e.g. switch from Jaeger to Tempo) without having to modify the application code (maybe you will need to modify environment variables but that&apos;s all).&lt;/p&gt;
&lt;p&gt;If you want to check in depth I recommend you to check the &lt;a href=&quot;https://opentelemetry.io/docs/specs/otlp/&quot;&gt;official documentation&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Cya!&lt;/h2&gt;
&lt;p&gt;Well, we covered a lot of what OTel is, the main components, concepts, and architecture... I highly recommend you to check some very useful links above:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://opentelemetry.io/docs/&quot;&gt;OpenTelemetry Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/magsther/awesome-opentelemetry&quot;&gt;Awesome Links for OpenTelemetry&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://opentelemetry.io/ecosystem/registry&quot;&gt;OpenTelemetry Registry&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Play around, get familiar with the documentation, take notes and start to use OpenTelemetry in your applications!&lt;/p&gt;
&lt;p&gt;I intend to make another blog post applying the concepts we covered here so stay tuned! Hope you like it and see you around! 👋&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Configuring a free email provider for your domain using AWS Route53 and Zoho]]></title><description><![CDATA[If you own a domain (in my case ) you can easily configure a free email provider and start using your custom email (e.g. ). This is so much…]]></description><link>https://felipetrindade.com/email-provider-for-domain/</link><guid isPermaLink="false">https://felipetrindade.com/email-provider-for-domain/</guid><pubDate>Thu, 08 Feb 2024 10:40:32 GMT</pubDate><content:encoded>&lt;p&gt;If you own a domain (in my case &lt;code class=&quot;language-text&quot;&gt;felipetrindade.com&lt;/code&gt;) you can easily configure a free email provider and start using your custom email (e.g. &lt;code class=&quot;language-text&quot;&gt;contact@felipetrindade.com&lt;/code&gt;). This is so much more professional!&lt;/p&gt;
&lt;h2&gt;Stack&lt;/h2&gt;
&lt;h4&gt;Infra as Code&lt;/h4&gt;
&lt;p&gt;As you probably know, DevOps Engineers hate to do manual things. That&apos;s why I&apos;m going to avoid as maximum as I can manual steps and I will configure our custom email provider using &lt;a href=&quot;https://www.pulumi.com/&quot;&gt;Pulumi&lt;/a&gt; with Typescript as Infrastructure as Code tool.&lt;/p&gt;
&lt;h4&gt;DNS&lt;/h4&gt;
&lt;p&gt;I will consider that your domain is already being managed by AWS Route53. This is not necessary, you can manage your domain in the registrar (the website you bought your domain, e.g. GoDaddy, Namecheap...), but since the code I wrote using Pulumi is specific this is a pre-requirement for following this post.&lt;/p&gt;
&lt;h4&gt;Email provider&lt;/h4&gt;
&lt;p&gt;There are myriad options of email providers out there, such as Google Workspace, Zoho, ProtonMail, Outlook, AOL, Yahoo... What we want is a free email provider (or one that has a free plan that is enough for our use case).&lt;/p&gt;
&lt;p&gt;I&apos;m very used to Gmail, but I&apos;m not willing to pay around &lt;a href=&quot;https://workspace.google.com/pricing&quot;&gt;$5&lt;/a&gt; per month to use barely use Gmail (Google Workspace). Oh, just for reference I paid $15 (second year) for a full year for my domain on NameCheap. And I pay around &lt;code class=&quot;language-text&quot;&gt;$0.5&lt;/code&gt; per month to maintain my &lt;a href=&quot;https://felipetrindade.com/static-website-s3-cloudfront/&quot;&gt;static blog/website&lt;/a&gt;. So domain + website cost me around &lt;code class=&quot;language-text&quot;&gt;$21&lt;/code&gt; per year... This is the same as 4 months of Google Workspace in the cheapest plan. So, I don&apos;t think it is worth it (for my use case).&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.zoho.com/mail/zohomail-pricing.html?zredirect=f&amp;#x26;zsrc=langdropdown&amp;#x26;lb=pt-br&quot;&gt;Zoho&lt;/a&gt; provided a good free plan for my use case, but another good alternative is &lt;a href=&quot;https://proton.me/pricing&quot;&gt;Proton&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Zoho configuration&lt;/h2&gt;
&lt;p&gt;Configuring Zoho is straightforward, you need to create the account, follow the steps and soon you will be presented with the following screen:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/3385e04a15e62001cd32eb336f395819/b03a8/zoho-01.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 44.93670886075949%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAJABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAAIF/8QAFQEBAQAAAAAAAAAAAAAAAAAAAQD/2gAMAwEAAhADEAAAAdlYqA//xAAXEAADAQAAAAAAAAAAAAAAAAAAAREg/9oACAEBAAEFAqyvP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABURAQEAAAAAAAAAAAAAAAAAABAR/9oACAECAQE/Aaf/xAAXEAADAQAAAAAAAAAAAAAAAAAAIDKh/9oACAEBAAY/ApJ1f//EABoQAAEFAQAAAAAAAAAAAAAAAAEAESBBgfD/2gAIAQEAAT8hLttC7hH/2gAMAwEAAgADAAAAEMjv/8QAFREBAQAAAAAAAAAAAAAAAAAAEBH/2gAIAQMBAT8Qh//EABYRAQEBAAAAAAAAAAAAAAAAAAARcf/aAAgBAgEBPxDCv//EABoQAQADAAMAAAAAAAAAAAAAAAEAETEQIXH/2gAIAQEAAT8QAgIvp2heFKgeEdYacf/Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;A screenshot of the DNS configuration page of Zoho.&quot;
        title=&quot;&quot;
        src=&quot;/static/3385e04a15e62001cd32eb336f395819/828fb/zoho-01.jpg&quot;
        srcset=&quot;/static/3385e04a15e62001cd32eb336f395819/ff44c/zoho-01.jpg 158w,
/static/3385e04a15e62001cd32eb336f395819/a6688/zoho-01.jpg 315w,
/static/3385e04a15e62001cd32eb336f395819/828fb/zoho-01.jpg 630w,
/static/3385e04a15e62001cd32eb336f395819/0ede0/zoho-01.jpg 945w,
/static/3385e04a15e62001cd32eb336f395819/b03a8/zoho-01.jpg 963w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;After configuring this DNS record (we will get there!) the following screen will be displayed:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/31b9300de8af36e83b31d698322e5b8c/ec7ce/zoho-02.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 81.0126582278481%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAQABQDASIAAhEBAxEB/8QAGAAAAwEBAAAAAAAAAAAAAAAAAAECAwX/xAAVAQEBAAAAAAAAAAAAAAAAAAABAv/aAAwDAQACEAMQAAAB62ipGUS//8QAGhAAAwADAQAAAAAAAAAAAAAAAAECEBESIv/aAAgBAQABBQKXVHoWyFzn/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFREBAQAAAAAAAAAAAAAAAAAAARD/2gAIAQIBAT8BZ//EABwQAAEEAwEAAAAAAAAAAAAAAAABAhEhEDFBYf/aAAgBAQAGPwJ2qWDhcDvVnP8A/8QAGxAAAwACAwAAAAAAAAAAAAAAAAERIVFhkeH/2gAIAQEAAT8haxKaJv1Z4hy1QnXU8FP/2gAMAwEAAgADAAAAEIc//8QAFhEBAQEAAAAAAAAAAAAAAAAAARBB/9oACAEDAQE/EAdn/8QAFxEAAwEAAAAAAAAAAAAAAAAAAAERIf/aAAgBAgEBPxBlcKf/xAAcEAEAAgMAAwAAAAAAAAAAAAABABEhMUFhkfD/2gAIAQEAAT8QYCKLFWuwt+z3BRta+GIAu0GKHkzVU6ESFvE//9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;A second screenshot of the DNS configuration page of Zoho.&quot;
        title=&quot;&quot;
        src=&quot;/static/31b9300de8af36e83b31d698322e5b8c/828fb/zoho-02.jpg&quot;
        srcset=&quot;/static/31b9300de8af36e83b31d698322e5b8c/ff44c/zoho-02.jpg 158w,
/static/31b9300de8af36e83b31d698322e5b8c/a6688/zoho-02.jpg 315w,
/static/31b9300de8af36e83b31d698322e5b8c/828fb/zoho-02.jpg 630w,
/static/31b9300de8af36e83b31d698322e5b8c/0ede0/zoho-02.jpg 945w,
/static/31b9300de8af36e83b31d698322e5b8c/ec7ce/zoho-02.jpg 970w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2&gt;Pulumi code&lt;/h2&gt;
&lt;p&gt;To configure Zoho to be the email provider for your domain you need to create TXT and MX records in your DNS (AWS Route53). I created a generic class for this:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// email.ts&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; route53 &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;@pulumi/aws&quot;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; ICommonProps &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;./commons&quot;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ICommonProps&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  domainName&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;
  accountId&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;
  zoneId&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Record&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  type&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;MX&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;TXT&quot;&lt;/span&gt;
  subdomain&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;
  value&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;IEmail&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  record&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Record&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  commonProps&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ICommonProps
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Email&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  id&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;
  props&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; IEmail

  &lt;span class=&quot;token function&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;id&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; props&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; IEmail&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; id
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;props &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; props
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setRecords&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;setRecords&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; i&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;record&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; subdomain &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;record&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;subdomain &lt;span class=&quot;token operator&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;route53&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Record&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;i&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;-record&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        zoneId&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;commonProps&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;zoneId&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; subdomain&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        type&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;record&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;type&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        records&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;record&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        ttl&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;300&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We need to instantiate this class in two parts. The first TXT DNS record and then we apply the Pulumi configuration and after it&apos;s configured we apply the rest of the records.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// index.ts&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; Email &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;./email&quot;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; commonProps&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ICommonProps &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  domainName&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;felipetrindade.com&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  accountId&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;1111111111111&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  zoneId&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Z123456789ABCDEFGHI00&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; emailConfig&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; IEmail &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  record&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    type&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;TXT&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    value&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;zoho-verification=zb57142340.zmverify.zoho.com&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  commonProps&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; commonProps&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Email&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;email&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; emailConfig&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;After you apply this (&lt;code class=&quot;language-text&quot;&gt;pulumi up&lt;/code&gt;), the second DNS screen will be displayed and you can edit this file to be:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// index.ts&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; Email &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;./email&quot;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; commonProps&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ICommonProps &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  domainName&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;felipetrindade.com&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  accountId&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;1111111111111&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  zoneId&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Z123456789ABCDEFGHI00&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; emailConfig&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; IEmail &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  record&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    type&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;TXT&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    value&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
      &lt;span class=&quot;token string&quot;&gt;&quot;zoho-verification=zb57142340.zmverify.zoho.com&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token string&quot;&gt;&quot;v=spf1 include:zoho.com ~all&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    type&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;MX&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    value&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
      &lt;span class=&quot;token string&quot;&gt;&quot;10 mx.zoho.com&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token string&quot;&gt;&quot;20 mx2.zoho.com&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token string&quot;&gt;&quot;50 mx3.zoho.com&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    type&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;TXT&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    subdomain&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;zmail._domainkey&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    value&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCnFUY1z6/mZNsJXDBic4c+lxD0HHAy/iKQybHKLcvxAGPKjoTGW/wtlH+ncsKtSV2QDACinwXAVqP0j8eJtOKnABCwklr2y3IoC/MzrCzMk9ef3ni1Vg8ktB6Ig5KKZGcmhn8GWFj+xXa/ezVc/7KSgABCogwt0DDYZCE+QvK4wIDAQAB&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  commonProps&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; commonProps&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Email&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;email&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; emailConfig&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;Email Forwarder&lt;/h2&gt;
&lt;p&gt;In case you want to set up a &lt;code class=&quot;language-text&quot;&gt;Contact &lt;/code&gt;Me` page on your website you will need to configure an email forwarder to forward the message to your email provider (in our case, Zoho).&lt;/p&gt;
&lt;p&gt;I did that on my website, as you can check &lt;a href=&quot;https://felipetrindade.com/contact/&quot;&gt;here&lt;/a&gt;. I&apos;ve used &lt;a href=&quot;https://www.emailjs.com/&quot;&gt;EmailJS&lt;/a&gt; which gives me 200 emails per month in the free plan. This is more than enough for my website!&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 466px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/33d92f5739787dd638ad63d5d7f441bc/fc1a1/email-forward.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 25.949367088607595%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAFCAYAAABFA8wzAAAACXBIWXMAAAsTAAALEwEAmpwYAAABDUlEQVR42m2QzU7CQBRGeVjfgD1LtoY3cM+ClYlLtxpNiDHRVC2d0r/YvykMOG2nPU4BiSZ+q5ube8+dMyNswjAkCAL6vuffGA3t15/Wz2SqDGvZnvujKIqYzWZMJhOGekjTNGw3G9S+hvwVc3eJWV7RWkpda7TW7NQWudO8+y8I/5FY7o5AIQTT6ZTxeIzrunahpqoq8ixFKvsy94bu+gLie/Z1hyxLSilJopAwVyxFzrNYExQnYFEUzOdzFosF0g4edE7qxnQUZcXGucV3HXTd/HLuCeOEp1XKw1uKFyTWrGZU2otJkpBlGQN8SNd1J2CL4zh46xjPEyilzgeNMaxWLr7wrM2ntfs4fMU3e8l2aCzHzPcAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;A diagram explaining how the email forwarding happens.&quot;
        title=&quot;&quot;
        src=&quot;/static/33d92f5739787dd638ad63d5d7f441bc/fc1a1/email-forward.png&quot;
        srcset=&quot;/static/33d92f5739787dd638ad63d5d7f441bc/c26ae/email-forward.png 158w,
/static/33d92f5739787dd638ad63d5d7f441bc/6bdcf/email-forward.png 315w,
/static/33d92f5739787dd638ad63d5d7f441bc/fc1a1/email-forward.png 466w&quot;
        sizes=&quot;(max-width: 466px) 100vw, 466px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;PS: Recently Zoho announced that POP/IMAP will only be available on the paid plan ($1 month).&lt;/p&gt;
&lt;h2&gt;Cya!&lt;/h2&gt;
&lt;p&gt;Hope you liked this post! Stay tuned for the following posts!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Deploying a static website on AWS using S3 and CloudFront]]></title><description><![CDATA[In this post, I'll comment, discuss and deep-dive about how to deploy a static web application using S3 and CloudFront. Actually, this was…]]></description><link>https://felipetrindade.com/static-website-s3-cloudfront/</link><guid isPermaLink="false">https://felipetrindade.com/static-website-s3-cloudfront/</guid><pubDate>Thu, 01 Feb 2024 15:40:32 GMT</pubDate><content:encoded>&lt;p&gt;In this post, I&apos;ll comment, discuss and deep-dive about how to deploy a static web application using S3 and CloudFront. Actually, this was the way I deployed this website/blog!&lt;/p&gt;
&lt;h2&gt;Considerations regarding the tools used&lt;/h2&gt;
&lt;p&gt;I&apos;ve used some tools to help me deploy the static web application. To be honest, I think this is the less important part here, I could have used other tools to do the same job. The most important thing is to understand why the tool is being used, if you understand the reason you can replace it with another tool easily.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;CI/CD&lt;/strong&gt;: I&apos;ve used GitHub Actions to perform the CI/CI workflow of the project for the infrastructure as code (IaC) and for the website (static web application). You could have used any continuous integration tool here, such as &lt;a href=&quot;https://docs.gitlab.com/ee/ci/&quot;&gt;GitLab CI/CD&lt;/a&gt;, &lt;a href=&quot;https://circleci.com/&quot;&gt;CircleCI&lt;/a&gt;, &lt;a href=&quot;https://www.jenkins.io/&quot;&gt;Jenkins&lt;/a&gt;... I decided to use GitHub Actions to manage the CI/CD because it&apos;s already integrated with GitHub, the free tier is enough for my use case and it is a managed service (and I was not in the mood to self-manage and deploy an open source CICD tool). The workflow was performing the following tasks:
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;For the infrastructure&lt;/strong&gt;: Deploying the cloud resources used in the project, such as S3 bucket, CloudFront distribution, and Route53 records. Don&apos;t worry! I will cover all these services, just keep reading!&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;For the web app&lt;/strong&gt;: Building the source code, generating the static files, pushing the static files to S3 and invalidating the CloudFront distribution.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Infrastructure as Code&lt;/strong&gt;: All the cloud resources created could have been created manually in the console, but what is the point of it? This is not reproducible, difficult to document the changes, hard to keep track of the changes. I decided to use &lt;a href=&quot;https://www.pulumi.com/&quot;&gt;Pulumi&lt;/a&gt; to create the documentation. I chose Pulumi because I was curious to give it a try and be able to compare it with other IaC tools that I&apos;ve used in other projects, such as &lt;a href=&quot;https://www.terraform.io/&quot;&gt;Terraform&lt;/a&gt;/&lt;a href=&quot;https://opentofu.org/&quot;&gt;OpenTofu&lt;/a&gt;, &lt;a href=&quot;https://terragrunt.gruntwork.io/&quot;&gt;Terragrunt&lt;/a&gt;, &lt;a href=&quot;https://aws.amazon.com/pt/cdk/&quot;&gt;AWS CDK&lt;/a&gt; and &lt;a href=&quot;https://aws.amazon.com/serverless/sam/&quot;&gt;SAM&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Web app&lt;/strong&gt;: Here we have so many options to build a web application, so many different stacks/frameworks are possible to be used here, such as pure HTML/CSS project, React, NextJS... I decided to use &lt;a href=&quot;https://www.gatsbyjs.com/starters/gatsbyjs/gatsby-starter-blog&quot;&gt;Gatsby&lt;/a&gt;, which is a web framework that uses React behind the scenes.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Architecture&lt;/h2&gt;
&lt;p&gt;A visual approach to what we are going to deploy is available below.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/982c8ddf44c12eb1b9ebcc690108197a/339e3/Architecture.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 51.26582278481012%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAAAsTAAALEwEAmpwYAAACEUlEQVR42mWSTWsTURSG89f8I64UxI0LV10VBNFuuhFcuHRTKCitUNMoUk1TbGlpSoNOm+ZjkkwySTrpZL7una/M452ExIoH3nvvORyew3tmCqjIsowkSYiiaC4pBXXTY+pJROAzHI1IpM/GocPansHLLwYPP9zxZNfiVfGG53sjKrrMURTyYwGR+L6PpyR8l2OtT8u0CRXcmtwhVW3zyGG9NGDja4/HOxOefRqyWbphrTjmSBd/gUIIZrMZy8jf19pvbsfDPCNN03kty1I8LyAQctUXiJBUOVzGf8DcvjrwVU16Du7IRO908FxHOQkXvWrAohnlQLJKlFbAdNmkIt+nYQwUICFVg6SM5grDeK7lWwhVD0PuxxyY7+9fywntxgXOXQdr1JzfvtPDtbv0exoDQ8O2dLxpj8A11H77hMFA6XYBrFQqVKvVFVAKD3f8E2Gf0735TuheQlwnCzUm5jEDvUzk1VR+zUxqxH6NmaghnOsFsFQqUS6XV8AgcJTvLr1RmafvH1DvfEbaV8RJjfNim611C2twrkC/cCeXbL+YUC3qZGmDgmmaNJtNNE1Nn1j3gC063QNebz2iZXxDNk/Qt99x9uOMj29OmI6UIwUI7Et23p5SPThVu9IpxHE839/yp84jjtQXdq4IphqJ0yb06ty2T7g62KXXPaTR2MceX5D4dRyrRqtZYtivEPld/gCk3+hg8oJaTAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;A diagram that explains how the CI/CD, Git repository and AWS Services work together.&quot;
        title=&quot;&quot;
        src=&quot;/static/982c8ddf44c12eb1b9ebcc690108197a/f058b/Architecture.png&quot;
        srcset=&quot;/static/982c8ddf44c12eb1b9ebcc690108197a/c26ae/Architecture.png 158w,
/static/982c8ddf44c12eb1b9ebcc690108197a/6bdcf/Architecture.png 315w,
/static/982c8ddf44c12eb1b9ebcc690108197a/f058b/Architecture.png 630w,
/static/982c8ddf44c12eb1b9ebcc690108197a/40601/Architecture.png 945w,
/static/982c8ddf44c12eb1b9ebcc690108197a/339e3/Architecture.png 977w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;The following AWS Services are being used:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;S3&lt;/strong&gt;: Service that stores objects, in our case, it will serve as the storage of our static web app.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ACM&lt;/strong&gt;: It&apos;s a service that will provide the TLS certificate that will make our website secure (using HTTPS).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;CloudFront&lt;/strong&gt;: It is a CDN service, that will cache the static files of our website and deliver them to the clients (users).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Route53&lt;/strong&gt;: To create records that allow the users to access the website using our domain.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;S3&lt;/h3&gt;
&lt;p&gt;The Simple Store Service, widely known as S3, is one of the most famous/oldest AWS services. It is an object storage service that automatically manages scalability, data availability, high availability, data replication... A cool feature of S3 is that you can set an &lt;a href=&quot;https://docs.aws.amazon.com/AmazonS3/latest/userguide/WebsiteHosting.html&quot;&gt;S3 bucket to host a static website&lt;/a&gt;, so you can simply build the website code and throw it in the bucket, set the bucket to be public, add a bucket policy that allows anyone to get objects from the bucket and you have a website up and &quot;running&quot;!&lt;/p&gt;
&lt;p&gt;That&apos;s it. It&apos;s that straightforward. But there are some catches that it is important to mention and be aware of. S3 website endpoints do not support HTTPS, which is a big bad thing, even if it&apos;s just a static website, the users might feel that the website is not safe, secure, trustworthy and won&apos;t put much credibility into it. Also, your website will be now hosted in the AWS region where the S3 bucket was created. This means that if you, for example, created the bucket in &lt;code class=&quot;language-text&quot;&gt;us-east-1&lt;/code&gt; (N. California) and a user from China wants to access the website, all the requests would have to go to the N. California servers, so the latency can be higher than expected. If it&apos;s a simple static website this might not even be noticed by the user, but if it contains videos, this can be very noticeable. And no, creating several buckets across different regions is not a good idea to solve the problem.&lt;/p&gt;
&lt;h4&gt;Bucket Policy&lt;/h4&gt;
&lt;p&gt;Since there is no such thing as an IAM Role for CloudFront, the way we allow CloudFront to get the assets from the S3 Bucket is by setting a bucket policy that allows the CloudFront service (we can add, and we will, a condition to only allow a specific CloudFront distribution, instead of the entire service) to pull objects from the bucket.
By doing that we guarantee that users are not able to access the website through the S3 Website endpoint, only through the CloudFront distribution. A configuration regarding access should also be done from the CloudFormation side, we will cover that in a bit!&lt;/p&gt;
&lt;h3&gt;CloudFront&lt;/h3&gt;
&lt;p&gt;Wouldn&apos;t it be nice if we were able to cache our website very close to the users across the world and also allow HTTPS in our website? That&apos;s where CloudFront comes in!&lt;/p&gt;
&lt;p&gt;CloudFront is the CDN (Content Delivery Network) of AWS. If you used AWS at least once you probably know that resources are deployed in regions (such as &lt;code class=&quot;language-text&quot;&gt;us-east-1&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;ap-south-1&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;eu-west-2&lt;/code&gt;...), and this means that the server that hosts the service is running in that region. So if you work for a company based in India and most of your clients are also from India, it probably makes sense to host this in India too (e.g. &lt;code class=&quot;language-text&quot;&gt;ap-south-1&lt;/code&gt;), because the data center that contains the server that is running your application will be closer to the client, resulting in lower latency and better user experience in general. Of course, there are some points to consider, such as the cost of the Indian region being more expensive than the US region, but that&apos;s the general idea in terms of performance.&lt;/p&gt;
&lt;p&gt;But if you want to deploy static content (e.g. HTML, CSS, JS, Images, Videos...) you can deploy these assets in data centers that are closer to the clients, called edge locations - also called CloudFront POP (point of presence). These edge locations will cache your assets so the delivery to the client will be way faster! These edge locations exist all around the world so it means that even if you only host your application (e.g. the API that is used by your website) in one region, parts of the website (that are using the CloudFront to deliver static content) will be fast to the client.&lt;/p&gt;
&lt;h4&gt;Data center levels&lt;/h4&gt;
&lt;p&gt;There are three data center levels when we talk about CloudFront: edge locations (also known as POP), regional edge caches and the origin.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/c8ab14b97acbc81fc1eb594ae9152938/07a6a/data-center-structure-cloudfront.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 51.26582278481012%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAAAsTAAALEwEAmpwYAAABzklEQVR42n1SS3KbQBTUrXOKLHOAHCCLrLKxF6lKVRRLsoQs8RmBsAgMSAgQfwYMnScwC9lJqHq8YWbo6n7dE7x5urbr+0FN8flDAPY0hef8RJmoEBlDlWq01qgz+qZ1Zo9/9u/JO8BuOIj8C/ZzA7GrIXVNFJGMTjDgxaDSgWYH1CqKxLwFvAKMIH1/ZSiqC4psBlOegqsajtYCsf+INJQgEhlNqqOrGIr0eYDq/srwulmjJVDXOSDw5nDNBY7GFpG3QpMrCE9zKNId+OY3nN0KvrcZsQZAm3vwzwGalwqWHuLrxxzf72ZwHANF/ARuLOExhrOzRJ1tSe4ecSBBXd/DMX8gjQxEUYzDwQJ3j5g808L3zxB1iT3j+PJJx3ZtgHMToTfrGXrEMCSGdSZDpDJJl3AyGJKTjIDqCuhwDtvmwwxvTRF9DwOP5rXsGboaQ8BXyC9rHO0p1vNvsDYMXKe5hqy/37YthBDvTRljU+YXJMEKtv6A014myY+Iz0uKikKxkUnNPc7uL5SpNcyw+0dsRvurMkZbKXCJIVc0+PaC/NKoKDI1Raah+DSDy//N4XhQi4zY7MjVNWVSIrkKmsJAneu0P1SdM1Li3AD+AZNx9RlR8BgNAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;A diagram that explains how each data center layer connects&quot;
        title=&quot;&quot;
        src=&quot;/static/c8ab14b97acbc81fc1eb594ae9152938/f058b/data-center-structure-cloudfront.png&quot;
        srcset=&quot;/static/c8ab14b97acbc81fc1eb594ae9152938/c26ae/data-center-structure-cloudfront.png 158w,
/static/c8ab14b97acbc81fc1eb594ae9152938/6bdcf/data-center-structure-cloudfront.png 315w,
/static/c8ab14b97acbc81fc1eb594ae9152938/f058b/data-center-structure-cloudfront.png 630w,
/static/c8ab14b97acbc81fc1eb594ae9152938/40601/data-center-structure-cloudfront.png 945w,
/static/c8ab14b97acbc81fc1eb594ae9152938/07a6a/data-center-structure-cloudfront.png 1095w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Let&apos;s take a look at each type and how they related:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Edge Location (POP)&lt;/strong&gt;: This is the closest server to the client and can serve the content directly to the users.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Regional Edge Caches&lt;/strong&gt;: It has a larger cache when compared to the POP so the objects persist longer than the POP.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Origin&lt;/strong&gt;: The &quot;place&quot; that contains the original content (in our case, the static files of the website). A common origin (and this is the origin that we will use) is to host the content in an S3 bucket, but you can also use Elastic Load Balancer, an HTTP server in an EC2 instance, &lt;a href=&quot;https://docs.aws.amazon.com/cloudfront/latest/APIReference/API_Origin.html&quot;&gt;and many other options&lt;/a&gt; as a CloudFront origin.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Distribution Price Class&lt;/h4&gt;
&lt;p&gt;We commented that the edge locations exist all around the world, and that&apos;s a fact, but you don&apos;t need to use all the edge locations. You can select the &lt;a href=&quot;https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/PriceClass.html&quot;&gt;Price Class&lt;/a&gt; of your CloudFront distribution.&lt;/p&gt;
&lt;p&gt;Edge locations are grouped in regions (North America, South America, Europe...). Price Class is a group of regions that will be used in your distribution. There are three Price Classes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Price Class All&lt;/strong&gt;: Uses all POP in all regions.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Price Class 200&lt;/strong&gt;: Uses all POP in all regions, except South America, Australia and New Zealand regions.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Price Class 100&lt;/strong&gt;: Uses POP only in North America, Europe and Israel.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Based on that, you should ideally use a Price Class that covers most (or all) of your clients, reducing the latency they will see and optimizing the user experience. It is tempting to select &lt;code class=&quot;language-text&quot;&gt;Price Class All&lt;/code&gt;, so you will cover the entire world! But be aware that one of the highest costs in CloudFront is the data transfer going out of CloudFront to the users and this depends on the region. The cheapest regions (North America and Europe) cost &lt;code class=&quot;language-text&quot;&gt;$0.085&lt;/code&gt; per GB transferred and the most expensive region (Hong Kong, Indonesia...) costs &lt;code class=&quot;language-text&quot;&gt;$0.120&lt;/code&gt; per GB transferred. As we gonna comment in the pricing section, if your website has little access you won&apos;t even be charged because the free tier is very generous, so it&apos;s ok to use &lt;code class=&quot;language-text&quot;&gt;Price Class All&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If you select &lt;code class=&quot;language-text&quot;&gt;Price Class 100&lt;/code&gt;, it means that users from Japan (for example), won&apos;t get the content from an edge location. To be honest, there is a chance that they can be served by an edge location (that&apos;s how AWS works) but you will pay the least expensive region in your price class.&lt;/p&gt;
&lt;h4&gt;Caching Flow&lt;/h4&gt;
&lt;p&gt;We can sum up the caching flow using the following flow chart below:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/98e0d887732de5ee1138d5697d50e39f/c0ebd/flowchart-cloudfront-cache.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 55.06329113924051%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAAsTAAALEwEAmpwYAAAByklEQVR42pWSXW8SQRSG+dleemW8MjEx8arGK03RVC1tik1NuBBLLbXU0mLkoyxLYZeFXXZ2Yb/3cdgifpLqm0wmc86Zd54zM7n5dIpQFGxdJwpD2oMJp22NM2VMbzQlSVI80cVUC3iuhlzi+z5BEMhcImMu03Yb0engC0FuWK3S2Nykc3CAaYzIf1S4v9vgXuGC3c86jm2jd/J0T+4wUd/ieRGu6zCTRnPPQ5MwzZ0dGltbmN0uOaR8mVictpAbxEzcAEN4zIIoi5EGiNE5SRzyuzxJ685mWJaVrXOsUZrezO7cp/ipy8tyi1K9Txgnq5ooinBkmws5jvN3w3Q5kqWhKWkflXo82G/ypKzKA4IsvsjbtiAMb6jF0ngt4c+K5e7hUCOM4h90YYA/n63aWUv4Jy8Y/WOuzh5j6vWMqnplsn16TaVlSK/0/wgX9Wpzmy9Hd1Fb76Shw5vaQF6DwquaRhzHsv0Ua2pnX+yfWk7ThNGglW3+9abTVc2tLX9/ZSEf4XVN51mlz96FgZD/r3Q55PmhQvFcwxYO5daY/LHK+6/G7YbWLOBpZcjG4YAXtQmmZVM4UXi4V2fzqIcxnlCs62x8uGb/csw3cqtD55Ycw6IAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;A flowchart that explains the caching flow of CloudFront&quot;
        title=&quot;&quot;
        src=&quot;/static/98e0d887732de5ee1138d5697d50e39f/f058b/flowchart-cloudfront-cache.png&quot;
        srcset=&quot;/static/98e0d887732de5ee1138d5697d50e39f/c26ae/flowchart-cloudfront-cache.png 158w,
/static/98e0d887732de5ee1138d5697d50e39f/6bdcf/flowchart-cloudfront-cache.png 315w,
/static/98e0d887732de5ee1138d5697d50e39f/f058b/flowchart-cloudfront-cache.png 630w,
/static/98e0d887732de5ee1138d5697d50e39f/40601/flowchart-cloudfront-cache.png 945w,
/static/98e0d887732de5ee1138d5697d50e39f/c0ebd/flowchart-cloudfront-cache.png 1177w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;You might be wondering: what happens if I update the content? Does it mean that the clients will get the cached content until eventually the cache is not available and the content needs to be fetched from the origin? Yes, that&apos;s exactly what happens. If you want to update the content and force it to show to your users you can &lt;em&gt;invalidate the cache&lt;/em&gt;! This is something that we will do in our CI/CD: build the website, push the assets to the S3 bucket and invalidate the cache. But don&apos;t worry, we will still get there.&lt;/p&gt;
&lt;h4&gt;HTTPS&lt;/h4&gt;
&lt;p&gt;To distribute the content using HTTPS it is necessary to have a certificate. You can use the AWS Certificate Manager (ACM) service to create a certificate and &quot;attach&quot; to the CloudFront distribution. This way, your website will support HTTPS using TLS protocol.&lt;/p&gt;
&lt;h4&gt;Alias&lt;/h4&gt;
&lt;p&gt;When a CloudFront distribution is created, it automatically provides a domain name for you to access the distribution, something like &lt;code class=&quot;language-text&quot;&gt;d139253ab29.cloudfront.net&lt;/code&gt;. This domain name is really bad in terms of usability and you definitely shouldn&apos;t use it directly, especially in production.&lt;/p&gt;
&lt;p&gt;The idea is to create an A record in your Route53 that points to a more friendly name (in a domain that you own), such as &lt;code class=&quot;language-text&quot;&gt;felipetrindade.com&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;blog.felipetrindade.com&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;www.felipetrindade.com&lt;/code&gt;.&lt;/p&gt;
&lt;h4&gt;Access Control&lt;/h4&gt;
&lt;p&gt;To allow CloudFront to authenticate with S3 as origin two ways can be used: Origin Access Control (OAC) and Origin Access Identity (OAI). The latest is the legacy method, so we will give preference to the OAC method. The main difference between OAC and OAI is that OAC enhances security (using short-term credentials behind the scenes) and allows you to use KMS with CloudFront and S3 bucket. Using OAC, we will allow the CloudFront distribution to access the S3 using authenticated requests as we want.&lt;/p&gt;
&lt;h4&gt;Lambda@Edge&lt;/h4&gt;
&lt;p&gt;CloudFront allows us to specify the default root object of our website, usually the &lt;code class=&quot;language-text&quot;&gt;index.html&lt;/code&gt;, but this only works on the root of the website (e.g. &lt;code class=&quot;language-text&quot;&gt;https://www.felipetrindade.com&lt;/code&gt; -&gt; &lt;code class=&quot;language-text&quot;&gt;https://www.felipetrindade.com/index.html&lt;/code&gt;). &lt;a href=&quot;https://aws.amazon.com/blogs/compute/implementing-default-directory-indexes-in-amazon-s3-backed-amazon-cloudfront-origins-using-lambdaedge/&quot;&gt;It does not work on any subdirectory level&lt;/a&gt; (e.g. &lt;code class=&quot;language-text&quot;&gt;https://www.felipetrindade.com/hello-world&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;Let me be more specific here, if you access the root page of the website (e.g. &lt;code class=&quot;language-text&quot;&gt;https://www.felipetrindade.com&lt;/code&gt;) and click on any post it will work normally, let&apos;s say that you click on the &lt;code class=&quot;language-text&quot;&gt;Hello, World!&lt;/code&gt; post, you will see that the URL of the browser will be modified to &lt;code class=&quot;language-text&quot;&gt;https://felipetrindade.com/hello-world&lt;/code&gt;. But if you reload the page of the post or simply try to access it from a link (&lt;code class=&quot;language-text&quot;&gt;https://www.felipetrindade.com/hello-world&lt;/code&gt;) you will get a 403 Access Denied error. But if you add &lt;code class=&quot;language-text&quot;&gt;index.html&lt;/code&gt; at the end of the URL it is gonna work perfectly (&lt;code class=&quot;language-text&quot;&gt;https://felipetrindade.com/hello-world/index.html&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;The explanation for this problem is fairly simple. S3 authenticates with CloudFront using the OAC, sending requests to the S3 Rest API. The API does not allow redirection to the default index page. So when the user goes directly to a subpage (e.g. &lt;code class=&quot;language-text&quot;&gt;https://felipetrindade.com/hello-world&lt;/code&gt;) it will request to S3 an object with &lt;code class=&quot;language-text&quot;&gt;hello-world&lt;/code&gt; as the object key and this key does not exist, but the key &lt;code class=&quot;language-text&quot;&gt;hello-world/index.html&lt;/code&gt; exists! So that&apos;s why it is going to work if we add the &lt;code class=&quot;language-text&quot;&gt;index.html&lt;/code&gt; at the end of the URL.&lt;/p&gt;
&lt;p&gt;This behavior is such a TERRIBLE user experience and we don&apos;t want to deploy such a bad thing. Luckily CloudFront provides us something called &lt;a href=&quot;https://docs.aws.amazon.com/lambda/latest/dg/lambda-edge.html&quot;&gt;Lambda@Edge&lt;/a&gt;. Basically, we are going to deploy Lambda Functions at edge locations and these Lambda functions can alter the response or request between the user and edge location or between edge location and request. So it can be used to do A/B testing, add headers, dynamically route based on headers/cookies/query strings and so on. You can set the Lambda to run in four types of events:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Viewer request&lt;/strong&gt;: User request to edge location.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Viewer response&lt;/strong&gt;: Edge location request to the user.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Origin request&lt;/strong&gt;: Edge location request to origin.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Origin response&lt;/strong&gt;: Origin request to edge location.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/082b0721b6fd96f83b364dc2213cbbde/153c4/lambda-at-edge.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 18.354430379746837%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAECAYAAACOXx+WAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA5ElEQVR42i2Qi2qEMBRE/f9vKqWUQlkolELdVcG3sqgx0fX94jRmG7gkDOcOmbEqUTPNM+ep65ooisiyDN/36bqOaZwZG+glrE8MUQnDBEFAmqas64pUip/fG9br+ydhlNC2DVJKqqpCCGHMG62JUlEGUIUg76vWFI/2gdIGJ980DUPfk2Y5L28fWJevby222kCQJAmu6+I4DtfrjaIsULIl9xbi60jklRTFnSR+crZt69szSTpt6ui3lec5+76bKNu2me8vy2JmmiazlKa5riLREUOGYeA4DsPNuqr5v66Tj+OYP28zK8OwVlgEAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;A diagram that explains Lambda@Edge events&quot;
        title=&quot;&quot;
        src=&quot;/static/082b0721b6fd96f83b364dc2213cbbde/f058b/lambda-at-edge.png&quot;
        srcset=&quot;/static/082b0721b6fd96f83b364dc2213cbbde/c26ae/lambda-at-edge.png 158w,
/static/082b0721b6fd96f83b364dc2213cbbde/6bdcf/lambda-at-edge.png 315w,
/static/082b0721b6fd96f83b364dc2213cbbde/f058b/lambda-at-edge.png 630w,
/static/082b0721b6fd96f83b364dc2213cbbde/40601/lambda-at-edge.png 945w,
/static/082b0721b6fd96f83b364dc2213cbbde/78612/lambda-at-edge.png 1260w,
/static/082b0721b6fd96f83b364dc2213cbbde/153c4/lambda-at-edge.png 1361w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;In our use case, we want to modify the &lt;code class=&quot;language-text&quot;&gt;Viewer Request&lt;/code&gt;, since we want to modify the URI so the Edge Location can effectively retrieve the sub-page! We are going to implement that using NodeJS and it is as simple as 10 lines of code: get the URL from the events objects (JSON) and return the URL with &lt;code class=&quot;language-text&quot;&gt;index.html&lt;/code&gt; appended.&lt;/p&gt;
&lt;h3&gt;Certificate Manager&lt;/h3&gt;
&lt;p&gt;AWS Certificate Manager is the service responsible for provisioning the TLS certificates. So you don&apos;t need to go through the burden of purchasing/issuing the certificate, renewing and uploading it to the servers. It is a really handy service and the best part is that public certificates are free, so you don&apos;t pay extra for it.&lt;/p&gt;
&lt;p&gt;One tricky thing about the integration between ACM and CloudFront is that the certificate &lt;a href=&quot;https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/cnames-and-https-requirements.html&quot;&gt;MUST be issued in the N. Virginia region&lt;/a&gt; (&lt;code class=&quot;language-text&quot;&gt;us-east-1&lt;/code&gt;).&lt;/p&gt;
&lt;h3&gt;Route53&lt;/h3&gt;
&lt;p&gt;This is the DNS service managed by AWS, where you can buy a domain and manage the records for the domain, allowing name resolution for hostnames you own. One thing that is important to mention is that you DO NOT have to buy your domain from AWS to use it. If your domain was purchased from websites like GoDaddy, Namecheap, HostGator or any other registrar you can delegate your domain to be managed by Route53. This tutorial is out of the scope of this blog post but it is a straightforward process, it is easy to find tutorials or videos that go through it. The idea is that you will create a public or private (in our example the website is public, so I created a public one) zone in Route53.&lt;/p&gt;
&lt;h2&gt;Pricing&lt;/h2&gt;
&lt;p&gt;Not sure if you noticed but the infrastructure we built is 100% serverless: we only pay for what we use and the amount of traffic we receive, let&apos;s take a deeper look at the costs:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Route53&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;We will pay &lt;code class=&quot;language-text&quot;&gt;$0.50&lt;/code&gt; to manage a public hosted zone. This is one of the highest costs of the project. There are other costs involved, but they are so low (if your website has low traffic) that you can consider it to be zero, e.g. &lt;code class=&quot;language-text&quot;&gt;$0.40&lt;/code&gt; per 1 million requests to resolve the host of your website (in my case &lt;code class=&quot;language-text&quot;&gt;felipetrindade.com&lt;/code&gt;).&lt;/p&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;S3&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;We pay for the assets that we put in S3. The storage cost is barely zero, for reference, the total size of my website is under 10MB (you pay only &lt;code class=&quot;language-text&quot;&gt;$0.023&lt;/code&gt; per GB). It&apos;s true that we also pay for API operations in S3 but it is only &lt;code class=&quot;language-text&quot;&gt;$0.004&lt;/code&gt; per 10,000 GET (which is likely to be the most used operation in your website).&lt;/p&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;ACM&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;As we commented before, public certificates are free. 😄&lt;/p&gt;
&lt;ol start=&quot;4&quot;&gt;
&lt;li&gt;CloudFront&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The CloudFront always free tier is generous so if your website does not have much access you probably won&apos;t even pay a single penny on that. The always free tier offers you 1TB of data transfer out, 10 million HTTP/HTTPS requests each month and this is more than enough for a personal website with low access. But in case you are curious to know more about the &lt;a href=&quot;https://aws.amazon.com/cloudfront/pricing/&quot;&gt;CloudFront pricing&lt;/a&gt;, here we go:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;HTTP requests from users: from &lt;code class=&quot;language-text&quot;&gt;$0.0075&lt;/code&gt; to &lt;code class=&quot;language-text&quot;&gt;$0.016&lt;/code&gt; per 10,000 HTTP requests&lt;/li&gt;
&lt;li&gt;HTTPS requests from users: from&lt;code class=&quot;language-text&quot;&gt;$0.01&lt;/code&gt; to &lt;code class=&quot;language-text&quot;&gt;$0.022&lt;/code&gt; per 10,000 HTTPS requests&lt;/li&gt;
&lt;li&gt;Data Transfer from CloudFront to users: from &lt;code class=&quot;language-text&quot;&gt;$0.085&lt;/code&gt; to &lt;code class=&quot;language-text&quot;&gt;$0.12&lt;/code&gt; per GB transferred&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start=&quot;5&quot;&gt;
&lt;li&gt;Lambda@Edge&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;We are charged &lt;code class=&quot;language-text&quot;&gt;$0.60&lt;/code&gt; per 1 million requests and, since we are going to use a 128MB Lambda, &lt;code class=&quot;language-text&quot;&gt;$0.00000625125&lt;/code&gt; for every second used. So considering that the Lambda will run for 100ms per execution (usually it&apos;s way less), this means that 1 million executions of the Lambda will cost us &lt;code class=&quot;language-text&quot;&gt;$1.23&lt;/code&gt;. So cheap!&lt;/p&gt;
&lt;h4&gt;Total Cost&lt;/h4&gt;
&lt;p&gt;In the end, for a small project, you probably going to pay only for the Route53 public hosted zone. So the monthly cost for your project will be only &lt;code class=&quot;language-text&quot;&gt;$0.50&lt;/code&gt;!&lt;/p&gt;
&lt;h2&gt;Infrastructure code&lt;/h2&gt;
&lt;p&gt;Well, my entire website code is &lt;a href=&quot;https://github.com/felipelaptrin/felipetrindade.com&quot;&gt;publicly available on GitHub&lt;/a&gt;! You can take your time to check the code, what is being done and how things were created. You can also find valuable information in the README.md files of the &lt;code class=&quot;language-text&quot;&gt;frontend&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;infrastructure&lt;/code&gt; folders.&lt;/p&gt;
&lt;p&gt;I rather not talk about the website code, although you can check what I&apos;m doing by taking a look at the Github repository. I rather comment on the infrastructure code. It&apos;s going to be a code specific for Pulumi but it is easy to &quot;migrate&quot; the code from Pulumi to Terraform or any other IaC tool since the valid thing here is to understand the resources being used.&lt;/p&gt;
&lt;h3&gt;Pulumi&lt;/h3&gt;
&lt;p&gt;I divided the infrastructure into two parts:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Frontend&lt;/strong&gt;: The architecture diagram you saw earlier, is composed of ACM Certificate, CloudFront, S3 bucket, and Route53 records.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;GitHub Actions OIDC&lt;/strong&gt;: In the CI/CD we need to perform some actions (e.g. push assets to the S3 bucket, invalidate CloudFront distribution) in the AWS account. There are two common ways to deal with this in terms of authentication:
&lt;ul&gt;
&lt;li&gt;Create an IAM User for the GitHub Actions, issue an access key/secret, store it in the secrets of GitHub actions, and perform authentication using these key pairs. The major problem with this approach in my opinion is that you are using static credentials, you will need to care about rotating these, so that&apos;s why we are not going through this path.&lt;/li&gt;
&lt;li&gt;Create an Identity Provider in AWS that allows GitHub Actions to authenticate with AWS via OIDC and creates an IAM Role that GitHub Actions will be able to assume. The credentials are dynamic, so it&apos;s an improvement in terms of security! We are going to implement this!&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Frontend&lt;/h4&gt;
&lt;p&gt;Pulumi creates resources when you instantiate the classes that represent the resource. My idea was to put all the frontend components into a single class called &lt;code class=&quot;language-text&quot;&gt;Frontend&lt;/code&gt; and instantiate in the &lt;code class=&quot;language-text&quot;&gt;index.ts&lt;/code&gt; file. So I ended up with the following file:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// frontend.ts&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; archive &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;@pulumi/archive&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; s3&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; acm&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; cloudfront&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; route53&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; lambda&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; iam&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; cloudwatch&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Provider &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;@pulumi/aws&apos;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; interpolate&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; asset &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;@pulumi/pulumi&apos;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; accountId &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;./commons&quot;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;IFrontend&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  domainName&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;
  lambdaAtEdgeLogGroupRetention&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;


&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Frontend&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  id&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;
  props&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; IFrontend
  certificate&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; acm&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Certificate
  distribution&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; cloudfront&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Distribution
  bucket&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; s3&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Bucket
  zoneId&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;Promise&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
  lambda&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; lambda&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;Function&lt;/span&gt;
  provider&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Provider

  &lt;span class=&quot;token function&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;id&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; props&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; IFrontend&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; id
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;props &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; props
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;zoneId &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getZone&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;certificate &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getCertificate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;bucket &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getS3Bucket&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;lambda &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getLambdaAtEdge&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;distribution &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getDistribution&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;provider &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Provider&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;us-east-1&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; region&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;us-east-1&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setBucketPolicy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setRoute53&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;getCertificate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; acm&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Certificate &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; certificate &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;acm&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Certificate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;-certificate&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      domainName&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;domainName&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      subjectAlternativeNames&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;domainName&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;*.&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;domainName&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      validationMethod&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;DNS&quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; provider&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;provider &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; certificateValidation &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;route53&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Record&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;-certificate&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      zoneId&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;zoneId&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; certificate&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;domainValidationOptions&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;resourceRecordName&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      records&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;certificate&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;domainValidationOptions&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;resourceRecordValue&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      type&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; certificate&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;domainValidationOptions&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;resourceRecordType&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      ttl&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;60&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;acm&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;CertificateValidation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;-certificate-validation&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      certificateArn&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; certificate&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;arn&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      validationRecordFqdns&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;certificateValidation&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;fqdn&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; certificate
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;getS3Bucket&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; s3&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Bucket &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; bucket &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;s3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Bucket&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;-bucket&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      website&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        indexDocument&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;index.html&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        errorDocument&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;index.html&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; bucket
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;getDistribution&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; cloudfront&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Distribution &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; originAccessControl &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;cloudfront&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;OriginAccessControl&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;-origin-access-control&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      originAccessControlOriginType&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;s3&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      signingBehavior&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;always&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      signingProtocol&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;sigv4&quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; distribution &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;cloudfront&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Distribution&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;-cloudfront-distribution&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      enabled&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      comment&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Cloudfront distribution of my personal website and blog&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      origins&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        domainName&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;bucket&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;bucketRegionalDomainName&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        originAccessControlId&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; originAccessControl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        originId&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;s3OriginId&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      priceClass&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;PriceClass_All&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      defaultCacheBehavior&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        allowedMethods&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
          &lt;span class=&quot;token string&quot;&gt;&quot;DELETE&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;token string&quot;&gt;&quot;GET&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;token string&quot;&gt;&quot;HEAD&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;token string&quot;&gt;&quot;OPTIONS&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;token string&quot;&gt;&quot;PATCH&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;token string&quot;&gt;&quot;POST&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;token string&quot;&gt;&quot;PUT&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        cachedMethods&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
          &lt;span class=&quot;token string&quot;&gt;&quot;GET&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;token string&quot;&gt;&quot;HEAD&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        targetOriginId&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;s3OriginId&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        viewerProtocolPolicy&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;redirect-to-https&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        cachePolicyId&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;658327ea-f89d-4fab-a63d-7e88639e58f6&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Managed-CachingOptimized&lt;/span&gt;
        lambdaFunctionAssociations&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          eventType&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;viewer-request&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          lambdaArn&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;lambda&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;qualifiedArn
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      restrictions&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        geoRestriction&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          restrictionType&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;none&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          locations&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      viewerCertificate&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        cloudfrontDefaultCertificate&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        acmCertificateArn&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;certificate&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;arn&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        sslSupportMethod&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;sni-only&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        minimumProtocolVersion&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;TLSv1.2_2021&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      customErrorResponses&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        errorCode&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;404&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        responsePagePath&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/404.html&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        responseCode&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;404&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        errorCachingMinTtl&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3600&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        errorCode&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;403&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        responsePagePath&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/404.html&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        responseCode&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;404&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        errorCachingMinTtl&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3600&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      defaultRootObject&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;index.html&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      aliases&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;domainName&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;www.&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;domainName&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; distribution
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;setBucketPolicy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; bucketPolicyDocument &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; interpolate&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;{
      &quot;Version&quot;: &quot;2008-10-17&quot;,
      &quot;Id&quot;: &quot;PolicyForCloudFrontPrivateContent&quot;,
      &quot;Statement&quot;: [
        {
          &quot;Sid&quot;: &quot;AllowCloudFrontServicePrincipal&quot;,
          &quot;Effect&quot;: &quot;Allow&quot;,
          &quot;Principal&quot;: {
            &quot;Service&quot;: &quot;cloudfront.amazonaws.com&quot;
          },
          &quot;Action&quot;: &quot;s3:GetObject&quot;,
          &quot;Resource&quot;: &quot;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;bucket&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;arn&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;/*&quot;,
          &quot;Condition&quot;: {
            &quot;StringEquals&quot;: {
              &quot;AWS:SourceArn&quot;: &quot;arn:aws:cloudfront::&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;accountId&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;:distribution/&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;distribution&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;
            }
          }
        }
      ]}&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;s3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;BucketPolicy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;-bucket-policy&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      bucket&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;bucket&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      policy&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; bucketPolicyDocument&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;getZone&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;Promise&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; zone &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; route53&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getZone&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;domainName
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; zoneId &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; zone&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;zone &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; zone&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;zoneId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; zoneId
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;setRoute53&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;route53&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Record&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;-record&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      zoneId&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;zoneId&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;domainName&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      type&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;A&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      aliases&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;distribution&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;domainName&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        zoneId&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;distribution&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;hostedZoneId&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        evaluateTargetHealth&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;route53&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Record&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;-record-www&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      zoneId&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;zoneId&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;www.&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;domainName&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      type&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;A&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      aliases&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;distribution&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;domainName&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        zoneId&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;distribution&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;hostedZoneId&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        evaluateTargetHealth&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;archiveLambdaCode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; archiveFile &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;lambda/lambda_at_edge.zip&quot;&lt;/span&gt;
    archive&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getFile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      type&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;zip&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      sourceFile&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;lambda/index.js&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      outputPath&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; archiveFile
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; archiveFile
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;getLambdaAtEdge&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; lambda&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;Function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; archiveFile &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;archiveLambdaCode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; trustRelationship &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; interpolate&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;{
      &quot;Version&quot;: &quot;2012-10-17&quot;,
      &quot;Statement&quot;: [{
        &quot;Effect&quot;: &quot;Allow&quot;,
        &quot;Principal&quot;: {
          &quot;Service&quot;: [
            &quot;lambda.amazonaws.com&quot;,
            &quot;edgelambda.amazonaws.com&quot;
        ]},
        &quot;Action&quot;: &quot;sts:AssumeRole&quot;
      }]
    }&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; policy &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;iam&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Policy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;-lambda-policy&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      policy&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; interpolate&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;{
        &quot;Version&quot;: &quot;2012-10-17&quot;,
        &quot;Statement&quot;: [{
          &quot;Sid&quot;: &quot;VisualEditor0&quot;,
          &quot;Effect&quot;: &quot;Allow&quot;,
          &quot;Action&quot;: [&quot;s3:List&quot;, &quot;s3:Get*&quot;],
          &quot;Resource&quot;: [
            &quot;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;bucket&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;arn&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;,
            &quot;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;bucket&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;arn&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;/*&quot;
          ]
        }]
      }&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; role &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;iam&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Role&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;-lambda-at-edge&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      assumeRolePolicy&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; trustRelationship&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      managedPolicyArns&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; policy&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;arn&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; logGroup &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;cloudwatch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;LogGroup&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;-lambda-at-edge-log-group&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      retentionInDays&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;lambdaAtEdgeLogGroupRetention&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; lambda_function &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;lambda&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      description&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Runs at edge to add &apos;/index.html&apos; in every uri, allowing users to access paths&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      role&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; role&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;arn&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      code&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;asset&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;FileArchive&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;archiveFile&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      runtime&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;nodejs20.x&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      handler&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;index.handler&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      architectures&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;x86_64&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      publish&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      loggingConfig&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        logGroup&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; logGroup&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        logFormat&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Text&quot;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; provider&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;provider &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;lambda&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Permission&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;-lambda-permission&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      action&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;lambda:InvokeFunction&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      statementId&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;AllowExecutionFromCloudFront&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; lambda_function&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      principal&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;edgelambda.amazonaws.com&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; lambda_function
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The Lambda@Edge code is the following:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// lambda/index.html&lt;/span&gt;
&lt;span class=&quot;token string&quot;&gt;&apos;use strict&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
exports&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;handler&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;event&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; context&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; callback&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; request &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Records&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;cf&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; uri &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;uri&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;uri&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;endsWith&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;/&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;uri &lt;span class=&quot;token operator&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;index.html&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;uri&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;includes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;.&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;uri &lt;span class=&quot;token operator&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;/index.html&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;callback&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I used a &lt;code class=&quot;language-text&quot;&gt;commons.ts&lt;/code&gt; file to export helpful functions and values:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// commons.ts&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; getCallerIdentity &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;@pulumi/aws&apos;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; current &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getCallerIdentity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; accountId &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; current&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;current &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; current&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;accountId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And instantiated my &lt;code class=&quot;language-text&quot;&gt;Frontend&lt;/code&gt; class in the &lt;code class=&quot;language-text&quot;&gt;index.ts&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// index.ts&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; Frontend &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;./frontend&quot;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; frontendConfig&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; githubOidcConfig &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;./config&quot;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; GitHubOidc &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;./oidc&quot;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;GitHubOidc&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;oidc&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; githubOidcConfig&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; frontend &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Frontend&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;frontend&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; frontendConfig&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Export - Slack Outputs - Used during CI&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; cloudfrontDistribution &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; frontend&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;distribution&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id
&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; frontendS3Bucket &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; frontend&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;bucket&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;bucket&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And the configs (parameters of my stack), I created the &lt;code class=&quot;language-text&quot;&gt;config.ts&lt;/code&gt; file:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// config.ts&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; IFrontend &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;./frontend&apos;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; IGitHubOidc &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;./oidc&apos;&lt;/span&gt;


&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; frontendConfig&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; IFrontend &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  domainName&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;felipetrindade.com&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; githubOidcConfig&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; IGitHubOidc &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  roleName&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;GithubActionsOidcWebsite&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  organization&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;felipelaptrin&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  repository&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;felipetrindade.com&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4&gt;Github Actions OIDC&lt;/h4&gt;
&lt;p&gt;Now, let&apos;s take a look at the class that creates the GitHub Actions identity provider.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// oidc.ts&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; iam &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;@pulumi/aws&quot;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; accountId &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;./commons&quot;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; interpolate &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;@pulumi/pulumi&quot;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;IGitHubOidc&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  roleName&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;
  organization&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;
  repository&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;GitHubOidc&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  id&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;
  props&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; IGitHubOidc
  role&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; iam&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Role

  &lt;span class=&quot;token function&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;id&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; props&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; IGitHubOidc&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; id
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;props &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; props
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setIdentityProvider&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;role &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getOidcRole&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;setIdentityProvider&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;iam&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;OpenIdConnectProvider&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;-identity-provider&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      url&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https://token.actions.githubusercontent.com&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      clientIdLists&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;sts.amazonaws.com&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      thumbprintLists&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&quot;1b511abead59c6ce207077c0bf0e0043b1382612&quot;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;getOidcRole&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; iam&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Role &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; trustRelationship &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; interpolate&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;{
      &quot;Version&quot;: &quot;2012-10-17&quot;,
      &quot;Statement&quot;: [{
        &quot;Effect&quot;: &quot;Allow&quot;,
        &quot;Principal&quot;: {
          &quot;Federated&quot;: &quot;arn:aws:iam::&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;accountId&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;:oidc-provider/token.actions.githubusercontent.com&quot;
        },
        &quot;Action&quot;: &quot;sts:AssumeRoleWithWebIdentity&quot;,
        &quot;Condition&quot;: {
          &quot;StringEquals&quot;: {
            &quot;token.actions.githubusercontent.com:aud&quot;: &quot;sts.amazonaws.com&quot;
          },
          &quot;StringLike&quot;: {
            &quot;token.actions.githubusercontent.com:sub&quot;: &quot;repo:&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;organization&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;repository&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;:*&quot;
          }
        }
      }]
    }&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; role &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;iam&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Role&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;-role&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;roleName&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      description&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Role assumed by the GitHub Actions to deploy resources related to the website/blog&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      assumeRolePolicy&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; trustRelationship&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      managedPolicyArns&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;arn:aws:iam::aws:policy/AdministratorAccess&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; role
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;CI/CD&lt;/h2&gt;
&lt;p&gt;Finally! Let&apos;s check the CI/CD. We can divide the CI/CD pipeline into two jobs:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Infrastructure deployment&lt;/strong&gt;: Here, we need to install Pulumi, check if there are drifts, show the actions that it will perform and finally deploy the infrastructure (the latest step only when merged in the main branch).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Frontend deployment&lt;/strong&gt;: We need to build the assets, push them to the S3 bucket and invalidate the CloudFront cache.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There is a clear dependency in these jobs: the infrastructure needs to be deployed first, only after the infrastructure deployment we can effectively deploy the website assets to CloudFront.&lt;/p&gt;
&lt;h3&gt;GitHub Actions Workflow&lt;/h3&gt;
&lt;p&gt;The final CI/CD for this project is the following:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; CI/CD

&lt;span class=&quot;token key atrule&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;branches&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; main
  &lt;span class=&quot;token key atrule&quot;&gt;pull_request&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;

&lt;span class=&quot;token key atrule&quot;&gt;permissions&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;id-token&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; write &lt;span class=&quot;token comment&quot;&gt;# This is required for requesting the JWT&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;contents&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; read  &lt;span class=&quot;token comment&quot;&gt;# This is required for actions/checkout&lt;/span&gt;

&lt;span class=&quot;token key atrule&quot;&gt;jobs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;infra&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;runs-on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ubuntu&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;latest
    &lt;span class=&quot;token key atrule&quot;&gt;outputs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;frontend-bucket&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; steps.frontend&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;bucket.outputs.s3 &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;cloudfront-distribution&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; steps.cloudfront&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;distribution.outputs.cloudfront &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;AWS_REGION&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; vars.AWS_REGION &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;PULUMI_CONFIG_PASSPHRASE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; secrets.PULUMI_CONFIG_PASSPHRASE &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Checkout repository
        &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; actions/checkout@v4

      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Assume Role via OIDC
        &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; aws&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;actions/configure&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;aws&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;credentials@v3
        &lt;span class=&quot;token key atrule&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;role-to-assume&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; vars.IAM_ROLE_ARN_OIDC_GITHUB &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;role-session-name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Github&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;Actions
          &lt;span class=&quot;token key atrule&quot;&gt;aws-region&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; vars.AWS_REGION &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Setup Node
        &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; actions/setup&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;node@v4
        &lt;span class=&quot;token key atrule&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;node-version&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 21.6.1

      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Install node dependencies
        &lt;span class=&quot;token key atrule&quot;&gt;working-directory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; infrastructure
        &lt;span class=&quot;token key atrule&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; npm install

      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Pulumi installation
        &lt;span class=&quot;token key atrule&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token scalar string&quot;&gt;
          curl -fsSL https://get.pulumi.com | sh
          export PATH=$PATH:/root/.pulumi/bin&lt;/span&gt;

      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Pulumi Login using S3 as backend
        &lt;span class=&quot;token key atrule&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; pulumi login $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; vars.PULUMI_BACKEND_S3_BUCKET &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Pulumi Preview
        &lt;span class=&quot;token key atrule&quot;&gt;working-directory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; infrastructure
        &lt;span class=&quot;token key atrule&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; pulumi preview &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;diff &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;refresh &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;s stack

      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Get Frontend S3 Bucket
        &lt;span class=&quot;token key atrule&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; frontend&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;bucket
        &lt;span class=&quot;token key atrule&quot;&gt;working-directory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; infrastructure
        &lt;span class=&quot;token key atrule&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token scalar string&quot;&gt;
          OUTPUT=$(pulumi stack output frontendS3Bucket -s stack)
          echo &quot;s3=$OUTPUT&quot; &gt;&gt; &quot;$GITHUB_OUTPUT&quot;&lt;/span&gt;

      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Get CloudFront Distribution ID
        &lt;span class=&quot;token key atrule&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; cloudfront&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;distribution
        &lt;span class=&quot;token key atrule&quot;&gt;working-directory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; infrastructure
        &lt;span class=&quot;token key atrule&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token scalar string&quot;&gt;
          OUTPUT=$(pulumi stack output cloudfrontDistribution -s stack)
          echo &quot;cloudfront=$OUTPUT&quot; &gt;&gt; &quot;$GITHUB_OUTPUT&quot;&lt;/span&gt;

      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Pulumi Deploy
        &lt;span class=&quot;token key atrule&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; github.ref == &apos;refs/heads/main&apos;
        &lt;span class=&quot;token key atrule&quot;&gt;working-directory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; infrastructure
        &lt;span class=&quot;token key atrule&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; pulumi up &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;yes &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;refresh &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;s stack

  &lt;span class=&quot;token key atrule&quot;&gt;frontend&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;runs-on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ubuntu&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;latest
    &lt;span class=&quot;token key atrule&quot;&gt;needs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; infra
    &lt;span class=&quot;token key atrule&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Checkout repository
        &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; actions/checkout@v4

      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Assume Role via OIDC
        &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; aws&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;actions/configure&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;aws&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;credentials@v3
        &lt;span class=&quot;token key atrule&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;role-to-assume&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; vars.IAM_ROLE_ARN_OIDC_GITHUB &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;role-session-name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Github&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;Actions
          &lt;span class=&quot;token key atrule&quot;&gt;aws-region&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; vars.AWS_REGION &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Setup Node
        &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; actions/setup&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;node@v4
        &lt;span class=&quot;token key atrule&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;node-version&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 21.6.1

      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Install node dependencies
        &lt;span class=&quot;token key atrule&quot;&gt;working-directory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; frontend
        &lt;span class=&quot;token key atrule&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token scalar string&quot;&gt;
          npm install
          npm install -g gatsby-cli&lt;/span&gt;

      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Build website
        &lt;span class=&quot;token key atrule&quot;&gt;working-directory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; frontend
        &lt;span class=&quot;token key atrule&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; gatsby build

      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Push assets to S3
        &lt;span class=&quot;token key atrule&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; github.ref == &apos;refs/heads/main&apos;
        &lt;span class=&quot;token key atrule&quot;&gt;working-directory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; frontend/public
        &lt;span class=&quot;token key atrule&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token scalar string&quot;&gt;
          S3=s3://${{ needs.infra.outputs.frontend-bucket }}
          aws s3 sync . $S3&lt;/span&gt;

      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Invalidate CloudFront Cache
        &lt;span class=&quot;token key atrule&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; github.ref == &apos;refs/heads/main&apos;
        &lt;span class=&quot;token key atrule&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token scalar string&quot;&gt;
          aws cloudfront create-invalidation \
            --distribution-id ${{ needs.infra.outputs.cloudfront-distribution }} \
            --paths &quot;/*&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I don&apos;t have much to comment about the CI/CD since I&apos;ve already made my considerations and explained the steps needed so it should be straightforward to understand what is being done. But I&apos;d like to comment on the Pulumi outputs.&lt;/p&gt;
&lt;p&gt;In the &lt;code class=&quot;language-text&quot;&gt;infra&lt;/code&gt; job, we are creating all the cloud resources needed (actually this is not correct... there are some manual steps needed that I will comment on in the next section). There are two outputs that we need to get from these resources created: the bucket name and the CloudFront distribution ID.&lt;/p&gt;
&lt;p&gt;Instead of hardcoding these values or using any other manual approach to get these values directly from the Pulumi stack output IaC tools usually have an &lt;code class=&quot;language-text&quot;&gt;output&lt;/code&gt; section where you can get metadata about the resources created and if you are using a tool that does not support that you can (using IaC code) store the needed values in the &lt;a href=&quot;https://docs.aws.amazon.com/systems-manager/latest/userguide/systems-manager-parameter-store.html&quot;&gt;AWS Parameter Store&lt;/a&gt; and read the value from the CI.&lt;/p&gt;
&lt;h4&gt;Secrets and Variables&lt;/h4&gt;
&lt;p&gt;Instead of hardcoding some important values in the workflow YAML directly I decided to store these values in the &lt;a href=&quot;https://docs.github.com/en/actions/security-guides/using-secrets-in-github-actions#creating-secrets-for-a-repository&quot;&gt;Secrets&lt;/a&gt; and &lt;a href=&quot;https://docs.github.com/en/actions/learn-github-actions/variables#creating-configuration-variables-for-a-repository&quot;&gt;Variables&lt;/a&gt;. This gives me more flexibility (e.g. if something changes I do not need to open a PR to change the value) and security.&lt;/p&gt;
&lt;p&gt;The following variables were set in the repository:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;AWS_REGION&lt;/code&gt;: The AWS region where all the resources will be deployed.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;PULUMI_BACKEND_S3_BUCKET&lt;/code&gt;: This is the S3 bucket that will store the Pulumi stack information and metadata. I will comment on this in the following sections.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;IAM_ROLE_ARN_OIDC_GITHUB&lt;/code&gt;: The IAM Role ARN of the role that Github will use to authenticate with AWS and perform the deployments in the account. Please notice that this can&apos;t be fetched from Pulumi outputs, since we need to have access to the Pulumi backend (in our case S3 bucket) to retrieve this information!&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And the following secret was set:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;PULUMI_CONFIG_PASSPHRASE&lt;/code&gt;: Pulumi uses this password to protect and lock your configuration values and secrets. This will be commented on soon.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;The chicken and egg problem&lt;/h2&gt;
&lt;p&gt;I&apos;m not sure if you noticed, but in this project, we faced the chicken and egg problem (actually two times!). If you didn&apos;t notice, let me explain these problems.&lt;/p&gt;
&lt;h4&gt;CI/CD Pipelines&lt;/h4&gt;
&lt;p&gt;We want to deploy our infrastructure using CI/CD pipelines but the CI/CD needs to assume a role but the role will be created by the CI/CD... Houston, we have a problem.&lt;/p&gt;
&lt;p&gt;There are some solutions to tackle this problem, let me point two:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Create resources related to OIDC manually&lt;/strong&gt;: After manually creating the resources that allow GitHub Actions to authenticate in AWS assuming the IAM Role you can import the manually created resources to your IaC code.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Deploy the stack manually a single time&lt;/strong&gt;: This involves no manual steps to be done, which is nice but someone (usually a DevOps from the infrastructure team) needs to apply the Pulumi stack manually once. After that, there is no need to apply things manually since the CI/CD pipeline will manage it.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Both options have pros and cons, I chose the second option. So I ran locally:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;pulumi up&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4&gt;IaC backend&lt;/h4&gt;
&lt;p&gt;Once again, we want to deploy our infrastructure using IaC but the IaC requires you to set up a backend to store the metadata about the stack you will deploy. You can&apos;t create the IaC backend using IaC because you need it in place before start to use it.&lt;/p&gt;
&lt;p&gt;Something important to notice is that this is a common thing for basically all IaC tools out there, if you use Terraform/OpenTofu you will need to set the backend provider (in AWS you probably will use DynamoDB table to perform state lock and S3 bucket to store the remote state), if you use AWS CDK you will need to perform the CDK Bootstrap command...&lt;/p&gt;
&lt;p&gt;In this project, using Pulumi, it was necessary to create the S3 bucket to store the Pulumi stack metadata. I decided to create this manually and after the creation, I added the S3 URI in the GitHub Actions Variables (&lt;code class=&quot;language-text&quot;&gt;PULUMI_BACKEND_S3_BUCKET&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;Also, the initial setup of the stack is to define the name of the stack and the password for the configuration (used in GitHub Actions Secret &lt;code class=&quot;language-text&quot;&gt;PULUMI_CONFIG_PASSPHRASE&lt;/code&gt;). To do that the following Pulumi command was used:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;pulumi stack init --secrets-provider&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;passphrase &lt;span class=&quot;token parameter variable&quot;&gt;-s&lt;/span&gt; stack&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;Back in the old days&lt;/h2&gt;
&lt;p&gt;Cloud and managed services are great! It makes our lives easier and simple but at the same time, it creates a layer of abstraction that makes it difficult to understand exactly what is happening behind the scenes. So I&apos;d like to comment on how people used to deploy static websites back then. I don&apos;t plan to deep dive here because this is not the goal of this blog post, but I think it&apos;s worth it to mention some steps needed:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Buy/rent a server/virtual machine&lt;/li&gt;
&lt;li&gt;Configure the firewall of the machine to allow traffic on port 80/443&lt;/li&gt;
&lt;li&gt;Setup a web server using tools such as &lt;a href=&quot;https://docs.nginx.com/nginx/admin-guide/web-server/&quot;&gt;NGINX&lt;/a&gt;, &lt;a href=&quot;https://httpd.apache.org/&quot;&gt;Apache&lt;/a&gt;, &lt;a href=&quot;https://tomcat.apache.org/&quot;&gt;Tomcat&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Configure the SSL/TLS certificate to allow a secure connection using HTTPS. You could buy the certificate from a Certificate Authority (CA) or use a nonprofit CA (e.g. &lt;a href=&quot;https://letsencrypt.org/&quot;&gt;Let&apos;s Encrypt&lt;/a&gt;) and install the certificate on your web server. Oh, and remember that SSL/TLS certificates expire so you would need to manage this manually or install a more automatic way to renew it (e.g. &lt;a href=&quot;https://certbot.eff.org/&quot;&gt;Certbot&lt;/a&gt;, used to auto-renew certificates issued by Let&apos;s Encrypt).&lt;/li&gt;
&lt;li&gt;Setup a CI/CD pipeline probably using a bash script that connects to the server using SSH&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Well, this is just one example of how this used to be done. Of course, other tools and other steps can be introduced here but as a general view, it&apos;s fine.&lt;/p&gt;
&lt;h2&gt;Cya!&lt;/h2&gt;
&lt;p&gt;Thanks for taking the time to read this blog post. Hope you liked it and feel free to message me if you want to chat about it. Hope to see you in the next blog post!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Hello, World!]]></title><description><![CDATA[It's been a while since I started to think about writing a blog. And well... the time has finally come! Motivation One thing that I love to…]]></description><link>https://felipetrindade.com/hello-world/</link><guid isPermaLink="false">https://felipetrindade.com/hello-world/</guid><pubDate>Fri, 26 Jan 2024 10:40:32 GMT</pubDate><content:encoded>&lt;p&gt;It&apos;s been a while since I started to think about writing a blog. And well... the time has finally come!&lt;/p&gt;
&lt;h2&gt;Motivation&lt;/h2&gt;
&lt;p&gt;One thing that I love to do, and I know this is not a common thing that people in the software engineering field like to do, is document things. Why? Well, I hate (although it&apos;s quite common in our area...) to get frustrated trying to understand how things come together, how they relate, how things work behind the scenes and having a hard time trying to understand what is the real motivation behind things being used.&lt;/p&gt;
&lt;p&gt;And after hours of googling, asking friends and watching videos you finally understood it all. And it was probably something that you said:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Oh, so it was just that? Why this was not said in the videos/documentation/explanations online?
It was so simple but people was not explaining that!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The &lt;em&gt;click&lt;/em&gt; in your mind happened that you finally said: &lt;strong&gt;I finally got it!&lt;/strong&gt;. It&apos;s a mindblowing experience that  I bet every software developer has been through, and it&apos;s simply magical.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 256px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/7c60f1e91ebf86afc264a3ffad3ae810/6f3f2/mind-blowing.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 150%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAeCAYAAAAsEj5rAAAACXBIWXMAAAsSAAALEgHS3X78AAAHf0lEQVR42lWWaVDU9xnHV44FdmF3YZdzWRZY7gWRQ5FL5FTAA7lFRMULtaBEBKoigqgIHkgBFUXxVjQK0bQZo4kmjW0y2ukRndZkOm2mM07STJsekzd98emzi9XmxW92Z/+7n+f7/T7P//mvwtNTjznQRGRIKMmxccyOiyMxJsb+WlFYwMyoaObEx9OwopJEq5VAYyAqtQ6Vuw6tTo9a3js5q3BWqu1HofM0EGUJIz0xgdzUuRSkp5KflkpBWhqrShaRkZREcXYWzevqSBJguBS2gYz+RswmM86uHihd3HEWqA2sMAcGERcZSdmCXDbXVNC8usZ+dqxbyfHOHdQsWsiG5RUc2dNKQWoa0ZZwgVpIEdXGACNuKh0ubto3CiNCLaQnJFC3pIiju7dzZeggN0cPc224l0eT5zl3tIuLgwe5NzHG8b3tFM+fT0ZiEnPiZhJmDkGj1eMmtl1VWpSuGhRhwaEUZ2XS/+Nmxo91c23kIO9dGeH+xCj3b5zmZ5eGmTo7wEdTF/jNoykmx44xeriLzSuqmTMzHr3e1360Om+xr0WRJuraN63lvaunGO3dzfE92xnr62Dq3AA3Th7iwkC3nH28P3GaD2+e5f3ro/zx1x9w+8JJ4sIi8Jcsg4OCp4FuOhQtDfXcuy5KrpxgqLuVw7u20d/exJGdWxnq2sHerRvo37mNd87/hLHDe3l4e5zff3qP55/d50B7M5lJyfj7GfHQGMS6J4p9rU08kMoD3e00rqyiYXkZB1q2sP+tzTRUlVKan8O2+hW8e+UU1070SySHuDU2wJ+fPea7v3zOxpU1BPj4o/PyQSt5Ko737GLy/BC9O5spycumfGEezWtqOCQqVy4pZl5Soh06fGA3v3o4KU5G6Wtr4uO7l/n+2y+5OzFOnoxYfHgYYYEBKA7taaOvo4Ujna3UlS6mcVU1B1sb6WxcT2NdNTEyo2mzEqgoyOX6mWOcPbqPC9K8Jw9u8fLLJ/znn18xuG8XKeEhotQXxYOpS4z0dkge29j9o/VslRnsErvrq5axa/NakmKspEg382Xo60qXcP/WODfF8hnJ87c//yn/+voFI11tzDKbsJpE4eN7b3Nu8ACDYv1k7x4Z7nLaN9azqaaK+tISqosKKcqYx9qyZSzLmbY+1LPTnvkfnn7A99+84IsHlzmxaRG1GVYUU5dO8fDOZcaO9dDZtJFjHa28tbrOftYsFWBhEY21NayvqKQwM5flRUuoKl5IRXEh14f6+Pr5J/z72Tvc6VxObrQZxd1rZxiTXMaO9NC9rYkTPXs50LyV1UuWUpKTR1VBEYuz8gRWQEpCGpuqKzjd38XqinKu9Pfwtxe/4NtPznC1rYzsmGAUnc1bZL72cf7wfsZ7u2lauYrKxRWyEIqYHZ9KwswUIsLjCZcTZImmtjifl88/5e9/esp3v7vDN5/d4mJHAyUpsQT6SlPy0jJoqavhzvEO/vH0BtvryrGGxmANm4l/cCxKnT+uOj9U3kb0Jot0O4cn717nr1/8kmf3hrk/2EZLSR5Z8dFoNV4oavOyGNlSzcvJ/TwcamGOzN386Dg0bp74mELRBwSjNgQQGmklKspKmTRpnQxz387tLJybTGHaHLKT4+071Vm2jqJyfhr1C7J4fHIrX93uIT05SdZZDFGhkbjr/QmLjsVVgLHRVh6+fZFHd2+Sm5JOTlY2wZYIKWjG3csPtdx6LrLKFMEhYURHRpGdkkSHKA0PD6dOZtB2jyfOSkTjF4SbgL28fJkc7pc75RxWSwxVJaUYxIGnXFd7+qJ016O0LQeD0YxZoMGWKHwCLeRnz+Pj22eoXlpM7bJFaCQ7pVQPCbbQ3VBHm8yo0RRGRmo6hiALHnJd9QroopLlYKvgHyRAsRgaYeXSQCcfjveRIM8Xk8lmx1d+4IOH3o/F0pBdTRvIy83DVR+AVn7rIXG46XxQerwC6vxM+AlQ621i2eJCBts3sCgzGZPRxAxnNSqdwa7QW7JakDmPecmpdiee/sFofALt+blqvUWh1zRQK0CdbxAx1jha15RjMctTzUMn68hb9lyAfb0rlB5ESs71laXMmpVsz1Xja8Ld4G+36yIFlbIL7Rl6yIdetuwK8mURRBIVLkHLo1WhcKIoK1VW2AISZTn4mUIoKV4gsxmOh49JsgtE/VrdtF2lbWx0UsmYNA83/zAUTu5kzp3NqtJCPLRepCXF8/mDCdbWLsddIvEQi7bcNAK0jZQ9O43ebtemzq7QOzoRtTkaZ40Prl5GnDyNRMrsVRbn0bJhBVeHDzI7JQ21NMEQGGKH2Rrx2uqr7KaBolAVYMHZ3YCji0Y65YPKNxQnvXTXEEhd+VI+mjjBnuaNUswfre807LVVUeei9nytzq7QDnNyxcHBAUcHJ5wErPINQRUQgYMmgLX1awQ6QmlRPk7y3f/BXGxjohZ1/wdTuslz2VHpxowZM14fR0clzmo97mYramM4Cnc/EjNzKKssw9UnyA6y52aDqXQ/UGcHOjg6/wDoMMMBJ/lLYdsybj5mVAYTjoZglOZYKSJZ2xogNt/k9gZmA/4X4fpJ/o7RZh4AAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;An image of a man with his mind exploding, which represents a mindblowing experience.&quot;
        title=&quot;&quot;
        src=&quot;/static/7c60f1e91ebf86afc264a3ffad3ae810/6f3f2/mind-blowing.png&quot;
        srcset=&quot;/static/7c60f1e91ebf86afc264a3ffad3ae810/c26ae/mind-blowing.png 158w,
/static/7c60f1e91ebf86afc264a3ffad3ae810/6f3f2/mind-blowing.png 256w&quot;
        sizes=&quot;(max-width: 256px) 100vw, 256px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Although this experience is incredible I still feel it&apos;s a burden to go through it. Wouldn&apos;t it be better if a good article existed and spared you all the pain of learning/understanding what you wanted?&lt;/p&gt;
&lt;p&gt;This. This is my real motivation. To write easy-to-understand guides/articles, simplify hard concepts and teach people the way I think about several topics.&lt;/p&gt;
&lt;h3&gt;The internet era and the lack of good content&lt;/h3&gt;
&lt;p&gt;I love the internet so much, don&apos;t get me wrong, but I hate so much the amount of tech material/content that we can find online. I notice that there are two scenarios:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Too much content online, but 95% of it is just basic knowledge (or too repetitive) and because of the huge amount of content, it&apos;s hard to filter and find a good one that deep dives or simply explains it in a different way&lt;/li&gt;
&lt;li&gt;Niched topic lacking content online&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In either scenario, it will be difficult to find good content that deep dives into the topic you are looking for.&lt;/p&gt;
&lt;p&gt;My goal is to try as max as I can to avoid too basic discussions or only talk about the obvious parts. Being obvious sometimes is necessary, but I will try my best to be obvious only when needed to guarantee that readers of the blog will be on the same page before jumping into a more complex/advanced topic/real explanation.&lt;/p&gt;
&lt;h2&gt;My way of doing things&lt;/h2&gt;
&lt;p&gt;I&apos;m a very visual guy. I rather have one image/diagram rather than 2 pages of text. Also, I&apos;m a very hands-on person, I like to learn by doing, to understand how things work in real life.&lt;/p&gt;
&lt;p&gt;There is a famous saying that goes like this:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Give a man a fish, he eats for a day. Teach a man how to fish, he eats for a lifetime&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;And I&apos;m 100% like that. If I need to code something, I won&apos;t simply copy and paste, I will invite you to the documentation, add references and explain why I used that function/class. This is powerful, I don&apos;t want to &quot;show off&quot;, copy and paste what I did and say &quot;It works man, trust me&quot;. I want you to understand what I did, how I did it, how you can improve it and how you can do it yourself, going directly to the source.&lt;/p&gt;
&lt;p&gt;So, to sum up, you should expect:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Articles with diagrams, pictures and flowcharts (for all the visual guys, just like me)&lt;/li&gt;
&lt;li&gt;Opinionated based articles&lt;/li&gt;
&lt;li&gt;How-to guides&lt;/li&gt;
&lt;li&gt;Connecting the dots between different topics&lt;/li&gt;
&lt;li&gt;Experiments and POCs&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Hope you enjoy it and see you around! 👋&lt;/p&gt;</content:encoded></item></channel></rss>