<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Serhii Vasylenko</title>
    <description>The latest articles on DEV Community by Serhii Vasylenko (@svasylenko).</description>
    <link>https://dev.to/svasylenko</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F51518%2F60259e60-86ea-47fe-92ff-5ac3f00835d2.jpg</url>
      <title>DEV Community: Serhii Vasylenko</title>
      <link>https://dev.to/svasylenko</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/svasylenko"/>
    <language>en</language>
    <item>
      <title>A Deep Dive Into Terraform Static Code Analysis Tools: Features and Comparisons</title>
      <dc:creator>Serhii Vasylenko</dc:creator>
      <pubDate>Tue, 16 Apr 2024 23:32:03 +0000</pubDate>
      <link>https://dev.to/svasylenko/a-deep-dive-into-terraform-static-code-analysis-tools-features-and-comparisons-1kbf</link>
      <guid>https://dev.to/svasylenko/a-deep-dive-into-terraform-static-code-analysis-tools-features-and-comparisons-1kbf</guid>
      <description>&lt;p&gt;Many teams employ Terraform by HashiCorp to efficiently manage their infrastructure, leveraging its ability to automate the lifecycle of complex environments. Yet, integrating security scanning into Terraform pipelines often remains overlooked, exposing these environments to potential security risks and compliance issues.&lt;/p&gt;

&lt;p&gt;This article explores several prominent static code analyzers that support Terraform code and focus on its security scanning. This comparison will guide teams in choosing the right tool to enhance their security measures within Terraform workflows, ensuring safer and more compliant infrastructure management.&lt;/p&gt;

&lt;p&gt;Here are the tools we'll be reviewing: &lt;strong&gt;KICS&lt;/strong&gt;, &lt;strong&gt;tfsec&lt;/strong&gt;, &lt;strong&gt;Trivy&lt;/strong&gt;, &lt;strong&gt;Terrascan&lt;/strong&gt;, &lt;strong&gt;Checkov&lt;/strong&gt;, and &lt;strong&gt;Semgrep OSS&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;While many of these tools also support other platforms and technologies, &lt;strong&gt;this review will concentrate exclusively on their functionality with Terraform.&lt;/strong&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  Why use Static Code Analysis for Terraform
&lt;/h2&gt;

&lt;p&gt;Static code analysis tools are necessary to enhance the security of Terraform-managed infrastructures. Unlike linters, these tools focus not on syntax errors or coding style but delve deeply into the code to identify security vulnerabilities and potential compliance issues without running the actual code. This proactive approach to security helps safeguard the infrastructure from potential threats before deployment.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Benefits
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Early Detection: Identifies security vulnerabilities and misconfigurations early in development, preventing them from reaching production. &lt;/li&gt;
&lt;li&gt;Compliance Assurance: Ensures Terraform code complies with industry standards and internal security policies.&lt;/li&gt;
&lt;li&gt;Automated Security Integration: Seamlessly integrates with CI/CD pipelines, automating security checks to maintain a continuous focus on security.&lt;/li&gt;
&lt;li&gt;Actionable Insights: Delivers detailed vulnerability reports, facilitating swift and effective resolution.&lt;/li&gt;
&lt;li&gt;Scalability: Effectively handles increasing project complexity and size, maintaining rigorous security standards without additional manual effort.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Expected Features
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Policy Coverage&lt;/strong&gt;: The tool should offer comprehensive scanning capabilities to detect security vulnerabilities specific to Infrastructure as Code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Customizable Security Policies&lt;/strong&gt;: It must allow users to define and adjust security policies and severity levels to align with specific project needs or compliance requirements. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Seamless Integration&lt;/strong&gt;: The analyzer should integrate effortlessly with existing CI/CD tools and version control systems, facilitating a smooth workflow. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Detailed Reporting&lt;/strong&gt;: Clear and actionable reports are crucial. The tool should prioritize issues based on severity and provide practical steps for remediation. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scanning Customization&lt;/strong&gt;: Users should be able to tailor the scanning process to focus on particular aspects of the codebase, enabling targeted and efficient security assessments.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With a clear understanding of the necessary features in a static code analyzer, which tools on the market best fulfill these criteria?&lt;/p&gt;

&lt;p&gt;Let's take a closer look at some leading options!&lt;/p&gt;

&lt;h2&gt;
  
  
  Meet the Static Code Analyzers for Terraform
&lt;/h2&gt;

&lt;p&gt;Following on what makes a static code analyzer robust, let's dive into some open-source tools that exemplify these essential features.&lt;/p&gt;

&lt;p&gt;I picked six tools for my review. I know there are more on the market, but I focused on &lt;strong&gt;open-source, free-to-use&lt;/strong&gt; tools and those that provide at least &amp;gt;100 out-of-the-box scanning policies for Terraform.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;KICS&lt;/strong&gt; (stands for "Keeping Infrastructure as Code Secure"): &lt;br&gt;
Owner/Maintainer: Checkmarx&lt;br&gt;
Age: First released on GitHub on November 30th, 2020&lt;br&gt;
License: &lt;a href="https://github.com/Checkmarx/kics/blob/master/LICENSE"&gt;Apache License 2.0&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;tfsec&lt;/strong&gt;&lt;br&gt;
Owner/Maintainer: Aqua Security (acquired in 2021)&lt;br&gt;
Age: First released on GitHub on March 5th, 2019&lt;br&gt;
License: &lt;a href="https://github.com/aquasecurity/tfsec/blob/master/LICENSE"&gt;MIT License&lt;/a&gt;&lt;br&gt;
&lt;em&gt;tfsec project is no longer actively maintained in favor of the Trivy tool. But because many people still use it and it's quite famous, I added tfsec to this comparison.&lt;br&gt;
However, I recommend against using it for new projects.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Trivy&lt;/strong&gt;&lt;br&gt;
Owner/Maintainer: Aqua Security&lt;br&gt;
Age: First released on GitHub on May 7th, 2019&lt;br&gt;
License: &lt;a href="https://github.com/aquasecurity/trivy/blob/main/LICENSE"&gt;Apache License 2.0&lt;/a&gt;&lt;br&gt;
&lt;em&gt;backward-compatible with tfsec&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Terrascan&lt;/strong&gt;&lt;br&gt;
Owner/Maintainer: Tenable (acquired in 2022)&lt;br&gt;
Age: First release on GitHub on November 28th, 2017&lt;br&gt;
License: &lt;a href="https://github.com/tenable/terrascan/blob/master/LICENSE"&gt;Apache License 2.0&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Checkov&lt;/strong&gt;&lt;br&gt;
Owner/Maintainer: Prisma Cloud by Palo Alto Networks (acquired in 2021)&lt;br&gt;
Age: First released on GitHub on March 31st, 2021&lt;br&gt;
License: &lt;a href="https://github.com/bridgecrewio/checkov/blob/main/LICENSE"&gt;Apache License 2.0&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Semgrep OSS&lt;/strong&gt;&lt;br&gt;
Owner/Maintainer: Semgrep&lt;br&gt;
Age: First release on GitHub on February 6th, 2020&lt;br&gt;
License: &lt;a href="https://github.com/semgrep/semgrep/blob/develop/LICENSE"&gt;GNU Lesser General Public License v2.1&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;These tools are essential in enhancing Terraform's security posture and reflect a strong collaboration between open-source communities and enterprise backing. This blend ensures that the tools are not only accessible but also robustly maintained and up-to-date.&lt;/p&gt;

&lt;p&gt;Let’s explore how these tools stack up regarding features and usability.&lt;/p&gt;
&lt;h2&gt;
  
  
  Comparing Out-of-the-Box Policies and Terraform Providers
&lt;/h2&gt;

&lt;p&gt;Understanding the number and variety of default policies each tool offers is crucial for those just beginning to explore security automation for Terraform.&lt;/p&gt;

&lt;p&gt;The extent of out-of-the-box policies can significantly ease the integration process of static analysis by providing immediate and comprehensive insights into potential security and compliance issues. Similarly, the number of supported Terraform Providers also plays a critical role.&lt;/p&gt;

&lt;p&gt;In this chapter, we delve into these foundational features across observed tools, helping you pinpoint which one could best satisfy your requirements for robust, ready-to-use security scanning.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Policies&lt;/th&gt;
&lt;th&gt;Supported Terraform Providers&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;KICKS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;663&lt;/td&gt;
&lt;td&gt;aws, azure, gcp, kubernetes, alicloud, databricks, github, nifcloud&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;tfsec&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;154&lt;/td&gt;
&lt;td&gt;aws, azure, gcp, digitalocean, kubernetes, cloudstack, github, openstack, oracle&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Trivy&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;322&lt;/td&gt;
&lt;td&gt;aws, azure, gcp, digitalocean, cloudstack, github, oracle, openstack&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Terrascan&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;790&lt;/td&gt;
&lt;td&gt;aws, azure, gcp, digitalocean, kubernetes, docker, github&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Checkov&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;2110&lt;/td&gt;
&lt;td&gt;aws, azure, gcp, digitalocean, kubernetes, github, gitlab, ibm, linode, openstack, alicloud&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Semgrep OSS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;362&lt;/td&gt;
&lt;td&gt;aws, azure, gcp&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;As you can see, all tools support the "Big Three" cloud service Terraform providers—AWS, Azure, and GCP—for managing resources on these popular platforms.&lt;/p&gt;

&lt;p&gt;With over 2000 out-of-the-box policies, Checkov significantly stands out from the competition. This tool also leads in the total number of supported Terraform providers.&lt;/p&gt;

&lt;p&gt;While the default policies provide a strong foundation for security scanning, the ability to tailor these policies is just as crucial. Next, we'll explore how each tool accommodates custom policy capabilities, allowing you to fine-tune the policies to fit your project's specific requirements.&lt;/p&gt;
&lt;h2&gt;
  
  
  Custom Policy Capabilities
&lt;/h2&gt;

&lt;p&gt;Default policies serve as the foundation, but the nuances of each project demand the extension of this base.&lt;/p&gt;

&lt;p&gt;Here, we delve into how each tool enables you to add custom policies, thus enhancing and refining the provided defaults.&lt;/p&gt;

&lt;p&gt;While all six tools support adding custom policies to their default set, they differ in terminology: 'policy' is the common term, whereas KICS refers to them as 'queries,' and Semgrep calls them 'rules.'&lt;/p&gt;

&lt;p&gt;Regarding policy syntax:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;OPA Rego&lt;/strong&gt; syntax is used by &lt;a href="https://docs.kics.io/latest/creating-queries/"&gt;KICS&lt;/a&gt;, &lt;a href="https://aquasecurity.github.io/trivy/v0.50/docs/scanner/misconfiguration/custom/"&gt;Trivy&lt;/a&gt;, &lt;a href="https://aquasecurity.github.io/tfsec/latest/guides/rego/rego/"&gt;tfsec&lt;/a&gt;, and &lt;a href="https://runterrascan.io/docs/policies/policies/"&gt;Terrascan&lt;/a&gt;.  It's a powerful language widely adopted in the industry, though there's a learning curve that could pay dividends for future projects.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;YAML&lt;/strong&gt; syntax is used by &lt;a href="https://www.checkov.io/3.Custom%20Policies/Custom%20Policies%20Overview.html"&gt;Checkov&lt;/a&gt; and &lt;a href="https://semgrep.dev/docs/writing-rules/rule-syntax/"&gt;Semgrep&lt;/a&gt;. This offers a familiar and straightforward start, with Checkov also allowing policies to be written in Python, albeit with some constraints. With YAML, the ease of use is balanced against the limitations set by the tool's capabilities.&lt;/p&gt;

&lt;p&gt;Understanding these differences will guide you to a tool that matches your security requirements, your team's expertise, and the scope of your infrastructure projects.&lt;/p&gt;

&lt;p&gt;To illustrate, here is an example of a KICS Rego policy checking for default RDS instance ports:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package Cx

import data.generic.common as common_lib
import data.generic.terraform as tf_lib

CxPolicy[result] {
    db := input.document[i].resource.aws_db_instance[name]

    enginePort := common_lib.engines[e]

    db.engine == e
    db.port == enginePort

    result := {
        "documentId": input.document[i].id,
        "resourceType": "aws_db_instance",
        "resourceName": tf_lib.get_resource_name(db, name),
        "searchKey": sprintf("aws_db_instance[%s].port", [name]),
        "issueType": "IncorrectValue",
        "keyExpectedValue": sprintf("aws_db_instance[%s].port should not be set to %d", [name, enginePort]),
        "keyActualValue": sprintf("aws_db_instance[%s].port is set to %d", [name, enginePort]),
        "searchLine": common_lib.build_search_line(["resource", "aws_db_instance", name, "port"], []),
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And an example of a Checkov YAML policy forbidding specific EC2 instance types:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
 &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Org's&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;compute&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;instances&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;should&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;not&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;be&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;p5.48xlarge&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;or&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;p4d.24xlarge"&lt;/span&gt;
 &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ACME_AWS_FORBIDDEN_EC2_TYPES"&lt;/span&gt;
 &lt;span class="na"&gt;category&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;NETWORKING"&lt;/span&gt;
&lt;span class="na"&gt;definition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
 &lt;span class="na"&gt;or&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
 &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;cond_type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;attribute"&lt;/span&gt;
   &lt;span class="na"&gt;resource_types&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;aws_instance"&lt;/span&gt;
   &lt;span class="na"&gt;attribute&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;instance_type"&lt;/span&gt;
   &lt;span class="na"&gt;operator&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;not_equals"&lt;/span&gt;
   &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;p5.48xlarge"&lt;/span&gt;
 &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;cond_type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;attribute"&lt;/span&gt;
   &lt;span class="na"&gt;resource_types&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
   &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;aws_instance"&lt;/span&gt;
   &lt;span class="na"&gt;attribute&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;instance_type"&lt;/span&gt;
   &lt;span class="na"&gt;operator&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;not_equals"&lt;/span&gt;
   &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;p4d.24xlarge"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With the ability to tailor policies to our specific needs, we'll next explore each tool's capacity to integrate broadly, determining how well they play with the rest of our tech stack.&lt;/p&gt;

&lt;h2&gt;
  
  
  Integration Capabilities
&lt;/h2&gt;

&lt;p&gt;Integration capabilities are the cornerstone of efficient DevOps practices.&lt;/p&gt;

&lt;p&gt;This section will evaluate how each static code analyzer enhances your tech stack through seamless integration with other systems and technologies.&lt;/p&gt;

&lt;p&gt;We will assess each tool against four key integration points that are vital for development workflows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Docker Image&lt;/strong&gt;: Ensures easy deployment across any container-supported environment.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IDE Plugins&lt;/strong&gt;: Facilitates real-time feedback and improves code quality directly within the developer's workspace.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CI/CD Systems&lt;/strong&gt;: Supports direct integration through plugins or extensions, eliminating the need for manual downloads or CLI setups.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pre-commit Hook&lt;/strong&gt;: Provides an early security checkpoint by scanning code before it is committed, catching errors at the initial stages.&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Docker Image&lt;/th&gt;
&lt;th&gt;IDE Plugins&lt;/th&gt;
&lt;th&gt;CI/CD Systems&lt;/th&gt;
&lt;th&gt;Hook&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;KICKS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;VSCode&lt;/td&gt;
&lt;td&gt;Github Actions, Terraform Cloud, Codefresh&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;tfsec&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;VSCode, JetBrains, Vim&lt;/td&gt;
&lt;td&gt;Github Actions&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Trivy&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;VSCode, JetBrains, Vim&lt;/td&gt;
&lt;td&gt;Azure DevOps, GitHub Actions, Buildkite, Dagger, Semaphore, CircleCI, Concourse CI&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Terrascan&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;VSCode&lt;/td&gt;
&lt;td&gt;GitHub Actions, Atlantis&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Checkov&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;VSCode, JetBrains&lt;/td&gt;
&lt;td&gt;GitHub Actions&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Semgrep OSS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;VSCode, JetBrains, Emacs, Vim&lt;/td&gt;
&lt;td&gt;GitLab&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;In addition to the table above, here are a few noteworthy features of some tools:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Checkov supports OpenAI integration to suggest remediations. But be careful because AI tends to hallucinate.&lt;/li&gt;
&lt;li&gt;KICS supports applying auto-remediation for some of its out-of-the-box policies. This also applies to custom policies, where you can define remediations and apply them automatically.&lt;/li&gt;
&lt;li&gt;Terrascan is the only one that provides the VSCode extension to create and test custom policies written in Rego.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Having covered the integration capabilities, let’s now focus on the output formats each tool provides.&lt;/p&gt;

&lt;h2&gt;
  
  
  Output Formats Provided
&lt;/h2&gt;

&lt;p&gt;Output formats extend the utility of static code analysis, facilitating integration with the CI/CD feedback loop and enabling its use as an artifact in subsequent CI jobs.&lt;/p&gt;

&lt;p&gt;This chapter examines the variety of formats each tool supports for this purpose.&lt;/p&gt;

&lt;p&gt;Each tool offers a range of output formats tailored to different needs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For GitLab users&lt;/strong&gt;: For teams leveraging GitLab's security scanning, KICS, Checkov, and Semgrep OSS are equipped with compatible output formats, facilitating smooth GitLab integration.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For GitHub users&lt;/strong&gt;: SARIF's adoption as an industry standard, particularly by GitHub for code scanning, makes it a must-have. All tools assessed offer SARIF support, ensuring interoperability and broad utility.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;JUnit Reports&lt;/strong&gt;: The availability of JUnit output is crucial for capturing test results in a format recognizable by various CI systems. Trivy, Terrascan, Checkov, and Semgrep OSS support this, enabling clear visualization of test outcomes and enhancing the feedback loop within CI pipelines.&lt;/p&gt;

&lt;p&gt;Beyond these, each tool supports additional formats, enriching their application and versatility. Here's the full breakdown of the output formats, complementing the standard CLI output:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Supported Output Formats&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;KICKS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;ASFF, CSV, Code Climate, CycloneDX, GItLab SAST, HTML, JSON, JUnit, PDF, SARIF, SonarQube&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;tfsec&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Checkstyle, CSV, HTML, JSON, JUnit, Markdown, SARIF&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Trivy&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;ASFF, Cosign, CycloneDX, JSON, SARIF, SPDX&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Terrascan&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;JSON, JUnit, SARIF, XML, YAML&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Checkov&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;CSV, CycloneDX, GItLab SAST, JSON, JUnit, SARIF, SPDX&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Semgrep OSS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Emacs, GitLab SAST, JSON, JUnit, SARIF, Vim&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Moving from output formats to operational adaptability, let's investigate the customization options for scanner settings. This important feature allows each tool to align with varied project demands.&lt;/p&gt;

&lt;h2&gt;
  
  
  Customizing Scanner Settings
&lt;/h2&gt;

&lt;p&gt;This chapter moves beyond the default scanner settings and delves into scanner settings' customizability, ensuring that tools can be calibrated for any development environment or security requirement.&lt;/p&gt;

&lt;p&gt;I will evaluate each tool against criteria that define a tool's adaptability and user-friendliness:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Targeted Scans&lt;/strong&gt;: Select specific directories for scanning or exclusion to focus on pertinent areas and skip irrelevant ones.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;In-Code Ignore Policies&lt;/strong&gt;: Enable ignore directives within code to skip checks when exceptions apply selectively.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Severity Thresholds&lt;/strong&gt;: Set reporting to include only findings above a chosen severity level, concentrating on the most impactful issues.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Configuration File&lt;/strong&gt;: Employ configuration files for consistency and collaboration, enabling a 'configuration as code' approach.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TF Variables Interpolation&lt;/strong&gt;: Interpret and evaluate Terraform variables for an accurate security assessment of IaC.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Module Scanning&lt;/strong&gt;: For complete coverage, scans should include both local and remote (public/private) Terraform modules.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Based on these criteria, the following table offers a comparative view of how each tool performs, giving you a clear snapshot of their customization capabilities:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Targeted Scans&lt;/th&gt;
&lt;th&gt;Ignore Policies&lt;/th&gt;
&lt;th&gt;Min Severity&lt;/th&gt;
&lt;th&gt;Config File&lt;/th&gt;
&lt;th&gt;Variables Interpolation&lt;/th&gt;
&lt;th&gt;Module Scanning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;KICKS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;⁉️️&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;tfsec&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Trivy&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Terrascan&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Checkov&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Semgrep OSS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Most reviewed tools meet nearly all the criteria set for scanner setting customization, demonstrating their flexibility and advanced capabilities. However, there are notable features worth considering:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;KICS: Provides limited module scanning capabilities, restricted to some public modules from the Terraform registry, and does not cover local or private custom modules.&lt;/li&gt;
&lt;li&gt;Terrascan &amp;amp; Trivy: Both feature server modes that centralize vulnerability databases. This centralization facilitates a unified approach to applying policies and configurations, enhancing consistency and efficiency for teams and reducing the management overhead of diverse policies across multiple projects.&lt;/li&gt;
&lt;li&gt;Semgrep: It doesn't support scanner configuration files; instead, it uses the "config" word to call the rule sets and accepts such configs. Notably, it also does not support the scanning of Terraform modules at all.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Terraform Security Scanning: The Big Picture and Top Pick
&lt;/h2&gt;

&lt;p&gt;Here's a comprehensive comparison summary to guide your selection of the most suitable Terraform static code analyzer:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Default Policies&lt;/th&gt;
&lt;th&gt;Custom Policies&lt;/th&gt;
&lt;th&gt;Integration&lt;/th&gt;
&lt;th&gt;Output Formats&lt;/th&gt;
&lt;th&gt;Customization&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;KICKS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;663&lt;/td&gt;
&lt;td&gt;OPA Rego&lt;/td&gt;
&lt;td&gt;✅Docker, ✅IDE, ✅CI/CD, ✅Git Hook&lt;/td&gt;
&lt;td&gt;ASFF, CSV, Code Climate, CycloneDX, GItLab SAST, HTML, JSON, JUnit, PDF, SARIF, SonarQube&lt;/td&gt;
&lt;td&gt;✅Targeted Scans, ✅Ignore Policies, ✅Min Severity, ✅Config File, ✅Variables Interpolation, ❌Module Scanning&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;tfsec&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;154&lt;/td&gt;
&lt;td&gt;OPA Rego&lt;/td&gt;
&lt;td&gt;✅Docker, ✅IDE, ✅CI/CD, ❌Git Hook&lt;/td&gt;
&lt;td&gt;Checkstyle, CSV, HTML, JSON, JUnit, Markdown, SARIF&lt;/td&gt;
&lt;td&gt;✅Targeted Scans, ✅Ignore Policies, ✅Min Severity, ✅Config File, ✅Variables Interpolation, ✅Module Scanning&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Trivy&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;322&lt;/td&gt;
&lt;td&gt;OPA Rego&lt;/td&gt;
&lt;td&gt;✅Docker, ✅IDE, ✅CI/CD, ❌Git Hook&lt;/td&gt;
&lt;td&gt;ASFF, Cosign, CycloneDX, JSON, SARIF, SPDX&lt;/td&gt;
&lt;td&gt;✅Targeted Scans, ✅Ignore Policies, ✅Min Severity, ✅Config File, ✅Variables Interpolation, ✅Module Scanning&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Terrascan&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;790&lt;/td&gt;
&lt;td&gt;OPA Rego&lt;/td&gt;
&lt;td&gt;✅Docker, ✅IDE, ✅CI/CD, ✅Git Hook&lt;/td&gt;
&lt;td&gt;JSON, JUnit, SARIF, XML, YAML&lt;/td&gt;
&lt;td&gt;✅Targeted Scans, ✅Ignore Policies, ✅Min Severity, ✅Config File, ✅Variables Interpolation, ✅Module Scanning&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Checkov&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;2110&lt;/td&gt;
&lt;td&gt;YAML, Python&lt;/td&gt;
&lt;td&gt;✅Docker, ✅IDE, ✅CI/CD, ✅Git Hook&lt;/td&gt;
&lt;td&gt;CSV, CycloneDX, GItLab SAST, JSON, JUnit, SARIF, SPDX&lt;/td&gt;
&lt;td&gt;✅Targeted Scans, ✅Ignore Policies, ✅Min Severity, ✅Config File, ✅Variables Interpolation, ✅Module Scanning&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Semgrep OSS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;362&lt;/td&gt;
&lt;td&gt;YAML&lt;/td&gt;
&lt;td&gt;✅Docker, ✅IDE, ✅CI/CD, ✅Git Hook&lt;/td&gt;
&lt;td&gt;Emacs, GitLab SAST, JSON, JUnit, SARIF, Vim&lt;/td&gt;
&lt;td&gt;✅Targeted Scans, ✅Ignore Policies, ✅Min Severity, ❌Config File, ✅Variables Interpolation, ❌Module Scanning&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Integrating a Terraform security scanning into your development pipeline is a proven strategy to boost your security posture. These tools detect potential vulnerabilities early and enforce best practices and compliance standards, representing a proactive approach to infrastructure security.&lt;/p&gt;

&lt;p&gt;For teams not yet utilizing these tools, &lt;strong&gt;Checkov&lt;/strong&gt; is my top recommendation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Biggest number of default policies and supported Terraform providers for a quick start.&lt;/li&gt;
&lt;li&gt;Custom policy support in YAML and Python for flexible policy creation.&lt;/li&gt;
&lt;li&gt;Wide integration options with Docker, IDEs, CI/CD systems, and Git Hooks for a smooth workflow.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Please share your favorite tool in the comments below! Also, let me know if I missed a cool product that should have been included in the review.&lt;/p&gt;

</description>
      <category>terraform</category>
      <category>security</category>
      <category>devops</category>
      <category>development</category>
    </item>
    <item>
      <title>Mastering AWS API Gateway V2 HTTP and AWS Lambda With Terraform</title>
      <dc:creator>Serhii Vasylenko</dc:creator>
      <pubDate>Thu, 11 Jan 2024 15:18:00 +0000</pubDate>
      <link>https://dev.to/aws-builders/mastering-aws-api-gateway-v2-http-and-aws-lambda-with-terraform-2d8d</link>
      <guid>https://dev.to/aws-builders/mastering-aws-api-gateway-v2-http-and-aws-lambda-with-terraform-2d8d</guid>
      <description>&lt;p&gt;With a solid foundation in AWS API Gateway and Lambda for serverless architecture, my recent deep dive into these cloud computing services felt like uncovering new layers in familiar territory. This article aims to be a comprehensive guide for developers and DevOps professionals looking to master serverless solutions using AWS and Terraform.&lt;/p&gt;

&lt;p&gt;The article provides an in-depth guide to combining AWS API Gateway V2 HTTP API (yes, this is the official name of that service 😄) and AWS Lambda services to implement a simple, robust, and cost-effective serverless back-end using Terraform.&lt;/p&gt;

&lt;p&gt;The journey was enlightening and engaging, especially as I were transforming these services into Infrastructure as Code. Through this article, I aim to share those moments of insight and the practical, hands-on tips that emerged from weaving these AWS services into a seamless, serverless architecture.&lt;/p&gt;

&lt;h2&gt;
  
  
  Navigating the System Design: HTTP API Gateway and Lambda in Action
&lt;/h2&gt;

&lt;p&gt;Beginning our journey, we examine the complexities of serverless architecture, focusing on HTTP API and Lambda. A comprehensive system diagram will guide us as we analyze each component’s function and their collaborative roles in the larger infrastructure.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Famw5ppbhu53u4cnb23wa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Famw5ppbhu53u4cnb23wa.png" alt="HTTP API Gateway and AWS Lambda flowchart"&gt;&lt;/a&gt;&lt;br&gt;
In our architecture, the HTTP API delegates access control to the Lambda function called “Authorizer”. This function stands as the gatekeeper, ensuring that only legitimate requests pass through to the underlying business logic.&lt;/p&gt;

&lt;p&gt;The HTTP API can have multiple routes (e.g., “/calendar,” “/meters,” and so on) and use different Authorizers per route or a single one for all of them. Clients that send their requests to the API must include specific identification information in their request header or query string. In this project, I go with a single authorizer to keep it simple.&lt;/p&gt;

&lt;p&gt;Upon receiving a request, the API service forwards a payload to the Authorizer containing metadata about the request, such as headers and query string components. The Authorizer processes this metadata (headers, in my case) to determine the request’s legitimacy.&lt;/p&gt;

&lt;p&gt;The decision, Allow or Deny, is passed back to the API, and if allowed, the API service then forwards the original request to the back-end, which, in this case, is implemented by additional Lambda functions. Otherwise, the client gets a response with a 403 status code, and the original request is not passed to the back-end.&lt;/p&gt;
&lt;h2&gt;
  
  
  Behind The Decision: Why Such a Setup?
&lt;/h2&gt;

&lt;p&gt;Choosing the right architectural setup is critical in balancing simplicity, cost-efficiency, and security. In this section, we uncover why integrating AWS HTTP API Gateway with Lambda Authorizer is a compelling choice, offering a streamlined approach without compromising security.&lt;/p&gt;
&lt;h3&gt;
  
  
  Cost-Effectiveness: Balancing Performance and Price
&lt;/h3&gt;

&lt;p&gt;The AWS HTTP API is noteworthy for its streamlined and simple design compared to other API Gateway options. That translates directly into cost savings for businesses. Its efficiency makes it an ideal choice for cost-effective serverless computing, especially for those looking to optimize their cloud infrastructure with Terraform automation. Here is a more detailed comparison of different API Gateway options — &lt;a href="https://docs.aws.amazon.com/whitepapers/latest/best-practices-api-gateway-private-apis-integration/cost-optimization.html" rel="noopener noreferrer"&gt;Cost optimization.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Security with Lambda Authorizer. This option means a Lambda function used for authorization, which is lean and efficient. It generally requires a bare minimum of resources. It executes quickly, particularly when configured with the ARM-based environment and 128M RAM allocation, costing $0,0000017 per second of running time, with $0.20 per 1M requests per month.&lt;/p&gt;

&lt;p&gt;💰 This pricing and performance combination are well-suited for rapid, lightweight authorizations. Together with AWS Lambda as a back-end, it makes a cost-effective solution. For example, if we add a few more Lambdas to back-end and assume that our setup receives 10000 requests per month, it would cost around $0.6 per month. Here is the link to detailed calculations — &lt;a href="https://calculator.aws/#/estimate?id=b1a8a473ab98ede32f5ca384c5e9487b967efafa" rel="noopener noreferrer"&gt;AWS Pricing Calculator&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Simplicity in Configuration: The Power of Header-Based Authorization
&lt;/h3&gt;

&lt;p&gt;A header-based authentication method facilitates straightforward client-server communication, often requiring less coding and resources to implement compared to more complex schemes.&lt;/p&gt;

&lt;p&gt;Although HTTP API offers stronger JWT-based authorization and mutual TLS authentication, header-based authorization remains a suitable choice for simpler applications that prioritize ease and quickness. By the way, there is also an option for IAM-based authorization whose core idea is the “private API” or internal usage of the API (e.g., solely inside the VPC, no internet), but with “&lt;a href="https://docs.aws.amazon.com/rolesanywhere/latest/userguide/introduction.html" rel="noopener noreferrer"&gt;IAM Anywhere&lt;/a&gt;,” this can be expanded to practically anywhere. 😁&lt;/p&gt;

&lt;p&gt;This architecture suits applications requiring rapid development and deployment without complex authorization mechanisms. It’s ideal for small to medium-sized serverless applications or specific use cases in larger systems where quick, cost-effective, and secure access to APIs is a priority.&lt;/p&gt;

&lt;p&gt;💡 Imagine a retail company wanting to manage its inventory efficiently. By leveraging AWS API Gateway and Lambda, they can develop a system where each item’s RFID tags are scanned and processed through an API endpoint. When a product is moved or sold, its status is updated in real-time in the database, facilitated by Lambda functions. This serverless architecture ensures high availability and scalability and significantly reduces operational costs, a crucial factor for the highly competitive retail industry. This example showcases how our serverless setup can be effectively utilized in retail for streamlined inventory tracking and management.&lt;/p&gt;
&lt;h2&gt;
  
  
  Exploring AWS Lambda: Features and Integration
&lt;/h2&gt;

&lt;p&gt;Diving into AWS Lambda, this section explores its features and indispensable role within the serverless infrastructure. We will unravel the complexities of Lambda functions and examine the practicalities of deploying and managing these functions within the project.&lt;/p&gt;
&lt;h3&gt;
  
  
  AWS Lambda Runtime and Deployment Model
&lt;/h3&gt;

&lt;p&gt;🚀 Choosing the AWS &lt;strong&gt;Lambda runtime arm64&lt;/strong&gt;, combined with the &lt;strong&gt;OS-only runtime based on Amazon Linux 2023&lt;/strong&gt;, strategically boosts cost efficiency and performance. This choice aligns with the best practices for serverless computing in AWS, offering an optimal solution for those seeking to leverage AWS services for scalable cloud solutions.&lt;/p&gt;

&lt;p&gt;Particularly effective for Go-based functions, this runtime configuration is lean yet powerful. For applications in other languages, delving into &lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html" rel="noopener noreferrer"&gt;language-specific runtimes based on AL 2023&lt;/a&gt; can also leverage the latest efficiencies of AWS-managed operating systems.&lt;/p&gt;

&lt;p&gt;I also welcome you to read this benchmarking analysis to get more insights about the ARM-based environment for AWS Lambda — &lt;a href="https://aws.amazon.com/blogs/apn/comparing-aws-lambda-arm-vs-x86-performance-cost-and-analysis-2/" rel="noopener noreferrer"&gt;Comparing AWS Lambda Arm vs. x86 Performance, Cost, and Analysis&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;.zip deployment&lt;/strong&gt; model is chosen for its simplicity, avoiding additional management of the image registry (ECR) and Docker images. Also, AWS automatically patches .zip functions for the latest runtime security and bug fixes.&lt;/p&gt;
&lt;h3&gt;
  
  
  Efficient Terraform Coding for AWS Lambda
&lt;/h3&gt;

&lt;p&gt;In our architecture, AWS Lambda functions serve dual purposes — as an authentication gatekeeper and a robust back-end for business logic. Despite varying code across functions, their configurations share much of similarities.&lt;/p&gt;

&lt;p&gt;By adhering to the DRY (Don't Repeat Yourself) principle, I have crafted a Terraform module to streamline the management of Lambda functions and their dependencies. This approach ensures maintainable and scalable infrastructure. The module's structure is as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;aws_lambda_function&lt;/code&gt; — to describe the core configuration of the function&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;aws_iam_role&lt;/code&gt; + &lt;code&gt;aws_iam_role_policy&lt;/code&gt; + &lt;code&gt;aws_iam_policy_document&lt;/code&gt; — to manage the access from Lambda to other resources (e.g., SSM Parameter Store)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;aws_cloudwatch_log_group&lt;/code&gt; — to keep the execution logs&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;aws_ssm_parameter&lt;/code&gt; — to store sensitive information (e.g., secrets) and other configurations that we should keep separate from the source code.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This Terraform module implements a project-specific use case for Lambda functions. However, if you're seeking for a generic all-in-one module for AWS Lambda, I recommend checking out this one — &lt;a href="https://registry.terraform.io/modules/terraform-aws-modules/lambda/aws/latest" rel="noopener noreferrer"&gt;Terraform AWS Lambda Module&lt;/a&gt; by Anton Babenko. &lt;/p&gt;

&lt;p&gt;To efficiently develop Terraform code for Lambda functions, use the following techniques:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use local values, expressions, and variables to implement consistent naming across different resources logically grouped by a module or project;&lt;/li&gt;
&lt;li&gt;Use function environment variables to connect the code with SSM Parameter Store parameters or Secrets Manager secrets to protect sensitive data like tokens or credentials;&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;for_each&lt;/code&gt; meta-argument and &lt;code&gt;for&lt;/code&gt; expression to reduce the amount of code and automate the configuration for resources of the same type (e.g., &lt;code&gt;ssm_parameter&lt;/code&gt;) or code blocks within a resource.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Below is a practical example illustrating these Terraform strategies in action:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="nx"&gt;locals&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
  &lt;span class="nx"&gt;full_function_name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;project_name&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;function_name&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;  
&lt;span class="p"&gt;}&lt;/span&gt;  
&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_lambda_function"&lt;/span&gt; &lt;span class="s2"&gt;"this"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
  &lt;span class="nx"&gt;function_name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;full_function_name&lt;/span&gt;  
  &lt;span class="nx"&gt;role&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_iam_role&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arn&lt;/span&gt;  
  &lt;span class="nx"&gt;architectures&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"arm64"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;  
  &lt;span class="nx"&gt;filename&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deployment_file&lt;/span&gt;  
  &lt;span class="nx"&gt;package_type&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Zip"&lt;/span&gt;  
  &lt;span class="nx"&gt;runtime&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"provided.al2023"&lt;/span&gt;  
  &lt;span class="nx"&gt;handler&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"bootstrap.handler"&lt;/span&gt;  
  &lt;span class="nx"&gt;timeout&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;function_timeout&lt;/span&gt;  
  &lt;span class="nx"&gt;environment&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
    &lt;span class="nx"&gt;variables&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;for&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt; &lt;span class="nx"&gt;in&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;function_ssm_parameter_names&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;upper&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"-"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"_"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;aws_ssm_parameter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;function_ssm_parameters&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;  
  &lt;span class="p"&gt;}&lt;/span&gt;  
&lt;span class="p"&gt;}&lt;/span&gt;  

&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_ssm_parameter"&lt;/span&gt; &lt;span class="s2"&gt;"function_ssm_parameters"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
  &lt;span class="nx"&gt;for_each&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;function_ssm_parameter_names&lt;/span&gt;  
  &lt;span class="nx"&gt;name&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"/projects/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;project_name&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/lambda/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;function_name&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;each&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;  
  &lt;span class="nx"&gt;type&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"SecureString"&lt;/span&gt;  
  &lt;span class="nx"&gt;key_id&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_kms_alias&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ssm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arn&lt;/span&gt;  
  &lt;span class="nx"&gt;value&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"1"&lt;/span&gt;  
  &lt;span class="nx"&gt;lifecycle&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
    &lt;span class="nx"&gt;ignore_changes&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;  
      &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
    &lt;span class="p"&gt;]&lt;/span&gt;  
  &lt;span class="p"&gt;}&lt;/span&gt;  
&lt;span class="p"&gt;}&lt;/span&gt;  

&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_cloudwatch_log_group"&lt;/span&gt; &lt;span class="s2"&gt;"this"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
  &lt;span class="nx"&gt;name&lt;/span&gt;              &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"/aws/lambda/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="kd"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;full_function_name&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;  
  &lt;span class="nx"&gt;log_group_class&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"STANDARD"&lt;/span&gt;  
  &lt;span class="nx"&gt;retention_in_days&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;  
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;The complete terraform module code is available in the &lt;a href="https://github.com/vasylenko/inkyframe/blob/main/infra/modules/lambda/main.tf" rel="noopener noreferrer"&gt;project repository&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In this Terraform code, I deliberately hardcoded specific arguments for an optimal Lambda runtime configuration, ensuring efficiency and performance.&lt;/p&gt;

&lt;p&gt;Then variables and local values, set only once, implement a naming convention for all resource arguments, making it easy to understand the infrastructure and change the naming and attributes later.&lt;/p&gt;

&lt;p&gt;Lambda's environment variables and corresponding SSM parameters coexist effectively with the help of &lt;code&gt;for_each&lt;/code&gt; and &lt;code&gt;for&lt;/code&gt;. I used the &lt;code&gt;for_each&lt;/code&gt; meta-argument to dynamically create SSM Parameter resources and the &lt;code&gt;for&lt;/code&gt; expression to configure environment variables in AWS Lambda. This also means that if the &lt;code&gt;function_ssm_parameter_names&lt;/code&gt; variable value is not provided, then Terraform does not create either SSM parameter resources or the environment code block inside the Lambda resource because the default value of that variable is an empty set.&lt;/p&gt;

&lt;p&gt;By the way, I have another blog post that explains several techniques to enhance your Terraform proficiency — &lt;a href="https://devdosvid.blog/2022/01/16/some-techniques-to-enhance-your-terraform-proficiency/?utm_medium=social&amp;amp;utm_source=devto&amp;amp;utm_campaign=blog-promo" rel="noopener noreferrer"&gt;check it out&lt;/a&gt;!&lt;/p&gt;

&lt;h3&gt;
  
  
  Invoking Lambda: Permissions and Resource-Based Policies
&lt;/h3&gt;

&lt;p&gt;Configured with just a few input variables, the Terraform module efficiently outputs the &lt;code&gt;aws_lambda_function&lt;/code&gt; resource. This streamlined output is then adeptly used to facilitate subsequent configurations within the HTTP API.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="s2"&gt;"lambda_api_gw_authorizer"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
  &lt;span class="nx"&gt;source&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"./modules/lambda"&lt;/span&gt;  
  &lt;span class="nx"&gt;deployment_file&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"../backend/lambda-apigw-authorizer/deployment.zip"&lt;/span&gt;  
  &lt;span class="nx"&gt;function_name&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"api-gateway-authorizer"&lt;/span&gt;  
  &lt;span class="nx"&gt;project_name&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;project_name&lt;/span&gt;  
  &lt;span class="nx"&gt;function_ssm_parameters&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;  
    &lt;span class="s2"&gt;"authorization-token"&lt;/span&gt;  
  &lt;span class="p"&gt;]&lt;/span&gt;  
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="s2"&gt;"lambda_calendar_backend"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
  &lt;span class="nx"&gt;source&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"./modules/lambda"&lt;/span&gt;  
  &lt;span class="nx"&gt;deployment_file&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"../backend/lambda-calendar-backend/deployment.zip"&lt;/span&gt;  
  &lt;span class="nx"&gt;function_name&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"calendar-backend"&lt;/span&gt;  
  &lt;span class="nx"&gt;project_name&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;project_name&lt;/span&gt;  
  &lt;span class="nx"&gt;function_ssm_parameters&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;  
    &lt;span class="s2"&gt;"google-api-oauth-token"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
    &lt;span class="s2"&gt;"google-api-credentials"&lt;/span&gt;  
  &lt;span class="p"&gt;]&lt;/span&gt;  
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As an example of module output usage, here is the configuration of &lt;code&gt;aws_lambda_permissions&lt;/code&gt; resource that I use outside the AWS Lambda module to allow the HTTP API service to invoke the function used as Authorizer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_lambda_permission"&lt;/span&gt; &lt;span class="s2"&gt;"allow_api_gw_invoke_authorizer"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
  &lt;span class="nx"&gt;statement_id&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"allowInvokeFromAPIGatewayAuthorizer"&lt;/span&gt;  
  &lt;span class="nx"&gt;action&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"lambda:InvokeFunction"&lt;/span&gt;  
  &lt;span class="nx"&gt;function_name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lambda_api_gw_authorizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;function_name&lt;/span&gt;  
  &lt;span class="nx"&gt;principal&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"apigateway.amazonaws.com"&lt;/span&gt;  
  &lt;span class="nx"&gt;source_arn&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;aws_apigatewayv2_api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;execution_arn&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/authorizers/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;aws_apigatewayv2_authorizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;header_based_authorizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;  
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Lambda resource-based policy combines the trust and permission policies, and provides a simple yet efficient way to grant other AWS services or principals the ability to invoke Lambda functions. It is important to note that for an API to invoke a function, Lambda requires its &lt;strong&gt;execution&lt;/strong&gt; ARN, not the resource ARN.&lt;/p&gt;

&lt;p&gt;As a side note, check out this &lt;a href="https://docs.aws.amazon.com/lambda/latest/operatorguide/intro.html" rel="noopener noreferrer"&gt;AWS Lambda Operator Guide&lt;/a&gt;, which offers specialized advice on developing, securing, and monitoring applications based on AWS Lambda.&lt;/p&gt;

&lt;p&gt;Let's switch to the HTTP API part to see how it looks and learn how it integrates Lambda functions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deep Dive into HTTP API Gateway
&lt;/h2&gt;

&lt;p&gt;Now, we focus on the HTTP API Gateway, delving into its essential concepts, seamless integration with AWS Lambda, and using Terraform efficiently for streamlined configuration.&lt;/p&gt;

&lt;p&gt;But before we do that, and since we have partially covered the Terraform code already, I'd like to illustrate the logical connection between three main components of the project's Terraform codebase: AWS Lambda, HTTP API, and API Routes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff9d3sv28obn3kgz6h7j6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff9d3sv28obn3kgz6h7j6.png" alt="AWS Lambda and HTTP API Terraform integration diagram"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I will explain the API Route module in detail a bit later, but for now, for the broader context, here is what happens inside Terraform:&lt;br&gt;
AWS HTTP API code logically represents the "global" (within a project) set of resources and uses the function created by the Lambda Terraform module for the Authorizer configuration. Meanwhile, the API Route Terraform module configures specific routes for the HTTP API (hence, requires some info from it) with integration to back-ends implemented by Lambdas (hence, requires some info from them, too).&lt;/p&gt;

&lt;p&gt;Back to HTTP API overview. The following &lt;strong&gt;components of the HTTP API&lt;/strong&gt; constitute its backbone:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Route&lt;/strong&gt; — a combination of the HTTP method (e.g., GET or POST) with the API route (e.g., /meters). For example: "POST /meters". Routes can optionally use &lt;strong&gt;Authorizers&lt;/strong&gt; — a mechanism to control access to the HTTP API.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Integration&lt;/strong&gt; — the technical and logical connection between the Route and one of the supported back-end resources. For example, with AWS Lambda integration, API Gateway sends the entire request as input to a back-end Lambda function and then transforms the Lambda function output to a front-end HTTP response.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stage and Deployment&lt;/strong&gt; — A stage serves as a designated reference to a deployment, essentially capturing a snapshot of the API at a certain point. It's employed to control and optimize a specific deployment version. For instance, stage configurations can be adjusted to tailor request throttling, set up logging, or establish stage variables to be used by API (if needed).&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Implementing AWS API Gateway V2 HTTP API with Terraform
&lt;/h3&gt;

&lt;p&gt;Below, I detail the Terraform resources essential for implementing the HTTP API, ensuring a transparent and effective setup:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;aws_apigatewayv2_api&lt;/code&gt; — the HTTP API itself;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;aws_apigatewayv2_route&lt;/code&gt; — the Route for the API that must specify the integration target (e.g., Lambda) and, optionally, the Authorizer;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;aws_apigatewayv2_authorizer&lt;/code&gt; — the Authorizer to use for Routes;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;aws_apigatewayv2_integration&lt;/code&gt; — the resource that specifies the back-end where the API sends the requests (e.g., AWS Lambda);&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;aws_lambda_permission&lt;/code&gt; — the resource-based policy for AWS Lambda to allow the invocations from the API;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;aws_apigatewayv2_stage&lt;/code&gt; — the name of the Stage that references the Deployment.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Applying Terraform for HTTP API Gateway and Lambda Authorizer
&lt;/h3&gt;

&lt;p&gt;The HTTP API is the simplest in the API Gateway family (so far), so its Terraform resource has relatively few configuration options, most of which can be left at their default values.&lt;/p&gt;

&lt;p&gt;As for the Authorizer, it can have two options for letting API know its decision: &lt;a href="https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-lambda-authorizer.html#http-api-lambda-authorizer.payload-format-response" rel="noopener noreferrer"&gt;simple response and IAM policy&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;simple response&lt;/strong&gt; just returns a Boolean value to indicate whether the API should allow the request (True) or forbid it (False).&lt;/p&gt;

&lt;p&gt;The IAM policy option is customizable and allows crafting custom policy statements that allow granular access to explicitly provided resources.&lt;/p&gt;

&lt;p&gt;In this project, I follow the way of simplicity and use the "simple response", so the response from Lambda Authorizer to HTTP API looks as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"isAuthorized"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's review the HTTP API resource along with the API Authorizer that I used for all routes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_apigatewayv2_api"&lt;/span&gt; &lt;span class="s2"&gt;"this"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
  &lt;span class="nx"&gt;name&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;project_name&lt;/span&gt;  
  &lt;span class="nx"&gt;protocol_type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"HTTP"&lt;/span&gt;  
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_apigatewayv2_authorizer"&lt;/span&gt; &lt;span class="s2"&gt;"header_based_authorizer"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
  &lt;span class="nx"&gt;api_id&lt;/span&gt;                            &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_apigatewayv2_api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;  
  &lt;span class="nx"&gt;authorizer_type&lt;/span&gt;                   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"REQUEST"&lt;/span&gt;  
  &lt;span class="nx"&gt;name&lt;/span&gt;                              &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"header-based-authorizer"&lt;/span&gt;  
  &lt;span class="nx"&gt;authorizer_payload_format_version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"2.0"&lt;/span&gt;  
  &lt;span class="nx"&gt;authorizer_uri&lt;/span&gt;                    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lambda_api_gw_authorizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;invoke_arn&lt;/span&gt;  
  &lt;span class="nx"&gt;enable_simple_responses&lt;/span&gt;           &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;  
  &lt;span class="nx"&gt;identity_sources&lt;/span&gt;                  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="s2"&gt;request.header.authorization"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; 
  &lt;span class="nx"&gt;authorizer_result_ttl_in_seconds&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3600&lt;/span&gt;  
&lt;span class="p"&gt;}&lt;/span&gt;  

&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_lambda_permission"&lt;/span&gt; &lt;span class="s2"&gt;"allow_api_gw_invoke_authorizer"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
  &lt;span class="nx"&gt;statement_id&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"allowInvokeFromAPIGatewayAuthorizer"&lt;/span&gt;  
  &lt;span class="nx"&gt;action&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"lambda:InvokeFunction"&lt;/span&gt;  
  &lt;span class="nx"&gt;function_name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lambda_api_gw_authorizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;function_name&lt;/span&gt;  
  &lt;span class="nx"&gt;principal&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"apigateway.amazonaws.com"&lt;/span&gt;  
  &lt;span class="nx"&gt;source_arn&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;aws_apigatewayv2_api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;execution_arn&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/authorizers/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;aws_apigatewayv2_authorizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;header_based_authorizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;  
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;The complete code is available in the &lt;a href="https://github.com/vasylenko/inkyframe/blob/main/infra/apigateway.tf" rel="noopener noreferrer"&gt;project repository&lt;/a&gt;&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Consider the following key points when Terraforming this part.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;identity_sources&lt;/code&gt; argument of the &lt;code&gt;aws_apigatewayv2_authorizer&lt;/code&gt; resource: This is where I defined what exactly the Authorizer should validate. I used the header named &lt;code&gt;authorization&lt;/code&gt; so the Authorizer Lambda function would check its value to decide whether to authorize the request.\&lt;br&gt;
💡 &lt;em&gt;Check out other options available to use as the identity source — &lt;a href="https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-lambda-authorizer.html#http-api-lambda-authorizer.identity-sources" rel="noopener noreferrer"&gt;Identity sources&lt;/a&gt;&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;authorizer_uri&lt;/code&gt; argument of the &lt;code&gt;aws_apigatewayv2_authorizer&lt;/code&gt; resource: It is the &lt;strong&gt;invocation&lt;/strong&gt; ARN of the Lambda function used as Authorizer (not the Lambda's resource ARN).&lt;/p&gt;

&lt;p&gt;&lt;code&gt;authorizer_result_ttl_in_seconds&lt;/code&gt; argument of the &lt;code&gt;aws_apigatewayv2_authorizer&lt;/code&gt; resource: This allows to skip the Authorizer invocation for the given time if a client provided the same identity source values (e.g., authorization header).&lt;/p&gt;

&lt;p&gt;AWS API Gateway HTTP can employ the identity sources as the cache key to preserve the authorization results for a while. Should a client provide identical parameters in identity sources within the preset TTL duration, API Gateway will retrieve the result from the cached authorizer instead of calling upon it again. This helps save a lot on AWS Lambda Authorizer invocations and works great with simple scenarios. However, it might be cumbersome if you need severral custom authorization responses per function or if you use custom IAM policies instead of the "simple response" option.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;source_arn&lt;/code&gt; argument of &lt;code&gt;aws_lambda_permission&lt;/code&gt;: Similar to the &lt;code&gt;authorizer_uri&lt;/code&gt; argument, this one expects the &lt;strong&gt;execution&lt;/strong&gt; ARN of the HTTP API followed by the Authorizer identifier.&lt;/p&gt;

&lt;p&gt;Now, let's see how Routes are codified with Terraform.&lt;/p&gt;
&lt;h3&gt;
  
  
  Applying Terraform for HTTP API Routes
&lt;/h3&gt;

&lt;p&gt;💡 Because an API typically has multiple routes, creating another Terraform module that implements the configurable HTTP API Gateway route is beneficial. Hence, the &lt;code&gt;aws_apigatewayv2_route&lt;/code&gt;, &lt;code&gt;aws_apigatewayv2_integration&lt;/code&gt;, and &lt;code&gt;aws_lambda_permission&lt;/code&gt; resources would constitute such a module.&lt;/p&gt;

&lt;p&gt;This Terraform module implements a specific use case for HTTP API Gateway. However, if you're seeking for a generic all-in-one module for API Gateway, I recommend checking out this one — &lt;a href="https://registry.terraform.io/modules/terraform-aws-modules/apigateway-v2/aws/latest" rel="noopener noreferrer"&gt;Terraform AWS API Gateway Module&lt;/a&gt; by Anton Babenko.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_apigatewayv2_route"&lt;/span&gt; &lt;span class="s2"&gt;"this"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
  &lt;span class="nx"&gt;api_id&lt;/span&gt;             &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;api_id&lt;/span&gt;  
  &lt;span class="nx"&gt;route_key&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;route_key&lt;/span&gt;  
  &lt;span class="nx"&gt;authorization_type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"CUSTOM"&lt;/span&gt;  
  &lt;span class="nx"&gt;authorizer_id&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;authorizer_id&lt;/span&gt;  
  &lt;span class="nx"&gt;target&lt;/span&gt;             &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"integrations/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;aws_apigatewayv2_integration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;  
&lt;span class="p"&gt;}&lt;/span&gt;  

&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_apigatewayv2_integration"&lt;/span&gt; &lt;span class="s2"&gt;"this"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
  &lt;span class="nx"&gt;api_id&lt;/span&gt;                 &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;api_id&lt;/span&gt;  
  &lt;span class="nx"&gt;integration_type&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"AWS_PROXY"&lt;/span&gt;  
  &lt;span class="nx"&gt;connection_type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"INTERNET"&lt;/span&gt;  
  &lt;span class="nx"&gt;integration_uri&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lambda_invocation_arn&lt;/span&gt;  
  &lt;span class="nx"&gt;payload_format_version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"2.0"&lt;/span&gt;  
&lt;span class="p"&gt;}&lt;/span&gt;  

&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_lambda_permission"&lt;/span&gt; &lt;span class="s2"&gt;"this"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
  &lt;span class="nx"&gt;statement_id&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"allowInvokeFromAPIGatewayRoute"&lt;/span&gt;  
  &lt;span class="nx"&gt;action&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"lambda:InvokeFunction"&lt;/span&gt;  
  &lt;span class="nx"&gt;function_name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lambda_function_name&lt;/span&gt;  
  &lt;span class="nx"&gt;principal&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"apigateway.amazonaws.com"&lt;/span&gt;  
  &lt;span class="nx"&gt;source_arn&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;api_gw_execution_arn&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/*/*/*/*"&lt;/span&gt;  
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First, I want to highlight several key aspects for understanding the resources' arguments within that module.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;target&lt;/code&gt; argument of the &lt;code&gt;aws_apigatewayv2_route&lt;/code&gt; resource implies that the integration ID should be prefixed with the "&lt;code&gt;integrations/&lt;/code&gt;" keyword.&lt;/p&gt;

&lt;p&gt;While the &lt;code&gt;connection_type&lt;/code&gt; argument of the &lt;code&gt;aws_apigatewayv2_integration&lt;/code&gt; resource specifies "INTERNET", it does not mean that the Lambda function must have the publicly available URL. This value must be used unless you work with a VPC endpoint for API Gateway for internal usage.&lt;/p&gt;

&lt;p&gt;For the &lt;code&gt;source_arn&lt;/code&gt; argument in the &lt;code&gt;aws_lambda_permission&lt;/code&gt; resource, similar to earlier, it requires the &lt;strong&gt;execution&lt;/strong&gt; ARN of the API. However, this time, it is the integration of the HTTP API Route with Lambda. And the ARN format of this one is different and a bit tricky:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;arn:partition:execute-api:region:account-id:api-id/stage/http-method/resource-path&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;arn:partition:execute-api:region:account-id:api-id&lt;/code&gt; part constitutes the execution ARN of the HTTP API itself, so for the sake of simplicity, I decided to go with wildcards after it.\&lt;br&gt;
&lt;em&gt;For your convenience, here is the &lt;a href="https://docs.aws.amazon.com/apigateway/latest/developerguide/arn-format-reference.html" rel="noopener noreferrer"&gt;detailed specification&lt;/a&gt; of API Gateway ARNs.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The HTTP API Route module expects several input variables:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;authorizer_id&lt;/code&gt; — the identifier of the Authorizer to use on this route;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;route_key&lt;/code&gt; — the route key for the route, e.g., &lt;code&gt;GET /foo/bar&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;api_id&lt;/code&gt; — the identifier of HTTP API created earlier;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;lambda_invocation_arn&lt;/code&gt; — the Invocation ARN of the Lambda function;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;lambda_function_name&lt;/code&gt; — the name of the Lambda function to integrate with the route;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;api_gw_execution_arn&lt;/code&gt; — the Execution ARN of the HTTP API that invokes a Lambda function.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's take a closer look on API Gateway V2 HTTP API route.&lt;/p&gt;

&lt;p&gt;A route consists of an HTTP method and a resource path with an optional variable. Based on the pre-defined convention, it uses a simplified routing configuration and methods request model (comparable to other APIs).&lt;/p&gt;

&lt;p&gt;While I was working with the HTTP API, I found this simplified approach to be great because it allows easy access to the request context from AWS Lambda functions, for example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A path variable in a route, e.g., &lt;code&gt;GET /calendars/{calendar-name}&lt;/code&gt;, would be available for the integrated AWS Lambda by its name inside the pathParameter JSON field, e.g.,  &lt;code&gt;pathParamters.calendar-name&lt;/code&gt;, of the event object sent by API to Lambda. In other words, you do not need to explicitly set the mapping between the path variable and its representation to the back-end.&lt;/li&gt;
&lt;li&gt;A request query string is parsed into separate parameter-value pairs and available in the &lt;code&gt;queryStringParameters&lt;/code&gt; field of the event object sent by API to Lambda. Again, without the explicit mapping configuration.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here, you can read more about the Route specification of HTTP API and how to transform requests and responses from the API side if you need to adjust something:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop-routes.html" rel="noopener noreferrer"&gt;Working with routes for HTTP APIs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-parameter-mapping.html" rel="noopener noreferrer"&gt;Transforming API requests and responses&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now back to Terraform. Below is the code snippet that illustrates the call of the API Route Terraform module:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="s2"&gt;"route_calendars"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
  &lt;span class="nx"&gt;source&lt;/span&gt;                &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"./modules/api-gateway-route"&lt;/span&gt;  
  &lt;span class="nx"&gt;api_id&lt;/span&gt;                &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_apigatewayv2_api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;  
  &lt;span class="nx"&gt;route_key&lt;/span&gt;             &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"GET /calendars/{calendar-name}"&lt;/span&gt;  
  &lt;span class="nx"&gt;api_gw_execution_arn&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_apigatewayv2_api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;execution_arn&lt;/span&gt;  
  &lt;span class="nx"&gt;lambda_invocation_arn&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lambda_calendar_backend&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;invoke_arn&lt;/span&gt;  
  &lt;span class="nx"&gt;lambda_function_name&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lambda_calendar_backend&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;function_name&lt;/span&gt;  
  &lt;span class="nx"&gt;authorizer_id&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_apigatewayv2_authorizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;header_based_authorizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;  
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This module logically relies on both the HTTP API and Lambda resources to configure their integration by implementing the Route.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enhancing Security and Monitoring of AWS API Gateway V2 HTTP API
&lt;/h2&gt;

&lt;p&gt;Several additional options are available to monitor and protect the HTTP API: logs, metrics, and throttling.&lt;/p&gt;

&lt;h3&gt;
  
  
  Overview of HTTP API monitoring and protection options
&lt;/h3&gt;

&lt;p&gt;Logging, metrics, and throttling are configured on the Stage level but allow configuration granularity for the Routes.&lt;/p&gt;

&lt;p&gt;For logs, you can configure the CloudWatch log group, the log format (JSON, CLF, XML, CSV), and content filters. The &lt;a href="https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-logging-variables.html" rel="noopener noreferrer"&gt;logging variables&lt;/a&gt; allow you to customize the information that appears in logs. I will provide an example of such a configuration later in the article.&lt;/p&gt;

&lt;p&gt;By default, API Gateway sends only API and stage-level metrics to CloudWatch in one-minute periods. However, you can enable &lt;a href="https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-metrics.html" rel="noopener noreferrer"&gt;detailed metrics&lt;/a&gt; and additionally collect the per-route metrics.&lt;/p&gt;

&lt;p&gt;To safeguard your HTTP API from excessive requests, you can employ throttling settings, which allow you to set limits per individual route as well as for all routes collectively.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configuring monitoring and protection for HTTP API with Terraform
&lt;/h3&gt;

&lt;p&gt;Now, let's see how Terraform helps configure the protection and monitoring for HTTP API.&lt;/p&gt;

&lt;p&gt;As mentioned earlier, API Gateway applies these configurations at the Stage level, which is why the aws_apigatewayv2_stage resource encapsulates them all.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_apigatewayv2_stage"&lt;/span&gt; &lt;span class="s2"&gt;"default"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
  &lt;span class="nx"&gt;api_id&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_apigatewayv2_api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;  
  &lt;span class="nx"&gt;name&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="s2"&gt;default"&lt;/span&gt;  
  &lt;span class="nx"&gt;auto_deploy&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;  
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Default stage (i.e., Production mode)"&lt;/span&gt;  
  &lt;span class="nx"&gt;default_route_settings&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
    &lt;span class="nx"&gt;throttling_burst_limit&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;  
    &lt;span class="nx"&gt;throttling_rate_limit&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;  
  &lt;span class="p"&gt;}&lt;/span&gt;  
  &lt;span class="nx"&gt;access_log_settings&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
    &lt;span class="nx"&gt;destination_arn&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_cloudwatch_log_group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;api_gateway_logs_inkyframe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arn&lt;/span&gt;  
    &lt;span class="nx"&gt;format&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jsonencode&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;  
      &lt;span class="nx"&gt;authorizerError&lt;/span&gt;           &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="s2"&gt;context.authorizer.error"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
      &lt;span class="nx"&gt;identitySourceIP&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="s2"&gt;context.identity.sourceIp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
      &lt;span class="nx"&gt;integrationError&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="s2"&gt;context.integration.error"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
      &lt;span class="nx"&gt;integrationErrorMessage&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="s2"&gt;context.integration.errorMessage"&lt;/span&gt;  
      &lt;span class="nx"&gt;integrationLatency&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="s2"&gt;context.integration.latency"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
      &lt;span class="nx"&gt;integrationRequestId&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="s2"&gt;context.integration.requestId"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
      &lt;span class="nx"&gt;integrationStatus&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="s2"&gt;context.integration.integrationStatus"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
      &lt;span class="nx"&gt;integrationStatusCode&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="s2"&gt;context.integration.status"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
      &lt;span class="nx"&gt;requestErrorMessage&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="s2"&gt;context.error.message"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
      &lt;span class="nx"&gt;requestErrorMessageString&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="s2"&gt;context.error.messageString"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
      &lt;span class="nx"&gt;requestId&lt;/span&gt;                 &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="s2"&gt;context.requestId"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
      &lt;span class="nx"&gt;routeKey&lt;/span&gt;                  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="s2"&gt;context.routeKey"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
    &lt;span class="p"&gt;})&lt;/span&gt;   
  &lt;span class="p"&gt;}&lt;/span&gt;  
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, I applied the default &lt;a href="https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-throttling.html" rel="noopener noreferrer"&gt;throttling settings&lt;/a&gt;: for my project, 1 request per second was enough at that point.&lt;/p&gt;

&lt;p&gt;🤔 There is a nuance, though, that makes Terraforming API Gateway a little inconvenient — the IAM role that allows API to write logs must be defined on a region level. Therefore, if you maintain several Terraform projects for the same AWS account, you might need to have the following configuration stand separately to avoid conflicts or misunderstandings:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_api_gateway_account"&lt;/span&gt; &lt;span class="s2"&gt;"this"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
  &lt;span class="nx"&gt;cloudwatch_role_arn&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_iam_role&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;api_gateway_cloudwatch_logs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arn&lt;/span&gt;  
&lt;span class="p"&gt;}&lt;/span&gt;  

&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_iam_role"&lt;/span&gt; &lt;span class="s2"&gt;"api_gateway_cloudwatch_logs"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
  &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"api-gateway-cloudwatch-logs"&lt;/span&gt;  
  &lt;span class="nx"&gt;assume_role_policy&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jsonencode&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;  
    &lt;span class="nx"&gt;Version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"2012-10-17"&lt;/span&gt;  
    &lt;span class="nx"&gt;Statement&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;  
      &lt;span class="p"&gt;{&lt;/span&gt;  
        &lt;span class="nx"&gt;Effect&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Allow"&lt;/span&gt;  
        &lt;span class="nx"&gt;Principal&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
          &lt;span class="nx"&gt;Service&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"apigateway.amazonaws.com"&lt;/span&gt;  
        &lt;span class="p"&gt;}&lt;/span&gt;  
        &lt;span class="nx"&gt;Action&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"sts:AssumeRole"&lt;/span&gt;  
      &lt;span class="p"&gt;}&lt;/span&gt;  
    &lt;span class="p"&gt;]&lt;/span&gt;  
  &lt;span class="p"&gt;})&lt;/span&gt;  
  &lt;span class="nx"&gt;managed_policy_arns&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:iam::aws:policy/service-role/AmazonAPIGatewayPushToCloudWatchLogs"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;  
&lt;span class="p"&gt;}&lt;/span&gt;  

&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_cloudwatch_log_group"&lt;/span&gt; &lt;span class="s2"&gt;"api_gateway_logs_inkyframe"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
  &lt;span class="nx"&gt;name&lt;/span&gt;              &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"/aws/apigateway/inkyframe"&lt;/span&gt;  
  &lt;span class="nx"&gt;log_group_class&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"STANDARD"&lt;/span&gt;  
  &lt;span class="nx"&gt;retention_in_days&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;  
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And one more thing about HTTP API deployments and stages. I use the special &lt;code&gt;$default&lt;/code&gt; keyword to have a single stage (hence, the default one), and I also used automatic deployments: with any change made to API configuration, AWS will automatically generate a new Deployment and bound it with the Stage. If you prefer controlling deployments manually, there is a special resource exists that implements this — &lt;code&gt;aws_apigatewayv2_deployment&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_apigatewayv2_deployment"&lt;/span&gt; &lt;span class="s2"&gt;"example"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;api_id&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_apigatewayv2_api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Example deployment"&lt;/span&gt;

  &lt;span class="nx"&gt;triggers&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;redeployment&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sha1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;","&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;tolist&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
      &lt;span class="nx"&gt;jsonencode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;aws_apigatewayv2_integration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;example&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="nx"&gt;jsonencode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;aws_apigatewayv2_route&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;example&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;])))&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;lifecycle&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;create_before_destroy&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In that case, the &lt;code&gt;aws_apigatewayv2_stage&lt;/code&gt; resource requires the &lt;code&gt;deployment_id&lt;/code&gt; argument to link itself with a particular Deployment and, therefore, represent the state of the API configuration.&lt;/p&gt;

&lt;p&gt;Also, API Gateway requires at least one configured API Route before the deployment is initiated/created. However, these resources do not explicitly depend on each other via attribute references. To avoid the race condition in Terraform, you need to reference the Route resource in the &lt;code&gt;aws_apigatewayv2_deployment&lt;/code&gt; resource via the &lt;code&gt;triggers&lt;/code&gt; argument (as shown above) or via the &lt;code&gt;depends_on&lt;/code&gt; meta-argument. Otherwise, Terraform will try to apply changes to both resources simultaneously.&lt;/p&gt;

&lt;h2&gt;
  
  
  Afterword: Simplifying Serverless Architectures
&lt;/h2&gt;

&lt;p&gt;In wrapping up our exploration of AWS HTTP API Gateway, AWS Lambda, and Terraform, we've delved into how these powerful tools work in tandem to streamline and enhance serverless architectures. This article aimed to combine my experience with new knowledge and demystify the complexities of used services, showcasing their capabilities in creating efficient, cost-effective solutions for modern cloud-based applications.&lt;/p&gt;

&lt;p&gt;We focused on practical implementation and the tangible benefits of combining these technologies. By leveraging Terraform, we've seen how infrastructure management can be simplified, allowing for clearer, more maintainable code. The combination of AWS Lambda and HTTP API Gateway has demonstrated the efficiency of serverless computing, offering scalability and performance without the burden of extensive configuration and management.&lt;/p&gt;

&lt;p&gt;This exploration underlines the importance of choosing the right tools and strategies in cloud computing. It reminds developers and architects that creating robust and efficient serverless systems is within reach with a thoughtful approach and the right set of tools. As the cloud landscape continues to evolve, staying informed and adaptable is key to harnessing the full potential of these technologies. 💚&lt;/p&gt;

</description>
      <category>aws</category>
      <category>terraform</category>
      <category>devops</category>
      <category>serverless</category>
    </item>
    <item>
      <title>Hello terraform_data! Goodbye Null Resource!</title>
      <dc:creator>Serhii Vasylenko</dc:creator>
      <pubDate>Wed, 19 Apr 2023 00:17:47 +0000</pubDate>
      <link>https://dev.to/aws-builders/hello-terraformdata-goodbye-nullresource-3g21</link>
      <guid>https://dev.to/aws-builders/hello-terraformdata-goodbye-nullresource-3g21</guid>
      <description>&lt;p&gt;&lt;strong&gt;Terraform&lt;/strong&gt; version &lt;strong&gt;1.4&lt;/strong&gt; was recently released and brought a range of new features, including improved run output in Terraform Cloud, the ability to use OPA policy results in the CLI, and a built-in alternative to the null resource — terraform_data.&lt;/p&gt;

&lt;p&gt;In this blog post, I want to demonstrate and explain the &lt;strong&gt;terraform_data&lt;/strong&gt; resource that serves two purposes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;firstly, it allows arbitrary values to be stored and used afterward to implement lifecycle triggers of other resources&lt;/li&gt;
&lt;li&gt;secondly, it can be used to trigger provisioners when there isn't a more appropriate managed resource available.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For those of you, who are familiar with the null provider, the &lt;code&gt;terraform_data&lt;/code&gt; resource might look very similar. And you're right!\&lt;br&gt;
Rather than being a separate provider, the terraform_data managed resource now offers the same capabilities as an integrated feature. Pretty cool! \&lt;br&gt;
While the null provider is still available and there are no statements about its deprecation thus far (&lt;a href="https://registry.terraform.io/providers/hashicorp/null/3.2.1/docs" rel="noopener noreferrer"&gt;as of April 2023, v3.2.1&lt;/a&gt;), the  &lt;code&gt;terraform_data&lt;/code&gt; is the native replacement of the &lt;code&gt;null_resource&lt;/code&gt;, and the latter might soon become deprecated.&lt;/p&gt;

&lt;p&gt;The  &lt;code&gt;terraform_data&lt;/code&gt; resource has two optional arguments, &lt;strong&gt;input&lt;/strong&gt; and &lt;strong&gt;triggers_replace&lt;/strong&gt;, and its configuration looks as follows:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fusn4xbmsqcxlfg48aw18.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fusn4xbmsqcxlfg48aw18.png" alt="terraform data resource arguments"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;input&lt;/code&gt; (optional) stores the value that is passed to the resource&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;triggers_replace&lt;/code&gt; (optional) is a value that triggers resource replacement when changes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are two attributes, in addition to the arguments, which are stored in the state: &lt;strong&gt;id&lt;/strong&gt; and &lt;strong&gt;output&lt;/strong&gt; after the resource is created. Let's take a look:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fafhv1g1aa9zd61kpnn4d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fafhv1g1aa9zd61kpnn4d.png" alt="terraform data resource attributes"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The  &lt;code&gt;output&lt;/code&gt; attribute is computed based on the value of the &lt;code&gt;input&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;id&lt;/code&gt; is just a unique value of the resource instance in the state (as for any other resource).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Use case for terraform_data with replace_triggered_by
&lt;/h2&gt;

&lt;p&gt;Let's take a look at the first use case for the terraform_data resource. It is the ability to trigger resource replacement based on the value of the input argument.&lt;/p&gt;

&lt;p&gt;A bit of context here: the &lt;strong&gt;replace_triggered_by&lt;/strong&gt; argument of the resource lifecycle meta-argument allows you to trigger resource replacement based on another referenced resource or its attribute. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you are not yet familiar with the &lt;code&gt;replace_triggered_by&lt;/code&gt;, you can check &lt;a href="https://devdosvid.blog/2022/05/04/new-lifecycle-options-and-refactoring-capabilities-in-terraform-1-1-and-1-2/#trigger-resource-replacement-with-replace_triggered_by" rel="noopener noreferrer"&gt;another blog post that explains it&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The replace_triggered_by is a powerful feature, but here is the thing about it: only a resource or its attribute must be specified, and &lt;strong&gt;you cannot use a variable or a local value for replace_triggered_by&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;But with terraform_data, you can indirectly initiate another resource replacement by using either a variable or an expression within a local value for the &lt;code&gt;input&lt;/code&gt; argument.&lt;/p&gt;

&lt;p&gt;Let me give you an example here. Consider the following code:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyeeu4vzq6komopc4x75b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyeeu4vzq6komopc4x75b.png" alt="trigger replacement based on input variable"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By modifying the  &lt;code&gt;revision&lt;/code&gt; variable, the next Terraform plan will suggest a replacement action against aws_instance.webserver:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F132ldl5cmplem7vf5lcu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F132ldl5cmplem7vf5lcu.png" alt="terraform_data with replace_triggered_by"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Use case for terraform_data with provisioner
&lt;/h2&gt;

&lt;p&gt;Before we start: HashiCorp suggests (and I also support that) avoiding provisioner usage unless you have no other options left. One of the reasons — additional, implicit, and unobvious dependency that appears in the codebase — the binary, which is called inside the provisioner block, must be present on the machine. \&lt;br&gt;
But let's be real, the provisioner feature is still kicking, and there's always that one unique project that needs it.&lt;/p&gt;

&lt;p&gt;Here is the code snippet that demonstrates the usage of the provisioner within the terraform_data resource: &lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2p1gpw1utib5bhq2b3p7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2p1gpw1utib5bhq2b3p7.png" alt="terraform_data with provisioner"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this example, the following happens:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When resources are created the first time, the provisioner inside &lt;code&gt;terraform_data&lt;/code&gt; runs&lt;/li&gt;
&lt;li&gt;Sequential plan/apply will trigger another execution of the provisioner only when the private IP of the instance (aws_instance.webserver.private_ip) changes because that will trigger &lt;code&gt;terraform_data&lt;/code&gt; recreation. At the same time, no changes to the internal IP mean no provisioner execution.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;With its ability to store and use values for lifecycle triggers and provisioners, &lt;strong&gt;terraform_data&lt;/strong&gt; is a powerful tool that can enhance your Terraform configuration. &lt;/p&gt;

&lt;p&gt;Although the null provider still has its place in the Terraform ecosystem, terraform_data is its evolution, and its integration as a feature is certainly something to be excited about. &lt;/p&gt;

&lt;p&gt;Why not give it a try in your next project and see how it can simplify your infrastructure as code workflows? Keep on coding! 🙌&lt;/p&gt;

</description>
      <category>terraform</category>
      <category>devops</category>
      <category>news</category>
      <category>programming</category>
    </item>
    <item>
      <title>Amazon VPC Lattice — Build Applications, Not Networks</title>
      <dc:creator>Serhii Vasylenko</dc:creator>
      <pubDate>Fri, 17 Mar 2023 23:33:13 +0000</pubDate>
      <link>https://dev.to/aws-builders/amazon-vpc-lattice-build-applications-not-networks-59j8</link>
      <guid>https://dev.to/aws-builders/amazon-vpc-lattice-build-applications-not-networks-59j8</guid>
      <description>&lt;p&gt;Last year's re:Invent brought a lot of amazing updates to the big family of AWS services. In this blog post, I would like to explain one of such new offerings — &lt;strong&gt;Amazon VPC Lattice&lt;/strong&gt; — an exciting new service that simplifies the networking layer for developers and cloud administrators.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Lattice
&lt;/h2&gt;

&lt;p&gt;So what exactly is Amazon VPC Lattice? It is an application layer networking service that enables consistent and secure service-to-service communication without the need for prior networking expertise. With VPC Lattice, you can easily configure network access, traffic management, and network monitoring, making service-to-service communication seamless across VPCs and accounts, irrespective of the underlying compute type.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it helps
&lt;/h2&gt;

&lt;p&gt;VPC Lattice helps address several use cases, including connecting services at scale, implementing granular access permissions, advanced traffic controls, and observing service-to-service interactions. The service offers connectivity over HTTP/HTTPS and gRPC protocols through a dedicated data plane within VPC. Administrators can use AWS Resource Access Manager (AWS RAM) to control which accounts and VPCs can establish communication through a service network.&lt;/p&gt;

&lt;p&gt;What's more, VPC Lattice is designed to be non-invasive and work alongside existing architecture patterns, allowing development teams across your organization to onboard their services incrementally.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it works
&lt;/h2&gt;

&lt;p&gt;VPC Lattice introduces four key components: Service, Service Directory, Service Network, and Auth Policy. These components simplify how users enable connectivity and apply standard policies to a collection of services. Service networks can be shared across accounts with AWS RAM and associated with VPCs to allow connectivity to a group of services.&lt;/p&gt;

&lt;p&gt;Here is the diagram that illustrates the use of Amazon VPC Lattice and the Service Network Manager to create a service network, define policies, and share cross-account access. &lt;/p&gt;

&lt;p&gt;The Service Network Manager subset at the top consists of four icons representing the process flow:&lt;/p&gt;

&lt;p&gt;1️⃣ The first step involves creating a service network by choosing a name and authentication type. &lt;/p&gt;

&lt;p&gt;2️⃣ The second step consists in defining access and monitoring by setting and managing access policies and selecting log destinations. &lt;/p&gt;

&lt;p&gt;3️⃣ The third step involves associating clients and services, allowing resources in associated VPCs to access the benefits associated with the service network. &lt;/p&gt;

&lt;p&gt;4️⃣The fourth step consists in adding specific assistance or service networks to AWS RAM shares to facilitate cross-account access.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0o9ftrwy93t34xwag4wx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0o9ftrwy93t34xwag4wx.png" alt="Lattice diagram"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Service Owner subset at the bottom consists of three steps:&lt;/p&gt;

&lt;p&gt;1️⃣ The first step involves creating a service by identifying the benefit and defining access and monitoring. &lt;/p&gt;

&lt;p&gt;2️⃣ The second step consists in defining routing by adding listeners and rules that point to the target groups that store the service. &lt;/p&gt;

&lt;p&gt;3️⃣ The third step consists in selecting the networks from the service that receives traffic.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pricing
&lt;/h2&gt;

&lt;p&gt;There are three factors that compose the price of VPC Lattice:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;number of provisioned services&lt;/li&gt;
&lt;li&gt;traffic volume to and from each service&lt;/li&gt;
&lt;li&gt;number of requests each service receives&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can see the detailed and actual price list on the services' pricing page — &lt;a href="https://aws.amazon.com/vpc/lattice/pricing/" rel="noopener noreferrer"&gt;&lt;strong&gt;link&lt;/strong&gt;&lt;/a&gt;, but here is an example: &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You provision a service network in the US East (N. Virginia) Region and associate &lt;strong&gt;100 services&lt;/strong&gt; to it. In a month, &lt;strong&gt;each service processes 100 GB of data at 200,000 requests per hour&lt;/strong&gt;. In this example, we calculate your charges as follows (all prices shown in USD):&lt;/p&gt;

&lt;p&gt;Monthly hourly charges:&lt;br&gt;
You pay &lt;strong&gt;$0.025 per hour for each service&lt;/strong&gt; in US East (N. Virginia)&lt;br&gt;
We assume that a month equals 730 hours (8,760 hours in a year/12 months = 730 hours per month)&lt;br&gt;
100 services * $0.025 per hour * 730 hours = &lt;strong&gt;$1,825.00 per month&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Win-win for Ops and Developers
&lt;/h2&gt;

&lt;p&gt;Overall, VPC Lattice bridges the gap between developers and cloud administrators by providing role-specific features and capabilities. Developers can focus on building applications, not networks, while cloud and network administrators can increase their organization's security posture by enabling authentication, authorization, and encryption consistently across mixed computing environments.&lt;/p&gt;

&lt;p&gt;Currently, Amazon VPC Lattice is in Preview in the US West (Oregon) region. I'm excited to see how VPC Lattice will shape the future of networking and make it even easier for developers to build complex applications. 🚀&lt;/p&gt;

&lt;p&gt;Some additional resources to learn more about Lattice:&lt;/p&gt;

&lt;p&gt;Presentation at re:Invent 2022 &lt;br&gt;
&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/fRjD1JI0H5w"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;A blog post at AWS with examples &lt;a href="https://aws.amazon.com/blogs/aws/introducing-vpc-lattice-simplify-networking-for-service-to-service-communication-preview/" rel="noopener noreferrer"&gt;Introducing VPC Lattice – Simplify Networking for Service-to-Service Communication&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>devrel</category>
      <category>cloud</category>
      <category>news</category>
    </item>
    <item>
      <title>Five Practical Ways To Get The Verified EC2 AMI</title>
      <dc:creator>Serhii Vasylenko</dc:creator>
      <pubDate>Tue, 26 Jul 2022 15:53:00 +0000</pubDate>
      <link>https://dev.to/aws-builders/five-practical-ways-to-get-the-verified-ec2-ami-4im7</link>
      <guid>https://dev.to/aws-builders/five-practical-ways-to-get-the-verified-ec2-ami-4im7</guid>
      <description>&lt;p&gt;EC2 AMI catalog consists of more than 160k public AMIs — a mix of Images created by users, published by vendors, and provided by AWS.&lt;/p&gt;

&lt;p&gt;So how to ensure that an AMI comes from the verified vendor or that is an official AMI published by AWS?&lt;/p&gt;

&lt;p&gt;How to find the trusted AMI among them all when you’re about to launch an EC2 Instance?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnoarulewa2u1ouokw31z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnoarulewa2u1ouokw31z.png" alt="Find the ~~Waldo~~ verified AMI owner"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On AWS, it’s typical that something can be made or done in several ways — that’s awesome. Some of them work better than others, some methods are official, and some you can use just for fun (&lt;a href="https://www.lastweekinaws.com/blog/the-17-ways-to-run-containers-on-aws/" rel="noopener noreferrer"&gt;check&lt;/a&gt; &lt;a href="https://www.lastweekinaws.com/blog/17-more-ways-to-run-containers-on-aws/" rel="noopener noreferrer"&gt;that&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;In this article, I will describe five ways of getting the official and verified AMI for your next EC2 Instance launch.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use EC2 Launch Wizard and AMI Catalog to get the official AMI
&lt;/h2&gt;

&lt;p&gt;When launching an EC2 Instance from a Management Console, you can apply the “Verified Provider” filter for the Community AMIs tab to ensure you get an AMI from a verified provider. &lt;/p&gt;

&lt;p&gt;The “Verified provider” label means an AMI is owned by an Amazon verified account.&lt;/p&gt;

&lt;p&gt;In the following example, I want to make sure that the Ubuntu 20.04 AMI comes from the verified source:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcg9z5xv41gpdqd5q1coh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcg9z5xv41gpdqd5q1coh.png" alt="Verified AMI in the AMI Catalog"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the past, you had to compare the AMI Owner ID with the publicly shared list of verified Owner IDs for every region. &lt;/p&gt;

&lt;p&gt;Not rocket science, but it takes time. So now it’s much more straightforward, thanks to the “Verified Provider” label.&lt;/p&gt;

&lt;p&gt;This feature also works great when you are creating a Launch Template. For example, if you want to create a fleet of &lt;a href="https://devdosvid.blog/2021/10/24/auto-scaling-group-for-your-macos-ec2-instances-fleet/" rel="noopener noreferrer"&gt;macOS Instances with AutoScaling&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;The Launch Template creation wizard seamlessly guides you from itself to the AMI Catalog (where you can search and pick the AMI) and back again.&lt;/p&gt;

&lt;h2&gt;
  
  
  Look for verified AMIs on the AMI page
&lt;/h2&gt;

&lt;p&gt;Another interface in the Management Console acts as the AMI browser. It does not have any fancy name except for the “AMIs page”, but you probably already know about it: it looks like a list of AMIs, and you can see it when you click on the “AMIs” menu item on the left side of the EC2 page menu.&lt;/p&gt;

&lt;p&gt;The AMI page allows you to leverage the API filters to narrow down the search, and the “Owner alias” filter is the one you need to ensure that an AMI comes from a trusted owner.&lt;/p&gt;

&lt;p&gt;Here is how it looks for my search of the official Amazon Linux 2 AMI:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyyceeqiix2hyebsrazap.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyyceeqiix2hyebsrazap.png" alt="Official Amazon Linux 2 AMI"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;AMIs shared by verified sources have &lt;code&gt;amazon&lt;/code&gt; (for AWS) or &lt;code&gt;aws-marketplace&lt;/code&gt; (for AWS partners) as the value for the Owner alias filter.&lt;/p&gt;

&lt;h2&gt;
  
  
  Find the EC2 AMI with Terraform
&lt;/h2&gt;

&lt;p&gt;Finding the official AMI with Terraform is also simple — the &lt;a href="https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/ami" rel="noopener noreferrer"&gt;aws_ami data source&lt;/a&gt; does the job.&lt;/p&gt;

&lt;p&gt;For example, here is how you can find the same Amazon Linux 2 AMI by specifying the &lt;code&gt;amazon&lt;/code&gt; as the value for the &lt;code&gt;owner&lt;/code&gt; argument of the data source:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3fqr7donl6pmgfz4w3zg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3fqr7donl6pmgfz4w3zg.png" alt="Finding the official Amazon Linux 2 AMI with Terraform"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Compare that with the filters on the AMI page — it looks similar, right? This is because of how Terraform works: it translates your code into API calls and sends them to AWS API endpoints.&lt;/p&gt;

&lt;p&gt;If you’re very new to Terraform, I suggest reading this article to understand the basic concepts: &lt;a href="https://devdosvid.blog/2020/05/02/terraform-explained-in-english/" rel="noopener noreferrer"&gt;Terraform explained in English&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Find the official AWS AMI using Describe Images CLI
&lt;/h2&gt;

&lt;p&gt;Sometimes you might need to get the AMI from CLI to pass it along as an argument downstream of the pipeline.&lt;/p&gt;

&lt;p&gt;This can be done with the &lt;strong&gt;ec2 describe-images&lt;/strong&gt; command of the AWS CLI&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5cxxcld8wpikp1hshlnx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5cxxcld8wpikp1hshlnx.png" alt="Find the verified AMI with AWS CLI"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The API filters I mentioned before also work here — use them to narrow your search.&lt;/p&gt;

&lt;h2&gt;
  
  
  Find the trusted AWS AMI with SSM
&lt;/h2&gt;

&lt;p&gt;Another way that involves AWS CLI is the &lt;strong&gt;ssm get-parameter&lt;/strong&gt; command:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcwbo42vj41hs90rq2v62.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcwbo42vj41hs90rq2v62.png" alt="Get the latest EKS optimized AMI from SSM"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It reveals one helpful feature of the Systems Manager — the Public parameters.&lt;/p&gt;

&lt;p&gt;Systems Manager Public parameters are how AWS distributes some widely used artifacts related to their services.&lt;/p&gt;

&lt;p&gt;For example, you can find official AMIs for many distributives there: Amazon Linux, Windows, macOS, Bottlerocket, Ubuntu, Debian, and FreeBSD.&lt;/p&gt;

&lt;p&gt;Read more at the &lt;a href="https://docs.aws.amazon.com/systems-manager/latest/userguide/parameter-store-finding-public-parameters.html" rel="noopener noreferrer"&gt;Finding public parameters&lt;/a&gt; documentation page if you want to know more.&lt;/p&gt;

&lt;h2&gt;
  
  
  Are all verified AMIs good?
&lt;/h2&gt;

&lt;p&gt;The “Verified provider” badge can be earned by a third party only when an AMI developer is registered as a Seller on the AWS Marketplace.&lt;/p&gt;

&lt;p&gt;Becoming a Seller there is not trivial and requires some &lt;a href="https://docs.aws.amazon.com/marketplace/latest/userguide/user-guide-for-sellers.html#seller-requirements-for-publishing-free-products" rel="noopener noreferrer"&gt;conditions&lt;/a&gt; to be met, and the &lt;a href="https://docs.aws.amazon.com/marketplace/latest/userguide/seller-registration-process.html" rel="noopener noreferrer"&gt;registration process&lt;/a&gt; itself also implies submitting the tax and banking information.&lt;/p&gt;

&lt;p&gt;Additionally, there are &lt;a href="https://docs.aws.amazon.com/marketplace/latest/userguide/product-and-ami-policies.html" rel="noopener noreferrer"&gt;specific policies and review processes&lt;/a&gt; apply to all AMIs submitted to the Marketplace.&lt;/p&gt;

&lt;p&gt;So it is okay to trust the third-party vendors with the “Verified” badge on a certain level. However, it is also always good to have additional scans and validation of the software you use. 🪲 😉&lt;/p&gt;





&lt;div class="ltag__user ltag__user__id__51518"&gt;
    &lt;a href="/svasylenko" class="ltag__user__link profile-image-link"&gt;
      &lt;div class="ltag__user__pic"&gt;
        &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F51518%2F60259e60-86ea-47fe-92ff-5ac3f00835d2.jpg" alt="svasylenko image"&gt;
      &lt;/div&gt;
    &lt;/a&gt;
  &lt;div class="ltag__user__content"&gt;
    &lt;h2&gt;
&lt;a class="ltag__user__link" href="/svasylenko"&gt;Serhii Vasylenko&lt;/a&gt;Follow
&lt;/h2&gt;
    &lt;div class="ltag__user__summary"&gt;
      &lt;a class="ltag__user__link" href="/svasylenko"&gt;I am an engineer from Ukraine. I like astronomy and everything related to DevOps. I thrive on developing great product offerings, great people, and great teams.&lt;/a&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;


</description>
      <category>devops</category>
      <category>aws</category>
      <category>cloud</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>New Lifecycle Options and Refactoring Capabilities in Terraform 1.1 and 1.2</title>
      <dc:creator>Serhii Vasylenko</dc:creator>
      <pubDate>Thu, 05 May 2022 13:14:48 +0000</pubDate>
      <link>https://dev.to/aws-builders/new-lifecycle-options-and-refactoring-capabilities-in-terraform-11-and-12-j19</link>
      <guid>https://dev.to/aws-builders/new-lifecycle-options-and-refactoring-capabilities-in-terraform-11-and-12-j19</guid>
      <description>&lt;p&gt;In this blog, I would like to tell you about new cool features that Terraform 1.1 and 1.2 bring. It feels like Terraform has doubled its speed of delivering the new features after they released the 1.0. 🤩&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;All code examples are based on the AWS Terraform provider ⛅️💛&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It’s been only a few months since Terraform 1.1 was released with the &lt;code&gt;moved&lt;/code&gt; block that empowers the code refactoring.&lt;/p&gt;

&lt;p&gt;Now Terraform 1.2 is almost &lt;a href="*https://github.com/hashicorp/terraform/releases/tag/v1.2.0-rc1*"&gt;ready&lt;/a&gt; (as I am writing this blog in early May 2022) to bring three new efficient controls to the resource lifecycle.&lt;br&gt;&lt;br&gt;
These are three new expressions: &lt;code&gt;precondition&lt;/code&gt;, &lt;code&gt;postcondition&lt;/code&gt;, and &lt;code&gt;replace_triggered_by&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Terraform Code Refactoring With the Moved Block
&lt;/h2&gt;

&lt;p&gt;Starting from the 1.1 version, Terraform users can use the &lt;code&gt;moved&lt;/code&gt; block to describe the changes in resource or module addresses (or resources inside a module) in the form of code.&lt;br&gt;&lt;br&gt;
Once that is described, Terraform performs the movement of the resource within the state during the first apply.&lt;/p&gt;

&lt;p&gt;In other words, what this feature gives you, is the ability to document your &lt;code&gt;terraform state mv&lt;/code&gt; actions, so you and other project or module users don’t need to perform them manually.&lt;/p&gt;

&lt;p&gt;As your code evolves, a resource or module can have several &lt;code&gt;moved&lt;/code&gt; blocks associated with it, and Terraform will thoroughly reproduce the whole history of its movement within a state (i.e., renaming).&lt;/p&gt;

&lt;p&gt;Let’s review some examples that illustrate how it works.&lt;/p&gt;
&lt;h3&gt;
  
  
  Moving a resource
&lt;/h3&gt;

&lt;p&gt;In a module, I have a bucket policy that has a generic, meaningless name. It is used in a module that creates a CloudFront distribution with an S3 bucket.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--k5bueWVh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/11recbc6esq28xis1gdk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--k5bueWVh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/11recbc6esq28xis1gdk.png" alt="An example resource" width="800" height="150"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It’s pretty OK to name a resource like that if you have only a single instance of that kind in your code.&lt;/p&gt;

&lt;p&gt;Later, when I need to add another policy to the module, I don’t want to name it “that”. Instead, I want my policies to have meaningful names now.&lt;br&gt;&lt;br&gt;
For example, I could rename the old policy with the &lt;code&gt;terraform state mv&lt;/code&gt; command, but other users of my module would not know about that.&lt;/p&gt;

&lt;p&gt;That is where the &lt;code&gt;moved&lt;/code&gt; block turns out to be helpful: I can document the name change, and later, everyone else who uses my module will get the same renaming.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--AL9EoLmb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4c3f9wvdtqwwy0cdcn00.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--AL9EoLmb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4c3f9wvdtqwwy0cdcn00.png" alt="Resource address update with the Moved block" width="800" height="270"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Terraform follows the instructions inside the &lt;code&gt;module&lt;/code&gt; block to plan and apply changes. Although the resource address update is not counted as a change in the Plan output, Terraform will perform that update during apply.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6xeb0pTO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/b38nfp4wp00xqb3ns374.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6xeb0pTO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/b38nfp4wp00xqb3ns374.png" alt="Terraform plan output" width="800" height="318"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Moving a module
&lt;/h3&gt;

&lt;p&gt;The same approach can be applied to a module.&lt;/p&gt;

&lt;p&gt;Here, I use two modules to create static hosting for a website with a custom TLS certificate: a combo of the CloudFront + S3 + ACM services.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--0lsvFYqR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4uz7lbyvp31meqfuaxgq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--0lsvFYqR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4uz7lbyvp31meqfuaxgq.png" alt="Two modules with generic names" width="800" height="437"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Again, if I need to add another couple of the CDN+Certificate modules, I would like to have meaningful names in my code so clearly distinguish one from another.&lt;/p&gt;

&lt;p&gt;Therefore, I would add two &lt;code&gt;moved&lt;/code&gt; blocks — one per module call.&lt;/p&gt;

&lt;p&gt;And by the way, since I renamed the module (from &lt;code&gt;cert&lt;/code&gt; to &lt;code&gt;example_com_cert&lt;/code&gt;), I need to update all references to that module’s outputs in the code too.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5INWfU9R--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5vdxaa1z2g1pio25sr9a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5INWfU9R--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5vdxaa1z2g1pio25sr9a.png" alt="Two modules renamed" width="800" height="629"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;However, there is one nuance: when you rename a module and declare that in the &lt;code&gt;moved&lt;/code&gt; block, you need to run the &lt;code&gt;terraform init&lt;/code&gt; before applying the change because Terraform must initialize the module with the new name first.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--fAh6ISsg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/unp7ccm3nxpjesu1zt6c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--fAh6ISsg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/unp7ccm3nxpjesu1zt6c.png" alt="Terraform error: module not installed" width="800" height="246"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There are some more advanced actions you can make with the &lt;code&gt;moved&lt;/code&gt; block:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Implement count and for_each meta-arguments to resources and modules&lt;/li&gt;
&lt;li&gt;Break one module into multiple Check the following detailed guide from HashiCorp that explains how to do that — &lt;a href="https://www.terraform.io/language/modules/develop/refactoring"&gt;Refactoring&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Introducing the &lt;code&gt;moved&lt;/code&gt; blocks into your codebase defacto starts the refactoring process for your module users. But the finale of that refactoring happens when you ultimately remove these blocks.&lt;/p&gt;

&lt;p&gt;Therefore, here is some advice on how to manage that:&lt;/p&gt;

&lt;p&gt;💡 Keep the &lt;code&gt;moved&lt;/code&gt; blocks in your code for long. For example, when removing a &lt;code&gt;moved&lt;/code&gt; block from the code, Terraform does not treat the new object name as a renaming anymore. Instead, Terraform will plan to delete the resource or module with the old name instead of renaming it.&lt;br&gt;
💡 Keep the complete chains of object renaming (sequence of moves). The whole history of object movement ensures that users with different module versions will get a consistent and predictable behavior of the refactoring.&lt;/p&gt;
&lt;h2&gt;
  
  
  Lifecycle expressions: precondition, postcondition, and replace_triggered_by
&lt;/h2&gt;

&lt;p&gt;Terraform 1.2 fundamentally improves the &lt;code&gt;lifecycle&lt;/code&gt; meta-argument by adding three new configuration options with rich capabilities.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4QvlBEi3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gm4sqdi9d1qecilksdmu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4QvlBEi3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gm4sqdi9d1qecilksdmu.png" alt="New configuration options for the lifecycle meta-argument" width="800" height="365"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Precondition and Postcondition
&lt;/h3&gt;

&lt;p&gt;When you need to make sure that specific condition is met before or after you create a resource, you can use &lt;code&gt;postcondition&lt;/code&gt; and &lt;code&gt;precondition&lt;/code&gt; blocks.&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;condition&lt;/em&gt; here — is some data or information about a resource you need to confirm to apply the code.&lt;/p&gt;

&lt;p&gt;Here are a few examples of such conditions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Validate some attributes of the Data Source that you cannot check using filters or other available arguments;&lt;/li&gt;
&lt;li&gt;Confirm the Resource argument that can compound several variables (e.g., list);&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;💡 &lt;strong&gt;Precondition&lt;/strong&gt; works as an expectation or a guess about some external (but within a module) value that a resource depends on.&lt;br&gt;
💡 &lt;strong&gt;Postcondition&lt;/strong&gt; works as the assurance that a resource fulfils a specific condition so other resources may rely on that. If postcondition fails for a resource, this prevents changes to all other resources that depend on it.&lt;/p&gt;

&lt;p&gt;Let’s review this new feature with an example of &lt;code&gt;postcondition&lt;/code&gt; usage.&lt;/p&gt;

&lt;p&gt;Consider the following case: our module receives AMI ID as the input variable, and that AMI should be used in the Launch Template then; we also have the requirement for the EC2 instance created from that Launch Template — its root EBS size must be equal or bigger than 600 GB.&lt;/p&gt;

&lt;p&gt;We cannot validate the EBS size using the variable that accepts the AMI ID. But we can write a &lt;strong&gt;postcondition&lt;/strong&gt; for the Data Source that gets the information about the AMI and reference that Data Source in the Launch Template resource afterward.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--rldqgdXi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/092czy7aobh7dta6toke.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--rldqgdXi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/092czy7aobh7dta6toke.png" alt="Data Source Postcondition" width="800" height="446"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;condition&lt;/code&gt; argument within the block accepts any of Terraform’s built-in functions or language operators.&lt;/p&gt;

&lt;p&gt;The special &lt;code&gt;self&lt;/code&gt; object is available only for the &lt;code&gt;postcondition&lt;/code&gt; block because it assumes that validation can be performed after the object is created and its attributes are known.&lt;/p&gt;

&lt;p&gt;Later, if a module user specifies the AMI with an EBS size lesser than 600 GB, Terraform will fail to create the Launch Template because it depends on the Data Source that did not pass the postcondition check.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--r1SRc3eO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3bx3a8weghiat9wkz1xa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--r1SRc3eO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3bx3a8weghiat9wkz1xa.png" alt="Resource postcondition error" width="800" height="240"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Terraform tries to evaluate the condition expressions as soonest: sometimes Terraform can check the value during the planning phase, but sometimes that can be done only after the resource is created if the value is unknown.&lt;/p&gt;
&lt;h3&gt;
  
  
  Validating module output with precondition
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;precondition&lt;/code&gt; block is also available for the module outputs.&lt;/p&gt;

&lt;p&gt;Just like the variable validation block assures that module input meets certain expectations, the &lt;code&gt;precondition&lt;/code&gt; is intended to ensure that a module produces the valid output.&lt;/p&gt;

&lt;p&gt;Here is an example: a module that creates an ACM certificate must prevent the usage of a specific domain name in the certificate’s Common Name or its SANs.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--adb-u3zy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xjp0riwkwo5733iopzjn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--adb-u3zy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xjp0riwkwo5733iopzjn.png" alt="Module output precondition" width="800" height="342"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this case, instead of validating several input variables, we can write the validation only once for the output.&lt;/p&gt;
&lt;h3&gt;
  
  
  Trigger resource replacement with replace_triggered_by
&lt;/h3&gt;

&lt;p&gt;Sometimes it’s needed to specify the dependency in the way that recreates a resource when another resource or its attribute changes.&lt;/p&gt;

&lt;p&gt;This is useful when two (or more) resources do not have any explicit dependency.&lt;/p&gt;

&lt;p&gt;Consider the following case: you have two EC2 instances, A and B, and need to recreate the B instance if the private IP of instance A is changed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--RGd1VdD1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/aplcw3dp5c8ahgt9wopn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--RGd1VdD1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/aplcw3dp5c8ahgt9wopn.png" alt="replace_triggered_by example" width="800" height="342"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is extremely useful when you’re dealing with logical abstractions over the set of resources.&lt;/p&gt;

&lt;p&gt;Replacement is triggered when:&lt;/p&gt;

&lt;p&gt;💡 Any of the resources referenced in &lt;code&gt;replace_triggered_by&lt;/code&gt; are updated&lt;br&gt;
💡 Any value is set to the resource attribute that is referenced in &lt;code&gt;replace_triggered_by&lt;/code&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Getting started with Terraform 1.1 and 1.2
&lt;/h2&gt;

&lt;p&gt;If you’re still using older Terraform versions, these new features might be a good motivation for you to upgrade!&lt;/p&gt;

&lt;p&gt;Before upgrading, be sure to read the upgrade notes for the specific version at the &lt;a href="https://github.com/hashicorp/terraform/releases"&gt;releases page&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Also, an excellent tool can help with fast switching between different Terraform versions while you’re experimenting — &lt;a href="https://tfswitch.warrensbox.com/"&gt;tfswitch&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;And if you liked that article, you might also like the others wrote about Terraform: &lt;strong&gt;&lt;a href="https://devdosvid.blog/series/terraform-proficiency/"&gt;Terraform Proficiency&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;




&lt;div class="ltag__user ltag__user__id__51518"&gt;
    &lt;a href="/svasylenko" class="ltag__user__link profile-image-link"&gt;
      &lt;div class="ltag__user__pic"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--OaW58MNK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/practicaldev/image/fetch/s--6678KR6E--/c_fill%2Cf_auto%2Cfl_progressive%2Ch_150%2Cq_auto%2Cw_150/https://dev-to-uploads.s3.amazonaws.com/uploads/user/profile_image/51518/60259e60-86ea-47fe-92ff-5ac3f00835d2.jpg" alt="svasylenko image"&gt;
      &lt;/div&gt;
    &lt;/a&gt;
  &lt;div class="ltag__user__content"&gt;
    &lt;h2&gt;
&lt;a class="ltag__user__link" href="/svasylenko"&gt;Serhii Vasylenko&lt;/a&gt;Follow
&lt;/h2&gt;
    &lt;div class="ltag__user__summary"&gt;
      &lt;a class="ltag__user__link" href="/svasylenko"&gt;I am an engineer from Ukraine. I like astronomy and everything related to DevOps. I thrive on developing great product offerings, great people, and great teams.&lt;/a&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;


</description>
      <category>terraform</category>
      <category>programming</category>
      <category>news</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Some Techniques to Enhance Your Terraform Proficiency</title>
      <dc:creator>Serhii Vasylenko</dc:creator>
      <pubDate>Tue, 18 Jan 2022 09:05:09 +0000</pubDate>
      <link>https://dev.to/aws-builders/some-techniques-to-enhance-your-terraform-proficiency-4n9e</link>
      <guid>https://dev.to/aws-builders/some-techniques-to-enhance-your-terraform-proficiency-4n9e</guid>
      <description>&lt;p&gt;Terraform built-in functionality is very feature-rich: functions, expressions, and meta-arguments provide many ways to shape the code and fit it to a particular use case. &lt;/p&gt;

&lt;p&gt;I want to share a few valuable practices to boost your Terraform expertise in this blog.&lt;/p&gt;

&lt;p&gt;Two notes:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;1️⃣ &lt;em&gt;Some code examples in this article will work with Terraform version 0.15 and onwards. But if you’re still using 0.14 or lower, here’s another motivation for you to upgrade&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;2️⃣ &lt;em&gt;I'll be using AWS Terraform provider code examples throughout the article but mentioned Terraform functions and expressions are provider-agnostic and will work the same with other providers&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Conditional resources creation
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--N1XHXnZ---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/09b7epalarykbci29yqz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--N1XHXnZ---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/09b7epalarykbci29yqz.png" alt="Conditional resources creation" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let’s start from the most popular one (although, still may be new for somebody): whether to create a resource depending on some fact, e.g., the value of a variable. Terraform meta-argument &lt;code&gt;count&lt;/code&gt; helps to describe that kind of logic.&lt;/p&gt;

&lt;p&gt;Here is how it may look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="k"&gt;data&lt;/span&gt; &lt;span class="s2"&gt;"aws_ssm_parameter"&lt;/span&gt; &lt;span class="s2"&gt;"ami_id"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ami_channel&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt; &lt;span class="err"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

  &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ami_channels&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ami_channel&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The notation &lt;code&gt;var.ami_channel == "" ? 0 : 1&lt;/code&gt; is called &lt;em&gt;conditional expression&lt;/em&gt; and means the following: if my variable is empty (&lt;code&gt;var.ami_channel == ""&lt;/code&gt; — hence, true) then set the count to 0, otherwise set to 1.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="nx"&gt;condition&lt;/span&gt; &lt;span class="err"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;true_val&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;false_val&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;In this illustration, I want to get the AMI ID from the SSM Parameter only if the AMI channel (e.g., beta or alpha) is specified. Otherwise, providing that the &lt;code&gt;ami_channel&lt;/code&gt; variable is an empty string by default (""), the data source should not be created.&lt;/p&gt;

&lt;p&gt;When following this method, keep in mind that the resource address will contain the index identifier. So when I need to use the value of the SSM parameter from our example, I need to reference it the following way:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="nx"&gt;ami_id&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="k"&gt;data&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_ssm_parameter&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ami_id&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The &lt;code&gt;count&lt;/code&gt; meta-argument can also be used to create a whole module conditionally:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="s2"&gt;"bucket"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;create_bucket&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="err"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
  &lt;span class="nx"&gt;source&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"./modules/s3_bucket"&lt;/span&gt;

  &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"my-unique-bucket"&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The &lt;code&gt;var.create_bucket == true ? 1 : 0&lt;/code&gt; expression can be written even shorter: &lt;code&gt;var.create_bucket ? 1 : 0&lt;/code&gt; because the &lt;code&gt;create_bucket&lt;/code&gt; variable has boolean type, apparently.&lt;/p&gt;

&lt;p&gt;But what if you need to produce more than one instance of a resource or module? And still be able to avoid their creation.&lt;/p&gt;

&lt;p&gt;Another meta-argument — &lt;code&gt;for_each&lt;/code&gt; — will do the trick.&lt;/p&gt;

&lt;p&gt;For example, this is how it looks for a module:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="s2"&gt;"bucket"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;for_each&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bucket_names&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="err"&gt;?&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bucket_names&lt;/span&gt;
  &lt;span class="nx"&gt;source&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"./modules/s3_bucket"&lt;/span&gt;

  &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;each&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="nx"&gt;enable_encryption&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;In this illustration, I also used a conditional expression that makes Terraform iterate through the set of values of &lt;code&gt;var.bucket_names&lt;/code&gt; if it’s not empty and create several modules. Otherwise, do not iterate at all and do not create anything.&lt;/p&gt;

&lt;p&gt;The same can be done for the resources. For example, when you need to create an arbitrary number of security group rules, e.g., to allowlist some IPs for your bastion host:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_security_group_rule"&lt;/span&gt; &lt;span class="s2"&gt;"allowlist"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;for_each&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cidr_blocks&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="err"&gt;?&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cidr_blocks&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ingress"&lt;/span&gt;
  &lt;span class="nx"&gt;from_port&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;22&lt;/span&gt;
  &lt;span class="nx"&gt;to_port&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;22&lt;/span&gt;
  &lt;span class="nx"&gt;protocol&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"tcp"&lt;/span&gt;
  &lt;span class="nx"&gt;cidr_blocks&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;each&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="nx"&gt;security_group_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_security_group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bastion&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;And just like with the &lt;code&gt;count&lt;/code&gt; meta-argument, with the &lt;code&gt;for_each&lt;/code&gt;, resource addresses will have the identifier named by the values provided to &lt;code&gt;for_each&lt;/code&gt;. For example, here is how I would reference a resource created in the module with &lt;code&gt;for_each&lt;/code&gt; described earlier:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="nx"&gt;bucket_name&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="k"&gt;module&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"photos"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Conditional resource arguments (attributes) setting
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Gep3OT77--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/67dnkfbwei8rcaprq7x2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Gep3OT77--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/67dnkfbwei8rcaprq7x2.png" alt="Conditional resource arguments" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now let’s go deeper and see how resource arguments can be conditionally set (or not).&lt;/p&gt;

&lt;p&gt;First, let’s review the conditional argument value setting with the &lt;code&gt;null&lt;/code&gt; data type:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_launch_template"&lt;/span&gt; &lt;span class="s2"&gt;"this"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"my-launch-template"&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
  &lt;span class="nx"&gt;key_name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;use_default_keypair&lt;/span&gt; &lt;span class="err"&gt;?&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keypair_name&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Here I want to skip the usage of the EC2 Key Pair for the Launch Template in some instances and Terraform allows me to write the conditional expression that will set the &lt;code&gt;null&lt;/code&gt; value for the argument. It means the &lt;em&gt;absence&lt;/em&gt; or &lt;em&gt;omission&lt;/em&gt; and Terraform would behave the same as if you did not specify the argument at all.&lt;/p&gt;

&lt;p&gt;Dynamic blocks are another case where this technique suits best. Take a look at the following piece of CloudFront resource code where I want to either describe the configuration for the custom error response or omit that completely:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_cloudfront_distribution"&lt;/span&gt; &lt;span class="s2"&gt;"cdn"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;enabled&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
  &lt;span class="nx"&gt;dynamic&lt;/span&gt; &lt;span class="s2"&gt;"custom_error_response"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;for_each&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;custom_error_response&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="err"&gt;?&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;custom_error_response&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="nx"&gt;iterator&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;cer&lt;/span&gt;
    &lt;span class="nx"&gt;content&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;error_code&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"error_code"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nx"&gt;error_caching_min_ttl&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"error_caching_min_ttl"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nx"&gt;response_code&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"response_code"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nx"&gt;response_page_path&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"response_page_path"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The &lt;code&gt;custom_error_response&lt;/code&gt; variable is &lt;code&gt;null&lt;/code&gt; by default, but it has the &lt;code&gt;object&lt;/code&gt; type, and users can assign the variable with the required nested specifications if needed. And when they do it, Terraform will add the &lt;code&gt;custom_error_response&lt;/code&gt; block to the resource configuration. Otherwise, it will be omitted entirely.&lt;/p&gt;
&lt;h2&gt;
  
  
  Convert types with ease
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Sm1gPMMJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xlycu80ki7u1z5eur4wn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Sm1gPMMJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xlycu80ki7u1z5eur4wn.png" alt="Convert types with ease" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ok, let’s move to the less conditional things now 😅&lt;/p&gt;

&lt;p&gt;Terraform has several type conversion functions: &lt;code&gt;tobool()&lt;/code&gt;, &lt;code&gt;tolist()&lt;/code&gt;,&lt;code&gt;tomap()&lt;/code&gt;, &lt;code&gt;tonumber()&lt;/code&gt;, &lt;code&gt;toset()&lt;/code&gt;, and &lt;code&gt;tostring()&lt;/code&gt;. Their purpose is to convert the input values to the compatible types.&lt;/p&gt;

&lt;p&gt;For example, suppose I need to pass the set to the &lt;code&gt;for_each&lt;/code&gt; (it accepts only sets and maps types of value), but I got the list as an input; let’s say I got it as an output from another module. In such a case, I would do something like this:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="nx"&gt;for_each&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;toset&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;remote_access_ports&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;

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

&lt;/div&gt;


&lt;p&gt;However, I can make my code cleaner and avoid the explicit conversion — I just need to define the value type in the configuration block of the &lt;code&gt;my_list&lt;/code&gt; variable. Terraform will do the conversion automatically when the value is assigned.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="k"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"remote_access_ports"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Ports for remote access"&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;While Terraform can do a lot of implicit conversions for you, explicit type conversions are practical during values normalization or when you need to calculate some complex value for a variable. For example, the Local Values, known as &lt;code&gt;locals&lt;/code&gt;, are the most suitable place for doing that.&lt;/p&gt;

&lt;p&gt;By the way, although there is a &lt;code&gt;tolist()&lt;/code&gt; function, there is no such thing as the &lt;code&gt;tostring()&lt;/code&gt; function. But what if you need to convert the list to string in Terraform?&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;one()&lt;/code&gt; function can help here: it takes a list, set, or tuple value with either zero or one element and returns either &lt;code&gt;null&lt;/code&gt; or that one element in the form of string.&lt;/p&gt;

&lt;p&gt;It’s useful in cases when a resource created using conditional expression is represented as either a zero- or one-element list, and you need to get a single value which may be either &lt;code&gt;null&lt;/code&gt; or &lt;code&gt;string&lt;/code&gt;, for example:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_kms_key"&lt;/span&gt; &lt;span class="s2"&gt;"main"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ebs_encrypted&lt;/span&gt; &lt;span class="err"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

  &lt;span class="nx"&gt;enable_key_rotation&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tags&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_kms_alias"&lt;/span&gt; &lt;span class="s2"&gt;"main"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ebs_encrypted&lt;/span&gt; &lt;span class="err"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

  &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"alias/encrypt-ebs"&lt;/span&gt;
  &lt;span class="nx"&gt;target_key_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;one&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;aws_kms_key&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;[*]&lt;/span&gt;&lt;span class="nx"&gt;key_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Write YAML or JSON as Terraform code (HCL)
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--LeQT09Zq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cdwhx72j67nywt1wqcfo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--LeQT09Zq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cdwhx72j67nywt1wqcfo.png" alt="Write YAML or JSON as Terraform code" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Sometimes you need to supply JSON or YAML files to the services you manage with Terraform. For example, if you want to create something with CloudFormation using Terraform (and I am not kidding). Sometimes the AWS Terraform provider does not support the needed resource, and you want to maintain the whole infrastructure code using only one tool.&lt;/p&gt;

&lt;p&gt;Instead of maintaining another file in JSON or YAML format, you can embed JSON or YAML code management into HCL by taking benefit of the &lt;code&gt;jsonencode()&lt;/code&gt; or &lt;code&gt;yamlencode()&lt;/code&gt; functions.&lt;/p&gt;

&lt;p&gt;The attractiveness of this approach is that you can reference other Terraform resources or their attributes right in the code of your object, and you have more freedom in terms of the code syntax and its formatting comparable to native JSON or YAML.&lt;/p&gt;

&lt;p&gt;Here is how it looks like:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="nx"&gt;locals&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;some_string&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ult"&lt;/span&gt;
  &lt;span class="nx"&gt;myjson_object&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jsonencode&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="s2"&gt;"Hashicorp Products"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;Terra&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"form"&lt;/span&gt;
      &lt;span class="nx"&gt;Con&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"sul"&lt;/span&gt;
      &lt;span class="nx"&gt;Vag&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"rant"&lt;/span&gt;
      &lt;span class="nx"&gt;Va&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;some_string&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The value of the &lt;code&gt;myjson_object&lt;/code&gt; local variable would look like this:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Hashicorp Products"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Con"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sul"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Terra"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"form"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Va"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ult"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Vag"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"rant"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;And here is a piece of real-world example:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="nx"&gt;locals&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;cf_template_body&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jsonencode&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="nx"&gt;Resources&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;DedicatedHostGroup&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;Type&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"AWS::ResourceGroups::Group"&lt;/span&gt;
        &lt;span class="nx"&gt;Properties&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;Name&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;service_name&lt;/span&gt;
          &lt;span class="nx"&gt;Configuration&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="nx"&gt;Type&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"AWS::EC2::HostManagement"&lt;/span&gt;
              &lt;span class="nx"&gt;Parameters&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt;
                  &lt;span class="nx"&gt;Name&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"auto-allocate-host"&lt;/span&gt;
                  &lt;span class="nx"&gt;Values&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;auto_allocate_host&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
                &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="p"&gt;...&lt;/span&gt;
            &lt;span class="p"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Templatize stuff
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--LIk7L7wd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/et3x95e5se6wlvuyasp9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--LIk7L7wd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/et3x95e5se6wlvuyasp9.png" alt="Templatize stuff" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The last case in this blog but not the least by its efficacy — render source file content as a template in Terraform.&lt;/p&gt;

&lt;p&gt;Let’s review the following scenario: you launch an EC2 instance and want to supply it with a bash script (via the user-data parameter) for some additional configuration at launch.&lt;/p&gt;

&lt;p&gt;Suppose we have the following bash script &lt;code&gt;instance-init.sh&lt;/code&gt; that sets the hostname and registers our instance in a monitoring system:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;

&lt;span class="nb"&gt;hostname &lt;/span&gt;example.com
bash /opt/system-init/register-monitoring.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;But what if you want to set a different hostname per instance, and some instances should not be registered in the monitoring system?&lt;/p&gt;

&lt;p&gt;In such a case, here is how the script file content will look:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#!/bin/bash

hostname ${system_hostname}
%{ if register_monitoring }
bash /opt/system-init/register-monitoring.sh
%{endif}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;And when you supply this file as an argument for the EC2 instance resource in Terraform, you will use the &lt;code&gt;templatefile()&lt;/code&gt; function to make the magic happen:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_instance"&lt;/span&gt; &lt;span class="s2"&gt;"web"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;ami&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;my_ami_id&lt;/span&gt;
  &lt;span class="nx"&gt;instance_type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;instance_type&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
  &lt;span class="nx"&gt;user_data&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;templatefile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;module}&lt;/span&gt;&lt;span class="s2"&gt;/instance-init.tftpl"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;system_hostname&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;system_hostname&lt;/span&gt;
    &lt;span class="nx"&gt;register_monitoring&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;add_to_monitoring&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;And of course, you can create a template from any file type. The only requirement here is that the template file must exist on the disk at the beginning of the Terraform execution.&lt;/p&gt;
&lt;h2&gt;
  
  
  Key takeaways
&lt;/h2&gt;

&lt;p&gt;Terraform is far beyond the standard resource management operations. With the power of built-in functions, you can write more versatile code and reusable Terraform modules.&lt;/p&gt;

&lt;p&gt;✅ Use &lt;a href="https://www.terraform.io/language/expressions/conditionals"&gt;conditional expressions&lt;/a&gt; with &lt;a href="https://www.terraform.io/language/meta-arguments/count"&gt;count&lt;/a&gt; and &lt;a href="https://www.terraform.io/language/meta-arguments/for_each"&gt;for_each&lt;/a&gt; meta-arguments, when the creation of a resource depends on some context or user inout.&lt;/p&gt;

&lt;p&gt;✅ Take advantage of &lt;a href="https://www.terraform.io/language/expressions/types#type-conversion"&gt;implicit types conversion&lt;/a&gt; when working with input variables and their values to keep your code cleaner.&lt;/p&gt;

&lt;p&gt;✅ Embed YAML and JSON-based objects right into your Terraform code using built-in &lt;a href="https://www.terraform.io/language/functions/jsonencode"&gt;encoding&lt;/a&gt; &lt;a href="https://www.terraform.io/language/functions/yamlencode"&gt;functions&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;✅ And when you need to pass some files to the managed service, you can treat them as &lt;a href="https://www.terraform.io/language/functions/templatefile"&gt;templates&lt;/a&gt; and make them multipurpose.&lt;/p&gt;

&lt;p&gt;Thank you for reading down to this point! 🤗&lt;/p&gt;

&lt;p&gt;And if you have some favorite Terraform tricks — I would love to know!&lt;/p&gt;




&lt;div class="ltag__user ltag__user__id__51518"&gt;
    &lt;a href="/svasylenko" class="ltag__user__link profile-image-link"&gt;
      &lt;div class="ltag__user__pic"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--OaW58MNK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/practicaldev/image/fetch/s--6678KR6E--/c_fill%2Cf_auto%2Cfl_progressive%2Ch_150%2Cq_auto%2Cw_150/https://dev-to-uploads.s3.amazonaws.com/uploads/user/profile_image/51518/60259e60-86ea-47fe-92ff-5ac3f00835d2.jpg" alt="svasylenko image"&gt;
      &lt;/div&gt;
    &lt;/a&gt;
  &lt;div class="ltag__user__content"&gt;
    &lt;h2&gt;
&lt;a class="ltag__user__link" href="/svasylenko"&gt;Serhii Vasylenko&lt;/a&gt;Follow
&lt;/h2&gt;
    &lt;div class="ltag__user__summary"&gt;
      &lt;a class="ltag__user__link" href="/svasylenko"&gt;I am an engineer from Ukraine. I like astronomy and everything related to DevOps. I thrive on developing great product offerings, great people, and great teams.&lt;/a&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;



</description>
      <category>terraform</category>
      <category>devops</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Apply Cloudfront Security Headers Policy With Terraform</title>
      <dc:creator>Serhii Vasylenko</dc:creator>
      <pubDate>Fri, 05 Nov 2021 13:23:17 +0000</pubDate>
      <link>https://dev.to/aws-builders/apply-cloudfront-security-headers-policy-with-terraform-fd3</link>
      <guid>https://dev.to/aws-builders/apply-cloudfront-security-headers-policy-with-terraform-fd3</guid>
      <description>&lt;p&gt;In November 2021, AWS announced Response Headers Policies — native support of response headers in CloudFront. You can read the full announcement here: &lt;a href="https://aws.amazon.com/blogs/networking-and-content-delivery/amazon-cloudfront-introduces-response-headers-policies/" rel="noopener noreferrer"&gt;Amazon CloudFront introduces Response Headers Policies&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I said “native” because previously you could set response headers either using &lt;a href="https://serhii.vasylenko.info/2021/05/21/configure-http-security-headers-with-cloudfront-functions.html" rel="noopener noreferrer"&gt;CloudFront Functions&lt;/a&gt; or &lt;a href="https://aws.amazon.com/blogs/networking-and-content-delivery/adding-http-security-headers-using-lambdaedge-and-amazon-cloudfront/" rel="noopener noreferrer"&gt;Lambda@Edge&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;And one of the common use cases for that was to set security headers. So now you don’t need to add intermediate requests processing to modify the headers: CloudFront does that for you &lt;strong&gt;with no additional fee&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Manage Security Headers as Code
&lt;/h2&gt;

&lt;p&gt;Starting from the &lt;a href="https://github.com/hashicorp/terraform-provider-aws/blob/main/CHANGELOG.md#3640-november-04-2021" rel="noopener noreferrer"&gt;3.64.0&lt;/a&gt; version of Terraform AWS provider, you can create the security headers policies and apply them for your distribution.&lt;/p&gt;

&lt;p&gt;Let’s see how that looks!&lt;/p&gt;

&lt;p&gt;First, you need to describe the &lt;code&gt;aws_cloudfront_response_headers_policy&lt;/code&gt; resource:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;

 &lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_cloudfront_response_headers_policy"&lt;/span&gt; &lt;span class="s2"&gt;"security_headers_policy"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"my-security-headers-policy"&lt;/span&gt;
  &lt;span class="nx"&gt;security_headers_config&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;content_type_options&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;override&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nx"&gt;frame_options&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;frame_option&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"DENY"&lt;/span&gt;
      &lt;span class="nx"&gt;override&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nx"&gt;referrer_policy&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;referrer_policy&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"same-origin"&lt;/span&gt;
      &lt;span class="nx"&gt;override&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nx"&gt;xss_protection&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;mode_block&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
      &lt;span class="nx"&gt;protection&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
      &lt;span class="nx"&gt;override&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nx"&gt;strict_transport_security&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;access_control_max_age_sec&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"63072000"&lt;/span&gt;
      &lt;span class="nx"&gt;include_subdomains&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
      &lt;span class="nx"&gt;preload&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
      &lt;span class="nx"&gt;override&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nx"&gt;content_security_policy&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;content_security_policy&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"frame-ancestors 'none'; default-src 'none'; img-src 'self'; script-src 'self'; style-src 'self'; object-src 'none'"&lt;/span&gt;
      &lt;span class="nx"&gt;override&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


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

&lt;/div&gt;
&lt;p&gt;List of security headers used:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://infosec.mozilla.org/guidelines/web_security#x-content-type-options" rel="noopener noreferrer"&gt;X-Content-Type-Options&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://infosec.mozilla.org/guidelines/web_security#x-frame-options" rel="noopener noreferrer"&gt;X-Frame-Options&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://infosec.mozilla.org/guidelines/web_security#referrer-policy" rel="noopener noreferrer"&gt;Referrer Policy&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://infosec.mozilla.org/guidelines/web_security#x-xss-protection" rel="noopener noreferrer"&gt;X-XSS-Protection&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://infosec.mozilla.org/guidelines/web_security#http-strict-transport-security" rel="noopener noreferrer"&gt;Strict Transport Security&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://infosec.mozilla.org/guidelines/web_security#content-security-policy" rel="noopener noreferrer"&gt;Content Security Policy&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The values for the security headers can be different, of course. However, the provided ones cover the majority of cases. And you can always get the up to date info about these headers and possible values here: &lt;a href="https://infosec.mozilla.org/guidelines/web_security" rel="noopener noreferrer"&gt;Mozilla web Security Guidelines&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Also, you could notice that provided example uses the &lt;code&gt;override&lt;/code&gt; argument a lot. The &lt;code&gt;override&lt;/code&gt; argument tells CloudFront to set these values for specified headers despite the values received from the origin. This way, you can enforce your security headers configuration.&lt;/p&gt;

&lt;p&gt;Once you have the &lt;code&gt;aws_cloudfront_response_headers_policy&lt;/code&gt; resource, you can refer to it in the code of &lt;code&gt;aws_cloudfront_distribution&lt;/code&gt; resource inside cache behavior block (default or ordered). For example, in your &lt;code&gt;default_cache_behavior&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;

&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_cloudfront_distribution"&lt;/span&gt; &lt;span class="s2"&gt;"test"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;default_cache_behavior&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;target_origin_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_s3_bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;my_origin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
    &lt;span class="nx"&gt;allowed_methods&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"GET"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"HEAD"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"OPTIONS"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="nx"&gt;cached_methods&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"GET"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"HEAD"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="nx"&gt;viewer_protocol_policy&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"redirect-to-https"&lt;/span&gt;

    &lt;span class="c1"&gt;# some arguments skipped from listing for the sake of simplicity&lt;/span&gt;

    &lt;span class="nx"&gt;response_headers_policy_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_cloudfront_response_headers_policy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;security_headers_policy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;

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

  &lt;span class="c1"&gt;# some arguments skipped from listing for the sake of simplicity&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;



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

&lt;/div&gt;
&lt;h3&gt;
  
  
  More to read:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudfront_response_headers_policy" rel="noopener noreferrer"&gt;Terraform Resource: aws_cloudfront_response_headers_policy&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/creating-response-headers-policies.html" rel="noopener noreferrer"&gt;Creating response headers policies - Amazon CloudFront&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/using-managed-response-headers-policies.html" rel="noopener noreferrer"&gt;Using the managed response headers policies - Amazon CloudFront&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/understanding-response-headers-policies.html" rel="noopener noreferrer"&gt;Understanding response headers policies - Amazon CloudFront&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;div class="ltag__user ltag__user__id__51518"&gt;
    &lt;a href="/svasylenko" class="ltag__user__link profile-image-link"&gt;
      &lt;div class="ltag__user__pic"&gt;
        &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F51518%2F60259e60-86ea-47fe-92ff-5ac3f00835d2.jpg" alt="svasylenko image"&gt;
      &lt;/div&gt;
    &lt;/a&gt;
  &lt;div class="ltag__user__content"&gt;
    &lt;h2&gt;
&lt;a class="ltag__user__link" href="/svasylenko"&gt;Serhii Vasylenko&lt;/a&gt;Follow
&lt;/h2&gt;
    &lt;div class="ltag__user__summary"&gt;
      &lt;a class="ltag__user__link" href="/svasylenko"&gt;I am an engineer from Ukraine. I like astronomy and everything related to DevOps. I thrive on developing great product offerings, great people, and great teams.&lt;/a&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;



</description>
      <category>aws</category>
      <category>terraform</category>
      <category>devops</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Auto Scaling Group for your macOS EC2 Instances fleet</title>
      <dc:creator>Serhii Vasylenko</dc:creator>
      <pubDate>Mon, 25 Oct 2021 08:33:05 +0000</pubDate>
      <link>https://dev.to/aws-builders/auto-scaling-group-for-your-macos-ec2-instances-fleet-57pj</link>
      <guid>https://dev.to/aws-builders/auto-scaling-group-for-your-macos-ec2-instances-fleet-57pj</guid>
      <description>&lt;p&gt;It’s been almost a year since I started using macOS EC2 instances on AWS: there were &lt;a href="https://dev.to/aws-builders/mac1-metal-ec2-instance-user-experience-j08"&gt;ups and downs in service offerings&lt;/a&gt; and a lot of discoveries with &lt;a href="https://dev.to/aws-builders/customizing-mac1-metal-ec2-ami-new-guts-more-glory-48da"&gt;macOS AMI build&lt;/a&gt; automation.&lt;/p&gt;

&lt;p&gt;And I like this small but so helpful update of EC2 service very much: with mac1.metal instances, seamless integration of Apple-oriented CI/CD with other AWS infrastructure could finally happen.&lt;/p&gt;

&lt;p&gt;While management of a single mac1.metal node (or a tiny number of ones) is not a big deal (especially when &lt;a href="https://dev.to/aws-builders/terraforming-mac1-metal-at-aws-2lb3"&gt;Dedicated Host support&lt;/a&gt; was added to Terraform provider), governing the fleet of instances is still complicated. Or it has been complicated until recent days.&lt;/p&gt;

&lt;h2&gt;
  
  
  Official / Unofficial Auto Scaling for macOS
&lt;/h2&gt;

&lt;p&gt;With a growing number of instances, the following challenges arise:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Scale mac1.metal instances horizontally&lt;/li&gt;
&lt;li&gt;Automatically allocate and release Dedicated Hosts needed for instances&lt;/li&gt;
&lt;li&gt;Automatically replace unhealthy instances&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you have worked with AWS before, you know that Auto Scaling Group service can solve such things.&lt;/p&gt;

&lt;p&gt;However, official documentation (as of October 2021) &lt;a href="https://github.com/awsdocs/amazon-ec2-user-guide/blob/269ac7494dd3aef62ae5d45e8b11f7ea5cadd2bf/doc_source/ec2-mac-instances.md"&gt;states&lt;/a&gt;: “You cannot use Mac instances with Amazon EC2 Auto Scaling”.&lt;/p&gt;

&lt;p&gt;But in fact, you can.&lt;/p&gt;

&lt;h2&gt;
  
  
  Combining services to get real power
&lt;/h2&gt;

&lt;p&gt;So how does all that work?&lt;/p&gt;

&lt;p&gt;Let’s review the diagram that illustrates the interconnection between involved services:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hdUCkUwf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ebxeig70ylb84debifro.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hdUCkUwf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ebxeig70ylb84debifro.png" alt="solution diagram" width="800" height="639"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With the help of the Licence Manager service and Launch Templates, you can set up EC2 Auto Scaling Group for mac1.metal and leave the automated instance provisioning to the service.&lt;/p&gt;

&lt;h3&gt;
  
  
  License Configuration
&lt;/h3&gt;

&lt;p&gt;First, you need to create a License Configuration so that the Host resource group can allocate the hots.&lt;/p&gt;

&lt;p&gt;Go to AWS License Manager -&amp;gt; Customer managed licenses -&amp;gt; Create customer-managed license.&lt;/p&gt;

&lt;p&gt;Specify &lt;strong&gt;Sockets&lt;/strong&gt; as the Licence type. You may skip setting the Number of Sockets. However, the actual limit of mac1.metal instances per account is regulated by Service Quota. The default number of mac instances allowed per account is 3. Therefore, consider &lt;a href="https://docs.aws.amazon.com/servicequotas/latest/userguide/request-quota-increase.html"&gt;increasing&lt;/a&gt; this to a more significant number.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--oVEfspmM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/skp4rw8nmz1d34ivhls3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--oVEfspmM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/skp4rw8nmz1d34ivhls3.png" alt="License configuration" width="800" height="807"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Host resource group
&lt;/h3&gt;

&lt;p&gt;Second, create the Host resource group: AWS License Manager -&amp;gt; Host resource groups -&amp;gt; Create host resource group.&lt;/p&gt;

&lt;p&gt;When creating the Host resource group, check “&lt;strong&gt;Allocate hosts automatically&lt;/strong&gt;” and “&lt;strong&gt;Release hosts automatically&lt;/strong&gt;” but leave “Recover hosts automatically” unchecked. Dedicated Host does &lt;a href="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/dedicated-hosts-recovery.html#dedicated-hosts-recovery-instances"&gt;not support host recovery&lt;/a&gt; for mac1.metal.&lt;br&gt;
However, Auto Scaling Group will maintain the desired number of instances if one fails the health check (which assumes the case of host failure as well).&lt;/p&gt;

&lt;p&gt;Also, I recommend specifying “mac1” as an allowed Instance family for the sake of transparent resource management: only this instance type is permitted to allocate hosts in the group.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--TlvYS7t0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o2eka4fpe65ddn3wrgo6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--TlvYS7t0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o2eka4fpe65ddn3wrgo6.png" alt="Host resource group" width="800" height="902"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Optionally, you may specify the license association here (the Host group will pick any compatible license) or select the license you created on step one. &lt;/p&gt;
&lt;h3&gt;
  
  
  Launch Template
&lt;/h3&gt;

&lt;p&gt;Create Launch Template: EC2 -&amp;gt; Launch templates -&amp;gt; Create launch template.&lt;/p&gt;

&lt;p&gt;I will skip the description of all Launch Template parameters (but here is a nice &lt;a href="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-launch-templates.html"&gt;tutorial&lt;/a&gt;), if you don’t mind, and keep focus only on the items relevant to the current case.&lt;/p&gt;

&lt;p&gt;Specify mac1.metal as the Instance type. Later, in Advanced details: find the &lt;strong&gt;Tenancy&lt;/strong&gt; parameter and set it to “Dedicated host”; for &lt;strong&gt;Target host by&lt;/strong&gt; select “Host resource group”, and once selected the new parameter &lt;strong&gt;Tenancy host resource group&lt;/strong&gt; will appear where you should choose your host group; select your license in &lt;strong&gt;License configurations&lt;/strong&gt; parameter.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--qEzV5beI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r566cqm7l0jzu3tozg57.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qEzV5beI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r566cqm7l0jzu3tozg57.png" alt="Launch Template" width="800" height="741"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Auto Scaling Group
&lt;/h3&gt;

&lt;p&gt;Finally, create the Auto Scaling Group: EC2 -&amp;gt; Auto Scaling groups -&amp;gt; Create Auto Scaling group.&lt;/p&gt;

&lt;p&gt;The vital thing to note here — is the availability of the mac1.metal instance in particular AZ.&lt;/p&gt;

&lt;p&gt;Mac instances are available in us-east-1 and &lt;a href="https://aws.amazon.com/about-aws/whats-new/2021/10/amazon-ec2-mac-instances-additional-regions/"&gt;7 more regions&lt;/a&gt;, but not every Availability Zone in the region supports it. So you must figure out which AZ supports the needed instance type.&lt;/p&gt;

&lt;p&gt;There is no documentation for that, but there is an AWS CLI command that can answer this question: &lt;a href="https://awscli.amazonaws.com/v2/documentation/api/latest/reference/ec2/describe-instance-type-offerings.html"&gt;describe-instance-type-offerings — AWS CLI 2.3.0 Command Reference&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here is an example for the us-east-1 region:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; aws ec2 describe-instance-type-offerings &lt;span class="nt"&gt;--location-type&lt;/span&gt; availability-zone-id &lt;span class="nt"&gt;--filters&lt;/span&gt; &lt;span class="nv"&gt;Name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;instance-type,Values&lt;span class="o"&gt;=&lt;/span&gt;mac1.metal &lt;span class="nt"&gt;--region&lt;/span&gt; us-east-1 &lt;span class="nt"&gt;--output&lt;/span&gt; text

INSTANCETYPEOFFERINGS   mac1.metal  use1-az6    availability-zone-id
INSTANCETYPEOFFERINGS   mac1.metal  use1-az4    availability-zone-id
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Keep that nuance in mind when selecting a subnet for the mac1.metal instances.&lt;/p&gt;

&lt;p&gt;When you know the AZ, specify the respective Subnet in the Auto Scaling Group settings, and you're ready to go! &lt;/p&gt;
&lt;h2&gt;
  
  
  Bring Infrastructure as Code here
&lt;/h2&gt;

&lt;p&gt;I suggest describing all that as a code. I prefer Terraform, and its AWS provider supports the needed resources. Except one.&lt;/p&gt;

&lt;p&gt;As of October 2021, resources supported :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/servicequotas_service_quota"&gt;aws_servicequotas_service_quota&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/licensemanager_license_configuration"&gt;aws_licensemanager_license_configuration&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/launch_template"&gt;aws_launch_template&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/autoscaling_group"&gt;aws_autoscaling_group&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Host resource group is not yet supported by the provider, unfortunately. However, we can use CloudFormation in Terraform to overcome that: describe the Host resource group as &lt;a href="https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudformation_stack"&gt;aws_cloudformation_stack&lt;/a&gt; Terraform resource using CloudFormation template from a file.&lt;/p&gt;

&lt;p&gt;Here is how it looks like:&lt;br&gt;

  Click here to see the code snippet
  &lt;br&gt;

&lt;pre&gt;&lt;code&gt;&lt;span&gt;resource&lt;/span&gt; &lt;span&gt;"aws_licensemanager_license_configuration"&lt;/span&gt; &lt;span&gt;"this"&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
  &lt;span&gt;name&lt;/span&gt;                     &lt;span&gt;=&lt;/span&gt; &lt;span&gt;local&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;full_name&lt;/span&gt;
  &lt;span&gt;license_counting_type&lt;/span&gt;    &lt;span&gt;=&lt;/span&gt; &lt;span&gt;"Socket"&lt;/span&gt;
&lt;span&gt;}&lt;/span&gt;

&lt;span&gt;resource&lt;/span&gt; &lt;span&gt;"aws_cloudformation_stack"&lt;/span&gt; &lt;span&gt;"this"&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
  &lt;span&gt;name&lt;/span&gt;          &lt;span&gt;=&lt;/span&gt; &lt;span&gt;local&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;full_name&lt;/span&gt; &lt;span&gt;# the name of CloudFormation stack&lt;/span&gt;
  &lt;span&gt;template_body&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;file&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;"${path.module}/resource-group-cf-stack-template.json"&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;
  &lt;span&gt;parameters&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;
    &lt;span&gt;GroupName&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;local&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;full_name&lt;/span&gt; &lt;span&gt;# the name for the Host group, passed to CloudFormation template&lt;/span&gt;
  &lt;span&gt;}&lt;/span&gt;
  &lt;span&gt;on_failure&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;"DELETE"&lt;/span&gt;
&lt;span&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;




&lt;/p&gt;

&lt;p&gt;And the next code snippet explains the CloudFromation template (which is the &lt;code&gt;resource-group-cf-stack-template.json&lt;/code&gt; file in the code snippet above)&lt;/p&gt;

&lt;p&gt;
  Click here to see the code snippet
  &lt;br&gt;

&lt;pre&gt;&lt;code&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;
  &lt;/span&gt;&lt;span&gt;"Parameters"&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;
    &lt;/span&gt;&lt;span&gt;"GroupName"&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;
      &lt;/span&gt;&lt;span&gt;"Type"&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;"String"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;
      &lt;/span&gt;&lt;span&gt;"Description"&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;"The name of Host Group"&lt;/span&gt;&lt;span&gt;
    &lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;
  &lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;span&gt;
  &lt;/span&gt;&lt;span&gt;"Resources"&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;
    &lt;/span&gt;&lt;span&gt;"DedicatedHostGroup"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;
      &lt;/span&gt;&lt;span&gt;"Type"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;"AWS::ResourceGroups::Group"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;
      &lt;/span&gt;&lt;span&gt;"Properties"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;
        &lt;/span&gt;&lt;span&gt;"Name"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;"Ref"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;"GroupName"&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;span&gt;
        &lt;/span&gt;&lt;span&gt;"Configuration"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;
          &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;
            &lt;/span&gt;&lt;span&gt;"Type"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;"AWS::ResourceGroups::Generic"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;
            &lt;/span&gt;&lt;span&gt;"Parameters"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;
              &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;
                &lt;/span&gt;&lt;span&gt;"Name"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;"allowed-resource-types"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;
                &lt;/span&gt;&lt;span&gt;"Values"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;"AWS::EC2::Host"&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;
              &lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;span&gt;
              &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;
                &lt;/span&gt;&lt;span&gt;"Name"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;"deletion-protection"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;
                &lt;/span&gt;&lt;span&gt;"Values"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;"UNLESS_EMPTY"&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;
              &lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;
            &lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;
          &lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;span&gt;
          &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;
            &lt;/span&gt;&lt;span&gt;"Type"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;"AWS::EC2::HostManagement"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;
            &lt;/span&gt;&lt;span&gt;"Parameters"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;
              &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;
                &lt;/span&gt;&lt;span&gt;"Name"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;"allowed-host-families"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;
                &lt;/span&gt;&lt;span&gt;"Values"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;"mac1"&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;
              &lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;span&gt;
              &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;
                &lt;/span&gt;&lt;span&gt;"Name"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;"auto-allocate-host"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;
                &lt;/span&gt;&lt;span&gt;"Values"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;"true"&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;
              &lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;span&gt;
              &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;
                &lt;/span&gt;&lt;span&gt;"Name"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;"auto-release-host"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;
                &lt;/span&gt;&lt;span&gt;"Values"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;"true"&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;
              &lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;span&gt;
              &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;
                &lt;/span&gt;&lt;span&gt;"Name"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;"any-host-based-license-configuration"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;
                &lt;/span&gt;&lt;span&gt;"Values"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;"true"&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;
              &lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;
            &lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;
          &lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;
        &lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;
      &lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;
    &lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;
  &lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;span&gt;
  &lt;/span&gt;&lt;span&gt;"Outputs"&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;
    &lt;/span&gt;&lt;span&gt;"ResourceGroupARN"&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;
      &lt;/span&gt;&lt;span&gt;"Description"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;"ResourceGroupARN"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;
      &lt;/span&gt;&lt;span&gt;"Value"&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;"Fn::GetAtt"&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;"DedicatedHostGroup"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;"Arn"&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;
    &lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;
  &lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;




&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;aws_cloudformation_stack&lt;/code&gt; resource will export the &lt;code&gt;DedicatedHostGroup&lt;/code&gt; attribute (see the code of CloudFromation template), which you will use later in the Launch Template resource. &lt;/p&gt;
&lt;h3&gt;
  
  
  Pro tips
&lt;/h3&gt;

&lt;p&gt;If you manage an AWS Organization, I have good news: Host groups and Licenses are supported by &lt;a href="https://docs.aws.amazon.com/ram/latest/userguide/shareable.html"&gt;Resource Access Manager&lt;/a&gt; service. Hence, you can host all mac instances in one account and share them with other accounts — it might be helpful for costs allocation, for example. Also, check out &lt;a href="https://dev.to/aws-builders/aws-resource-access-manager-simple-and-powerful-service-for-multi-account-resource-governance-11na"&gt;my blog about AWS RAM&lt;/a&gt; if you are very new to this service.&lt;/p&gt;

&lt;p&gt;To solve the “which AZ supports mac metal” puzzle, you can leverage the &lt;a href="https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/ec2_instance_type_offerings"&gt;aws_ec2_instance_type_offerings&lt;/a&gt; and &lt;a href="https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/subnet_ids"&gt;aws_subnet_ids&lt;/a&gt; data sources.&lt;/p&gt;
&lt;h2&gt;
  
  
  Costs considerations
&lt;/h2&gt;

&lt;p&gt;License Manager is a &lt;a href="https://aws.amazon.com/license-manager/pricing/"&gt;free of charge service&lt;/a&gt;, as well as &lt;a href="https://aws.amazon.com/autoscaling/pricing/"&gt;Auto Scaling&lt;/a&gt;, and &lt;a href="https://aws.amazon.com/about-aws/whats-new/2017/11/introducing-launch-templates-for-amazon-ec2-instances/"&gt;Launch Template&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;So it’s all about the price for mac1.metal Dedicated Host which is &lt;a href="https://aws.amazon.com/ec2/dedicated-hosts/pricing/"&gt;$1.083 per hour&lt;/a&gt; as of October 2021. However, &lt;a href="https://docs.aws.amazon.com/savingsplans/latest/userguide/what-is-savings-plans.html"&gt;Saving Plans&lt;/a&gt; can be applied.&lt;/p&gt;

&lt;p&gt;Please note that the minimum allocation time for that type of host is 24 hours. Maybe someday AWS will change that to 1-hour minimum (fingers crossed).&lt;/p&gt;
&lt;h2&gt;
  
  
  Oh. So. ASG.
&lt;/h2&gt;

&lt;p&gt;The Auto Scaling for mac1.metal opens new possibilities for CI/CD: you can integrate that to your favorite tool (GitLab, Jenkins, whatsoever) using AWS Lambda and provision new instances when your development/testing environments need that. Or you can use other cool ASG stuff, such as Lifecycle hooks, to create even more custom scenarios.&lt;/p&gt;

&lt;p&gt;Considering the “hidden” (undocumented) nature of the described setup, I suggest treating it as rather testing than production-ready for now. However, my tests show that everything works pretty well: hosts are allocated, instances are spawned, and the monthly bill grows.&lt;/p&gt;

&lt;p&gt;I suppose AWS will officially announce all this in the nearest future. Along with that, I am looking forward to the announcement of Monterey-based AMIs and maybe even M1 chip-based instances (will it be mac2.metal?).&lt;/p&gt;

&lt;p&gt;And I want to say thanks (thanks, pal!) to &lt;a href="https://github.com/hashicorp/terraform/issues/28531"&gt;OliverKoo&lt;/a&gt;, who started digging into that back in April'21. &lt;/p&gt;




&lt;div class="ltag__user ltag__user__id__51518"&gt;
    &lt;a href="/svasylenko" class="ltag__user__link profile-image-link"&gt;
      &lt;div class="ltag__user__pic"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--OaW58MNK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/practicaldev/image/fetch/s--6678KR6E--/c_fill%2Cf_auto%2Cfl_progressive%2Ch_150%2Cq_auto%2Cw_150/https://dev-to-uploads.s3.amazonaws.com/uploads/user/profile_image/51518/60259e60-86ea-47fe-92ff-5ac3f00835d2.jpg" alt="svasylenko image"&gt;
      &lt;/div&gt;
    &lt;/a&gt;
  &lt;div class="ltag__user__content"&gt;
    &lt;h2&gt;
&lt;a class="ltag__user__link" href="/svasylenko"&gt;Serhii Vasylenko&lt;/a&gt;Follow
&lt;/h2&gt;
    &lt;div class="ltag__user__summary"&gt;
      &lt;a class="ltag__user__link" href="/svasylenko"&gt;I am an engineer from Ukraine. I like astronomy and everything related to DevOps. I thrive on developing great product offerings, great people, and great teams.&lt;/a&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;



</description>
      <category>aws</category>
      <category>devops</category>
      <category>tutorial</category>
      <category>architecture</category>
    </item>
    <item>
      <title>AWS Resource Access Manager — Simple and powerful service for multi-account resource governance</title>
      <dc:creator>Serhii Vasylenko</dc:creator>
      <pubDate>Mon, 27 Sep 2021 11:16:27 +0000</pubDate>
      <link>https://dev.to/aws-builders/aws-resource-access-manager-simple-and-powerful-service-for-multi-account-resource-governance-11na</link>
      <guid>https://dev.to/aws-builders/aws-resource-access-manager-simple-and-powerful-service-for-multi-account-resource-governance-11na</guid>
      <description>&lt;p&gt;With a multi-account approach of building the infrastructure, there is always a challenge of provision and governance of the resources to subordinate accounts within the Organization. Provision resources, keep them up to date, and decommission them properly — that's only a part of them.&lt;/p&gt;

&lt;p&gt;AWS has numerous solutions that help make this process reliable and secure, and the Resource Access Manager (RAM) is one of them.&lt;/p&gt;

&lt;p&gt;In a nutshell, the RAM service allows you to share the AWS resources you create in one AWS account with other AWS accounts. These can be your organizations' accounts, organizational units (OU), or even third-party accounts.&lt;/p&gt;

&lt;p&gt;So let's see what the RAM is and review some of its usage examples. &lt;/p&gt;

&lt;h2&gt;
  
  
  Why using RAM
&lt;/h2&gt;

&lt;p&gt;There are several benefits of using the RAM service:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Reduced operational overhead&lt;/strong&gt;. Eliminate the need of provisioning the same kind of resource multiple times — RAM does that for you&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Simplified security management&lt;/strong&gt;. AWS RAM-managed permissions (at least one per resource type) define the actions that principals with access to the resources (i.e., resource users) can perform on those resources.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Consistent experience&lt;/strong&gt;. You share the resource in its state and with its security configuration with an arbitrary number of accounts. &lt;/p&gt;

&lt;p&gt;That plays incredibly well in the case of organization-wide sharing: new accounts get the resources automatically. And the shared resource itself looks like a native resource in the account that accepts your sharing.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Audit and visibility&lt;/strong&gt;. RAM integrates with the CloudWatch and CloudTrail.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  How to share a resource
&lt;/h2&gt;

&lt;p&gt;When you share a resource, the AWS account that owns that resource retains full ownership of the resource.&lt;/p&gt;

&lt;p&gt;Sharing of the resource doesn't change any permissions or quotas that apply to that resource. Also, you can share the resource only if you own it.&lt;/p&gt;

&lt;p&gt;Availability of the shared resources scopes to the Region: the users of your shared resources can access these resources only in the same Region where resources belong.&lt;/p&gt;

&lt;p&gt;Creation of resource share consists of three steps:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8v6wrva946qfzv7ybpvr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8v6wrva946qfzv7ybpvr.png" alt="AWS RAM diagram"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Specify the share name and the resource(s) you want to share. It can be either one resource type or several. You can also skip the resources selection and do that later. &lt;/p&gt;

&lt;p&gt;It's possible to modify the resource share later (e.g., you want to add some resources to the share).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Associate permissions with resource types you share. Some resources can have only one managed permission (will be attached automatically), and some can have multiple.&lt;/p&gt;

&lt;p&gt;You can check the Permissions Library in the AWS RAM Console to see what managed permissions are available.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Select who can use the resources you share: either external or Organization account or IAM role/user. If you share the resource with third parties, they will have to accept the sharing explicitly.&lt;/p&gt;

&lt;p&gt;Organization-wide resource share is accepted implicitly if resource sharing is enabled for the Organization.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Finally, review the summary page of the resource share and create it.&lt;/p&gt;

&lt;p&gt;Only specific actions are available to the users of shared resources. These actions mostly have the "read-only" nature and &lt;a href="https://docs.aws.amazon.com/ram/latest/userguide/shareable.html" rel="noopener noreferrer"&gt;vary by resource type&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Also, the RAM service is &lt;a href="https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ram_resource_share" rel="noopener noreferrer"&gt;supported by Terraform&lt;/a&gt;, so the resource sharing configuration may look like that, for example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_ram_resource_share"&lt;/span&gt; &lt;span class="s2"&gt;"example"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;                      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"example"&lt;/span&gt;
  &lt;span class="nx"&gt;allow_external_principals&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;

  &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;Environment&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Production"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_ram_resource_association"&lt;/span&gt; &lt;span class="s2"&gt;"example"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;resource_arn&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_subnet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arn&lt;/span&gt;
  &lt;span class="nx"&gt;resource_share_arn&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_ram_resource_share&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arn&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Example use cases
&lt;/h2&gt;

&lt;p&gt;One of the trivial but valuable examples of RAM service usage is sharing a Manged Prefix List.&lt;br&gt;
Suppose you have some service user across your Organization, a self-hosted VPN server, for example. And you have a static set of IPs for that VPN: you trust these IPs and would like them to be allow-listed in other services.&lt;/p&gt;

&lt;p&gt;How to report these IPs to all organization accounts/users? &lt;/p&gt;

&lt;p&gt;And if the IP set changes, how to announce that change, and what should be done to reflect that change in services that depend on it, for example, Security Groups?&lt;/p&gt;

&lt;p&gt;The answer is a shared &lt;a href="https://docs.aws.amazon.com/vpc/latest/userguide/managed-prefix-lists.html#managed-prefix-lists-concepts" rel="noopener noreferrer"&gt;Managed Prefix List&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You create the list once in the account and share it across your Organization. Other accounts automatically get access to that list and can reference the list in their Security Groups. And when the list entry is changed, they do not need to perform any actions: their Security Groups will get the updated IPs implicitly.&lt;/p&gt;

&lt;p&gt;Another everyday use case of RAM is the VPC sharing that can form the foundation of the &lt;a href="https://aws.amazon.com/blogs/networking-and-content-delivery/vpc-sharing-a-new-approach-to-multiple-accounts-and-vpc-management/" rel="noopener noreferrer"&gt;multi-account AWS architectures&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;Of course, the RAM service is not the only way to organize and centralize resource management in AWS. There are Service Catalog, Control Tower, Systems Manager, Config, and others. However, the RAM is relatively simple to adopt but is capable of providing worthy outcomes.&lt;/p&gt;

&lt;p&gt;I hope the article was informative and relevant to you!&lt;/p&gt;

&lt;p&gt;If you liked it, please support me by sharing this article on social networks 🙏&lt;/p&gt;

</description>
      <category>aws</category>
      <category>devops</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Run Ansible playbook on mac1.metal instances fleet with AWS Systems Manager</title>
      <dc:creator>Serhii Vasylenko</dc:creator>
      <pubDate>Thu, 27 May 2021 11:35:13 +0000</pubDate>
      <link>https://dev.to/aws-builders/run-ansible-playbook-on-mac1-metal-instances-fleet-with-aws-systems-manager-36j6</link>
      <guid>https://dev.to/aws-builders/run-ansible-playbook-on-mac1-metal-instances-fleet-with-aws-systems-manager-36j6</guid>
      <description>&lt;p&gt;In days of containers and serverless applications, Ansible looks not such a trendy thing.&lt;/p&gt;

&lt;p&gt;But still, there are cases when it helps, and there are cases when it combines very well with brand new product offerings, such as EC2 Mac instances.&lt;/p&gt;

&lt;p&gt;The more I use mac1.metal in AWS, the more I see that Ansible becomes a bedrock of software customization in my case.&lt;/p&gt;

&lt;p&gt;And when you have a large instances fleet, the &lt;a href="https://aws.amazon.com/systems-manager/"&gt;AWS Systems Manager&lt;/a&gt; becomes your best friend (the sooner you get along together, the better).&lt;/p&gt;

&lt;p&gt;So is it possible to use Ansible playbooks for mac1.metal on a big scale, with the help of AWS Systems Manager? &lt;/p&gt;

&lt;h2&gt;
  
  
  (Not) Available out of the box
&lt;/h2&gt;

&lt;p&gt;AWS Systems Manager (SSM hereafter) has a pre-defined, shared Document that allows running Ansible playbooks.&lt;br&gt;
It’s called “AWS-RunAnsiblePlaybook,” and you can find it in AWS SSM → Documents → Owned by Amazon.&lt;/p&gt;

&lt;p&gt;However, this Document is not quite “friendly” to macOS. When the SSM agent calls Ansible on the Mac EC2 instance, it does not recognize the Ansible installed with Homebrew (de-facto most used macOS package manager).&lt;/p&gt;

&lt;p&gt;So if you try to run a command on the mac1.metal instance using this Document, you will get the following error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Ansible is not installed. Please &lt;span class="nb"&gt;install &lt;/span&gt;Ansible and rerun the command.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The root cause is trivial: the path to Ansible binary is not present on the list of paths available to the SSM agent by default.&lt;/p&gt;

&lt;p&gt;There are several ways to solve that, but I believe that the most convenient one would be to create your custom Document — a slightly adjusted version of the default one provided by AWS.&lt;/p&gt;
&lt;h2&gt;
  
  
  Creating own SSM Document for Ansible installed with Homebrew
&lt;/h2&gt;

&lt;p&gt;All you need to do is clone the Document provided by AWS and change its code a little — replace the callouts of &lt;code&gt;ansible&lt;/code&gt; with the full path to the binary.&lt;/p&gt;

&lt;p&gt;Navigate to AWS SSM → Documents → Owned by Amazon and type &lt;code&gt;AWS-RunAnsiblePlaybook&lt;/code&gt; in the search field.&lt;/p&gt;

&lt;p&gt;Select the Document by pressing the circle on its top-right corner and then click Actions → Clone document.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2uYLnjK9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r00epai0mgtyjj4ecqrb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2uYLnjK9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r00epai0mgtyjj4ecqrb.png" alt="" width="800" height="477"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Give the new SSM Document a name, e.g., &lt;code&gt;macos-arbitrary-ansible-playbook&lt;/code&gt;, and change the &lt;code&gt;ansible&lt;/code&gt; callouts (at the end of the code) with the full path to the ansible symlink made by Homebrew which is &lt;code&gt;/usr/local/bin/ansible&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Here is the complete source code of the Document with adjusted Ansible path:&lt;/p&gt;

&lt;p&gt;
  Click to see the Document source code
  &lt;br&gt;

&lt;pre&gt;&lt;code&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;
  &lt;/span&gt;&lt;span&gt;"schemaVersion"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;"2.0"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;
  &lt;/span&gt;&lt;span&gt;"description"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;"Use this document to run arbitrary Ansible playbooks on macOS EC2 instances. Specify either YAML text or URL. If you specify both, the URL parameter will be used. Use the extravar parameter to send runtime variables to the Ansible execution. Use the check parameter to perform a dry run of the Ansible execution. The output of the dry run shows the changes that will be made when the playbook is executed."&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;
  &lt;/span&gt;&lt;span&gt;"parameters"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;
    &lt;/span&gt;&lt;span&gt;"playbook"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;
      &lt;/span&gt;&lt;span&gt;"type"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;"String"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;
      &lt;/span&gt;&lt;span&gt;"description"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;"(Optional) If you don't specify a URL, then you must specify playbook YAML in this field."&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;
      &lt;/span&gt;&lt;span&gt;"default"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;""&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;
      &lt;/span&gt;&lt;span&gt;"displayType"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;"textarea"&lt;/span&gt;&lt;span&gt;
    &lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;span&gt;
    &lt;/span&gt;&lt;span&gt;"playbookurl"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;
      &lt;/span&gt;&lt;span&gt;"type"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;"String"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;
      &lt;/span&gt;&lt;span&gt;"description"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;"(Optional) If you don't specify playbook YAML, then you must specify a URL where the playbook is stored. You can specify the URL in the following formats: http://example.com/playbook.yml or s3://examplebucket/plabook.url. For security reasons, you can't specify a URL with quotes."&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;
      &lt;/span&gt;&lt;span&gt;"default"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;""&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;
      &lt;/span&gt;&lt;span&gt;"allowedPattern"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;"^&lt;/span&gt;&lt;span&gt;\\&lt;/span&gt;&lt;span&gt;s*$|^(http|https|s3)://[^']*$"&lt;/span&gt;&lt;span&gt;
    &lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;span&gt;
    &lt;/span&gt;&lt;span&gt;"extravars"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;
      &lt;/span&gt;&lt;span&gt;"type"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;"String"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;
      &lt;/span&gt;&lt;span&gt;"description"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;"(Optional) Additional variables to pass to Ansible at runtime. Enter a space separated list of key/value pairs. For example: color=red or fruits=[apples,pears]"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;
      &lt;/span&gt;&lt;span&gt;"default"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;"foo=bar"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;
      &lt;/span&gt;&lt;span&gt;"displayType"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;"textarea"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;
      &lt;/span&gt;&lt;span&gt;"allowedPattern"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;"^((^|&lt;/span&gt;&lt;span&gt;\\&lt;/span&gt;&lt;span&gt;s)&lt;/span&gt;&lt;span&gt;\\&lt;/span&gt;&lt;span&gt;w+=(&lt;/span&gt;&lt;span&gt;\\&lt;/span&gt;&lt;span&gt;S+|'.*'))*$"&lt;/span&gt;&lt;span&gt;
    &lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;span&gt;
    &lt;/span&gt;&lt;span&gt;"check"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;
      &lt;/span&gt;&lt;span&gt;"type"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;"String"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;
      &lt;/span&gt;&lt;span&gt;"description"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;" (Optional) Use the check parameter to perform a dry run of the Ansible execution."&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;
      &lt;/span&gt;&lt;span&gt;"allowedValues"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;
        &lt;/span&gt;&lt;span&gt;"True"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;
        &lt;/span&gt;&lt;span&gt;"False"&lt;/span&gt;&lt;span&gt;
      &lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;span&gt;
      &lt;/span&gt;&lt;span&gt;"default"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;"False"&lt;/span&gt;&lt;span&gt;
    &lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;span&gt;
    &lt;/span&gt;&lt;span&gt;"timeoutSeconds"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;
      &lt;/span&gt;&lt;span&gt;"type"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;"String"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;
      &lt;/span&gt;&lt;span&gt;"description"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;"(Optional) The time in seconds for a command to be completed before it is considered to have failed."&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;
      &lt;/span&gt;&lt;span&gt;"default"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;"3600"&lt;/span&gt;&lt;span&gt;
    &lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;
  &lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;span&gt;
  &lt;/span&gt;&lt;span&gt;"mainSteps"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;
    &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;
      &lt;/span&gt;&lt;span&gt;"action"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;"aws:runShellScript"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;
      &lt;/span&gt;&lt;span&gt;"name"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;"runShellScript"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;
      &lt;/span&gt;&lt;span&gt;"inputs"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;
        &lt;/span&gt;&lt;span&gt;"timeoutSeconds"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;""&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;
        &lt;/span&gt;&lt;span&gt;"runCommand"&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;
          &lt;/span&gt;&lt;span&gt;"#!/bin/bash"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;
          &lt;/span&gt;&lt;span&gt;"/usr/local/bin/ansible --version"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;
          &lt;/span&gt;&lt;span&gt;"if [$? -ne 0]; then"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;
          &lt;/span&gt;&lt;span&gt;" echo &lt;/span&gt;&lt;span&gt;\"&lt;/span&gt;&lt;span&gt;Ansible is not installed. Please install Ansible and rerun the command&lt;/span&gt;&lt;span&gt;\"&lt;/span&gt;&lt;span&gt; &amp;gt;&amp;amp;2"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;
          &lt;/span&gt;&lt;span&gt;" exit 1"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;
          &lt;/span&gt;&lt;span&gt;"fi"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;
          &lt;/span&gt;&lt;span&gt;"execdir=$(dirname $0)"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;
          &lt;/span&gt;&lt;span&gt;"cd $execdir"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;
          &lt;/span&gt;&lt;span&gt;"if [-z ''] ; then"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;
          &lt;/span&gt;&lt;span&gt;" if [[&lt;/span&gt;&lt;span&gt;\"\"&lt;/span&gt;&lt;span&gt; == http*]]; then"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;
          &lt;/span&gt;&lt;span&gt;" wget '' -O playbook.yml"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;
          &lt;/span&gt;&lt;span&gt;" if [$? -ne 0]; then"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;
          &lt;/span&gt;&lt;span&gt;" echo &lt;/span&gt;&lt;span&gt;\"&lt;/span&gt;&lt;span&gt;There was a problem downloading the playbook. Make sure the URL is correct and that the playbook exists.&lt;/span&gt;&lt;span&gt;\"&lt;/span&gt;&lt;span&gt; &amp;gt;&amp;amp;2"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;
          &lt;/span&gt;&lt;span&gt;" exit 1"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;
          &lt;/span&gt;&lt;span&gt;" fi"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;
          &lt;/span&gt;&lt;span&gt;" elif [[&lt;/span&gt;&lt;span&gt;\"\"&lt;/span&gt;&lt;span&gt; == s3*]] ; then"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;
          &lt;/span&gt;&lt;span&gt;" aws --version"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;
          &lt;/span&gt;&lt;span&gt;" if [$? -ne 0]; then"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;
          &lt;/span&gt;&lt;span&gt;" echo &lt;/span&gt;&lt;span&gt;\"&lt;/span&gt;&lt;span&gt;The AWS CLI is not installed. The CLI is required to process Amazon S3 URLs. Install the AWS CLI and run the command again.&lt;/span&gt;&lt;span&gt;\"&lt;/span&gt;&lt;span&gt; &amp;gt;&amp;amp;2"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;
          &lt;/span&gt;&lt;span&gt;" exit 1"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;
          &lt;/span&gt;&lt;span&gt;" fi"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;
          &lt;/span&gt;&lt;span&gt;" aws s3 cp '' playbook.yml"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;
          &lt;/span&gt;&lt;span&gt;" if [$? -ne 0]; then"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;
          &lt;/span&gt;&lt;span&gt;" echo &lt;/span&gt;&lt;span&gt;\"&lt;/span&gt;&lt;span&gt;Error while downloading the document from S3&lt;/span&gt;&lt;span&gt;\"&lt;/span&gt;&lt;span&gt; &amp;gt;&amp;amp;2"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;
          &lt;/span&gt;&lt;span&gt;" exit 1"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;
          &lt;/span&gt;&lt;span&gt;" fi"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;
          &lt;/span&gt;&lt;span&gt;" else"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;
          &lt;/span&gt;&lt;span&gt;" echo &lt;/span&gt;&lt;span&gt;\"&lt;/span&gt;&lt;span&gt;The playbook URL is not valid. Verify the URL and try again.&lt;/span&gt;&lt;span&gt;\"&lt;/span&gt;&lt;span&gt;"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;
          &lt;/span&gt;&lt;span&gt;" fi"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;
          &lt;/span&gt;&lt;span&gt;"else"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;
          &lt;/span&gt;&lt;span&gt;" echo '' &amp;gt; playbook.yml"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;
          &lt;/span&gt;&lt;span&gt;"fi"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;
          &lt;/span&gt;&lt;span&gt;"if [[&lt;/span&gt;&lt;span&gt;\"\"&lt;/span&gt;&lt;span&gt; == True]] ; then"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;
          &lt;/span&gt;&lt;span&gt;" /usr/local/bin/ansible-playbook -i &lt;/span&gt;&lt;span&gt;\"&lt;/span&gt;&lt;span&gt;localhost,&lt;/span&gt;&lt;span&gt;\"&lt;/span&gt;&lt;span&gt; --check -c local -e &lt;/span&gt;&lt;span&gt;\"\"&lt;/span&gt;&lt;span&gt; playbook.yml"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;
          &lt;/span&gt;&lt;span&gt;"else"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;
          &lt;/span&gt;&lt;span&gt;" /usr/local/bin/ansible-playbook -i &lt;/span&gt;&lt;span&gt;\"&lt;/span&gt;&lt;span&gt;localhost,&lt;/span&gt;&lt;span&gt;\"&lt;/span&gt;&lt;span&gt; -c local -e &lt;/span&gt;&lt;span&gt;\"\"&lt;/span&gt;&lt;span&gt; playbook.yml"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;
          &lt;/span&gt;&lt;span&gt;"fi"&lt;/span&gt;&lt;span&gt;
        &lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;
      &lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;
    &lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;
  &lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;

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




&lt;/p&gt;
&lt;h2&gt;
  
  
  Applying Ansible playbook to the fleet of mac1.metal
&lt;/h2&gt;

&lt;p&gt;Let’s give our new SSM Document a try! (I suppose you have at least one mac1 instance running, right?)&lt;/p&gt;

&lt;p&gt;In AWS SSM, go to the Run Command feature, then click on the Run Command button.&lt;/p&gt;

&lt;p&gt;On the new panel, type the name of your Document (&lt;code&gt;macos-arbitrary-ansible-playbook&lt;/code&gt; in this example) in the search field and press enter.&lt;/p&gt;

&lt;p&gt;Select the Document, and you’ll see its parameters and settings.&lt;/p&gt;

&lt;p&gt;The rest is self-explanatory. Enter either a playbook code or a link to the source file, add extra variables if needed, and select the target host or a filtered bunch (I like that feature with tags filtering!). Finally, click on the “Run” orange button to apply your playbook.&lt;/p&gt;

&lt;p&gt;That’s it! Now you can make all your ansible-playbook dreams come true! 😁&lt;/p&gt;




&lt;div class="ltag__user ltag__user__id__51518"&gt;
    &lt;a href="/svasylenko" class="ltag__user__link profile-image-link"&gt;
      &lt;div class="ltag__user__pic"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--OaW58MNK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/practicaldev/image/fetch/s--6678KR6E--/c_fill%2Cf_auto%2Cfl_progressive%2Ch_150%2Cq_auto%2Cw_150/https://dev-to-uploads.s3.amazonaws.com/uploads/user/profile_image/51518/60259e60-86ea-47fe-92ff-5ac3f00835d2.jpg" alt="svasylenko image"&gt;
      &lt;/div&gt;
    &lt;/a&gt;
  &lt;div class="ltag__user__content"&gt;
    &lt;h2&gt;
&lt;a class="ltag__user__link" href="/svasylenko"&gt;Serhii Vasylenko&lt;/a&gt;Follow
&lt;/h2&gt;
    &lt;div class="ltag__user__summary"&gt;
      &lt;a class="ltag__user__link" href="/svasylenko"&gt;I am an engineer from Ukraine. I like astronomy and everything related to DevOps. I thrive on developing great product offerings, great people, and great teams.&lt;/a&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;



</description>
      <category>aws</category>
      <category>ansible</category>
      <category>macos</category>
      <category>devops</category>
    </item>
    <item>
      <title>Configure HTTP Security headers with CloudFront Functions</title>
      <dc:creator>Serhii Vasylenko</dc:creator>
      <pubDate>Sat, 22 May 2021 01:57:23 +0000</pubDate>
      <link>https://dev.to/aws-builders/configure-http-security-headers-with-cloudfront-functions-1iid</link>
      <guid>https://dev.to/aws-builders/configure-http-security-headers-with-cloudfront-functions-1iid</guid>
      <description>&lt;p&gt;&lt;strong&gt;UPDATE November 5th, 2021&lt;/strong&gt; &lt;br&gt;
There is a new, native way of doing that. Read more here:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag__link"&gt;
  &lt;a href="/aws-builders" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__org__pic"&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F2794%2F88da75b6-aadd-4ea1-8083-ae2dfca8be94.png" alt="AWS Community Builders "&gt;
      &lt;div class="ltag__link__user__pic"&gt;
        &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F51518%2F60259e60-86ea-47fe-92ff-5ac3f00835d2.jpg" alt=""&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/aws-builders/apply-cloudfront-security-headers-policy-with-terraform-fd3" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Apply Cloudfront Security Headers Policy With Terraform&lt;/h2&gt;
      &lt;h3&gt;Serhii Vasylenko for AWS Community Builders  ・ Nov 5 '21&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#aws&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#terraform&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#devops&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#architecture&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;





&lt;p&gt;A couple of weeks ago, AWS released CloudFront Functions — a “true edge” compute capability for the CloudFront.&lt;/p&gt;

&lt;p&gt;It is “true edge” because Functions work on 200+ edge locations (&lt;a href="https://aws.amazon.com/cloudfront/features/?whats-new-cloudfront.sort-by=item.additionalFields.postDateTime&amp;amp;whats-new-cloudfront.sort-order=desc#Edge_Computing" rel="noopener noreferrer"&gt;link to doc&lt;/a&gt;) while its predecessor, the Lambda@Edge, runs on a small number of regional edge caches.&lt;/p&gt;

&lt;p&gt;One of the use cases for Lambda@Edge was adding security HTTP headers (it’s even listed on the &lt;a href="https://aws.amazon.com/lambda/edge/" rel="noopener noreferrer"&gt;product page&lt;/a&gt;), and now there is one more way to make it using CloudFront Functions. &lt;/p&gt;

&lt;h2&gt;
  
  
  What are security headers, and why it matters
&lt;/h2&gt;

&lt;p&gt;Security Headers are one of the web security pillars.&lt;/p&gt;

&lt;p&gt;They specify security-related information of communication between a web application (i.e., website) and a client (i.e., browser) and protect the web app from different types of attacks. Also, HIPAA and PCI, and other security standard certifications generally include these headers in their rankings. &lt;/p&gt;

&lt;p&gt;We will use CloudFront Functions to set the following headers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt; &lt;a href="https://infosec.mozilla.org/guidelines/web_security#content-security-policy" rel="noopener noreferrer"&gt;Content Security Policy&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt; &lt;a href="https://infosec.mozilla.org/guidelines/web_security#http-strict-transport-security" rel="noopener noreferrer"&gt;Strict Transport Security&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt; &lt;a href="https://infosec.mozilla.org/guidelines/web_security#x-content-type-options" rel="noopener noreferrer"&gt;X-Content-Type-Options&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt; &lt;a href="https://infosec.mozilla.org/guidelines/web_security#x-xss-protection" rel="noopener noreferrer"&gt;X-XSS-Protection&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt; &lt;a href="https://infosec.mozilla.org/guidelines/web_security#x-frame-options" rel="noopener noreferrer"&gt;X-Frame-Options&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt; &lt;a href="https://infosec.mozilla.org/guidelines/web_security#referrer-policy" rel="noopener noreferrer"&gt;Referrer Policy&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can find a short and detailed explanation for each security header on &lt;a href="https://infosec.mozilla.org/guidelines/web_security" rel="noopener noreferrer"&gt;Web Security cheatsheet made by Mozilla&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  CloudFront Functions overview
&lt;/h2&gt;

&lt;p&gt;In a nutshell, CloudFront Functions allow performing simple actions against HTTP(s) request (from the client) and response (from the CloudFront cache at the edge). Functions take less than one millisecond to execute, support JavaScript (ECMAScript 5.1 compliant), and cost $0.10 per 1 million invocations.&lt;/p&gt;

&lt;p&gt;Every CloudFront distribution has one (default) or more Cache behaviors, and Functions can be associated with these behaviors to execute upon a specific event.&lt;/p&gt;

&lt;p&gt;That is how the request flow looks like in general, and here is where CloudFront Functions execution happens:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbq4tww9uugndo1xlm2p0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbq4tww9uugndo1xlm2p0.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;CloudFront Functions support Viewer Request (after CloudFront receives a request from a client) and Viewer Response (before CloudFront forwards the response to the client) events.&lt;/p&gt;

&lt;p&gt;You can read more about the events types and their properties here — &lt;a href="https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/lambda-cloudfront-trigger-events.html" rel="noopener noreferrer"&gt;CloudFront Events That Can Trigger a Lambda Function - Amazon CloudFront&lt;/a&gt;.  &lt;/p&gt;

&lt;p&gt;Also, the CloudFront Functions allow you to manage and operate the code and lifecycle of the functions directly from the CloudFront web interface.&lt;/p&gt;

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

&lt;p&gt;CloudFront distribution should exist before Function creation so you could associate the Function with the distribution.&lt;/p&gt;

&lt;p&gt;Creation and configuration of the CloudFront Function consist of the following steps:&lt;/p&gt;

&lt;h3&gt;
  
  
  Create Function
&lt;/h3&gt;

&lt;p&gt;In the AWS Console, open CloudFront service and lick on the Functions on the left navigation bar, then click Create function button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmpouyjgs73bg2i04keaj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmpouyjgs73bg2i04keaj.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Enter the name of your Function (e.g., “security-headers”) and click Continue.&lt;/p&gt;

&lt;h3&gt;
  
  
  Build Function
&lt;/h3&gt;

&lt;p&gt;On the function settings page, you will see four tabs with the four lifecycle steps: Build, Test, Publish, Associate.&lt;/p&gt;

&lt;p&gt;Paste the function code into the editor and click “Save.”&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F984a3sokw5ndsl8j51r5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F984a3sokw5ndsl8j51r5.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here is the source code of the function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;strict-transport-security&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;max-age=63072000; includeSubdomains; preload&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt; 
&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;content-security-policy&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;default-src 'none'; img-src 'self'; script-src 'self'; style-src 'self'; object-src 'none'; frame-ancestors 'none'&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt; 
&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;x-content-type-options&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;nosniff&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt; 
&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;x-xss-protection&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1; mode=block&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;referrer-policy&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;same-origin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;x-frame-options&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;DENY&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Test Function
&lt;/h4&gt;

&lt;p&gt;Open the “Test” tab — let’s try our function first before it becomes live!&lt;/p&gt;

&lt;p&gt;Select Viewer Response event type and Development Stage, then select “Viewer response with headers” as a Sample test event (you will get a simple set of headers automatically).&lt;/p&gt;

&lt;p&gt;Now click the blue “Test” button and observe the output results:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Compute utilization represents the relative amount of time (on a scale between 0 and 100) your function took to run&lt;/li&gt;
&lt;li&gt;Check the Response headers tab and take a look at how the function added custom headers.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fohftop75g5jg4sth18tj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fohftop75g5jg4sth18tj.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Publish Function
&lt;/h3&gt;

&lt;p&gt;Let’s publish our function. To do that, open the Publish tab and click on the blue button “Publish and update.”&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frtg9v3uz8udlelb44ic0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frtg9v3uz8udlelb44ic0.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Associate your Function with CloudFront distribution
&lt;/h3&gt;

&lt;p&gt;Now, you can associate the function with the CloudFront distribution.&lt;/p&gt;

&lt;p&gt;To do so, open the Associate tab, select the distribution and event type (Viewer Response), and select the Cache behavior of your distribution which you want to use for the association.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsy4o019r7ujn9384ue1h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsy4o019r7ujn9384ue1h.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once you associate the function with the CloudFront distribution, you can test it in live mode.&lt;/p&gt;

&lt;p&gt;I will use curl here to demonstrate it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; curl &lt;span class="nt"&gt;-i&lt;/span&gt; https://d30i87a4ss9ifz.cloudfront.net
HTTP/2 200
content-type: text/html
content-length: 140
&lt;span class="nb"&gt;date&lt;/span&gt;: Sat, 22 May 2021 00:22:18 GMT
last-modified: Tue, 27 Apr 2021 23:07:14 GMT
etag: &lt;span class="s2"&gt;"a855a3189f8223db53df8a0ca362dd62"&lt;/span&gt;
accept-ranges: bytes
server: AmazonS3
via: 1.1 50f21cb925e6471490e080147e252d7d.cloudfront.net &lt;span class="o"&gt;(&lt;/span&gt;CloudFront&lt;span class="o"&gt;)&lt;/span&gt;
content-security-policy: default-src &lt;span class="s1"&gt;'none'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; img-src &lt;span class="s1"&gt;'self'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; script-src &lt;span class="s1"&gt;'self'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; style-src &lt;span class="s1"&gt;'self'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; object-src &lt;span class="s1"&gt;'none'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; frame-ancestors &lt;span class="s1"&gt;'none'&lt;/span&gt;
strict-transport-security: max-age&lt;span class="o"&gt;=&lt;/span&gt;63072000&lt;span class="p"&gt;;&lt;/span&gt; includeSubdomains&lt;span class="p"&gt;;&lt;/span&gt; preload
x-xss-protection: 1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;mode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;block
x-frame-options: DENY
referrer-policy: same-origin
x-content-type-options: nosniff
x-cache: Miss from cloudfront
x-amz-cf-pop: WAW50-C1
x-amz-cf-id: ud3qH8rLs7QmbhUZ-DeupGwFhWLpKDSD59vr7uWC65Hui5m2U8o2mw&lt;span class="o"&gt;==&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also test your results here — &lt;a href="https://observatory.mozilla.org/" rel="noopener noreferrer"&gt;Mozilla Observatory&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs8wif4x6xc12jyfjjn5m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs8wif4x6xc12jyfjjn5m.png"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F28mwl8axanrjmt3cnp7q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F28mwl8axanrjmt3cnp7q.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Read more
&lt;/h2&gt;

&lt;p&gt;That was a simplified overview of the CloudFront Functions capabilities.&lt;/p&gt;

&lt;p&gt;But if you want to get deeper, here is a couple of useful links to start:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Another overview from AWS — &lt;a href="https://aws.amazon.com/blogs/aws/introducing-cloudfront-functions-run-your-code-at-the-edge-with-low-latency-at-any-scale" rel="noopener noreferrer"&gt;CloudFront Functions Launch Blog&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;More about creating, testing, updating and publishing of CloudFront Functions — &lt;a href="https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/managing-functions.html" rel="noopener noreferrer"&gt;Managing functions in CloudFront Functions - Amazon CloudFront&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  So what to choose?
&lt;/h2&gt;

&lt;p&gt;CloudFront Functions are simpler than Lambda@Edge and run faster with minimal latency and minimal time penalty for your web clients.&lt;/p&gt;

&lt;p&gt;Lambda@Edge takes more time to invoke, but it can run upon Origin Response event so that CloudFront can cache the processed response (including headers) and return it faster afterward.&lt;/p&gt;

&lt;p&gt;But again, the CloudFront Functions invocations are much cheaper (6x times) than Lambda@Edge, and you do not pay for the function execution duration.&lt;/p&gt;

&lt;p&gt;The final decision would also depend on the dynamic/static nature of the content you have at your origin.&lt;/p&gt;

&lt;p&gt;To make a wise and deliberate decision, try to analyze your use case using these two documentation articles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/edge-functions.html" rel="noopener noreferrer"&gt;Choosing between CloudFront Functions and Lambda@Edge&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/lambda-how-to-choose-event.html" rel="noopener noreferrer"&gt;How to Decide Which CloudFront Event to Use to Trigger a Lambda Function&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>aws</category>
      <category>cloudfront</category>
      <category>security</category>
      <category>serverless</category>
    </item>
  </channel>
</rss>
