Terraform Cookbook

Recipes for Codifying Infrastructure

With Early Release ebooks, you get books in their earliest form—the author’s raw and unedited content as they write—so you can take advantage of these technologies long before the official release of these titles.

Kerim Satirli and Taylor Dolezal

Terraform Cookbook

by Kerim Satirli and Taylor Dolezal

Copyright © 2023 O’Reilly Media. All rights reserved.

Printed in the United States of America.

Published by O’Reilly Media, Inc., 1005 Gravenstein Highway North, Sebastopol, CA 95472.

O’Reilly books may be purchased for educational, business, or sales promotional use. Online editions are also available for most titles (http://oreilly.com). For more information, contact our corporate/institutional sales department: 800-998-9938 or corporate@oreilly.com.

Editors: Michele Cronin and John Devins

Production Editor: Beth Kelly

Copyeditor: FILL IN COPYEDITOR

Proofreader: FILL IN PROOFREADER

Indexer: FILL IN INDEXER

Interior Designer: David Futato

Cover Designer: Karen Montgomery

Illustrator: Kate Dullea

September 2022: First Edition

Revision History for the Early Release

2022-06-22: First Release

2022-10-17: Second Release

2023-11-09: Third Release

See http://oreilly.com/catalog/errata.csp?isbn=9781098108465 for release details.

The O’Reilly logo is a registered trademark of O’Reilly Media, Inc. Terraform Cookbook, the cover image, and related trade dress are trademarks of O’Reilly Media, Inc.

The views expressed in this work are those of the authors, and do not represent the publisher’s views. While the publisher and the authors have used good faith efforts to ensure that the information and instructions contained in this work are accurate, the publisher and the authors disclaim all responsibility for errors or omissions, including without limitation responsibility for damages resulting from the use of or reliance on this work. Use of the information and instructions contained in this work is at your own risk. If any code samples or other technology this work contains or describes is subject to open source licenses or the intellectual property rights of others, it is your responsibility to ensure that your use thereof complies with such licenses and/or rights.

978-1-098-10839-7

[FILL IN]

Chapter 1. Getting Started with Terraform

A Note for Early Release Readers

With Early Release ebooks, you get books in their earliest form—the author’s raw and unedited content as they write—so you can take advantage of these technologies long before the official release of these titles.

This will be the 1st chapter of the final book. Please note that the GitHub repo will be made active later on.

If you have comments about how we might improve the content and/or examples in this book, or if you notice missing material within this chapter, please reach out to the editor at mcronin@oreilly.com.

Terraform is an open-source tool created by HashiCorp that allows you to manage your infrastructure as code. It provides a simple and consistent way to define, provision, and manage resources across cloud platforms and on-premise environments.

With Terraform, you can describe your infrastructure in a declarative language called the HashiCorp Configuration Language (HCL). This allows you to specify the desired state of your infrastructure rather than having to script the steps to get there. Terraform then applies this configuration to create, modify, or delete resources as necessary to achieve the desired state.

The real power of Terraform is in its ability to manage complex infrastructure. You can create reusable modules that can be shared and applied to different projects, making it easy to maintain consistency across multiple environments. Additionally, Terraform’s state management system ensures that the current state of your infrastructure is tracked and can be used to update, destroy, or recreate resources as needed.

One of the significant benefits of using Terraform is its support for a wide range of cloud providers, such as Amazon Web Services (AWS), Google Cloud Platform (GCP), and Microsoft Azure. This allows you to manage your infrastructure across different providers, avoiding vendor lock-in and providing greater flexibility.

Terraform is a powerful tool that allows you to manage infrastructure as code, making it easier to deploy, maintain, and scale your applications. With Terraform, you can describe your infrastructure using a simple and consistent language, reuse modules, and manage resources across different providers.

1.1 1.1 When to Use Terraform

Terraform is an excellent choice when managing complex infrastructure composed of many resources. It’s beneficial when managing infrastructure that spans multiple cloud providers or on-premises environments. Here are some examples of when to use Terraform:

Managing multiple environments: Terraform excels at managing multiple environments, such as development, staging, and production. With Terraform, you can define your infrastructure as code, making it easy to deploy and manage across environments.

Managing complex infrastructure: Terraform is particularly useful for managing complex infrastructure composed of many resources. With Terraform, you can define your infrastructure in a declarative language, creating, modifying, and deleting resources.

Managing infrastructure across cloud providers: Terraform is an excellent choice for managing infrastructure across different cloud providers. With Terraform, you can consistently define your infrastructure, regardless of your cloud provider.

Automating infrastructure deployment: Terraform is an excellent choice for automating infrastructure deployment. With Terraform, you can define your infrastructure as code and use automation tools, such as CI/CD pipelines, to deploy your infrastructure automatically.

1.2 1.2 When Not to Use Terraform

While Terraform is a powerful tool, some jobs have better choices. Here are some examples of when you might want to consider other tools:

Single-server infrastructure: If you manage a single server, you might not need Terraform. Simple configuration management tools like Ansible or Puppet might be a better choice.

Infrastructure as a Service (IaaS): If you only use a single cloud provider and manage only a small amount of infrastructure, you might not need Terraform. Many cloud providers offer tools for managing infrastructure, such as AWS CloudFormation or Azure Resource Manager.

Platform as a Service (PaaS): If you use a service like Netlify or Google App Engine, you might not need Terraform. These providers typically manage the infrastructure for you, so you only need to worry about deploying your application.

When deciding whether to use Terraform, consider the complexity of your infrastructure and whether you need to manage infrastructure across multiple providers. Consider other tools if you manage only a small amount of infrastructure or use only a single cloud provider.

1.3 1.3 Installing and Configuring Terraform

Problem

You need to install Terraform on your local machine or a remote server.

Solution

Use the appropriate installation method for your operating system or package manager.

Here are the steps for installing Terraform on some popular operating systems:

Installing Terraform on Linux

Download the latest Terraform package for Linux from the official Terraform website.

Extract the downloaded package to a directory on your system. For example, sudo unzip terraform_1.4.0_linux_amd64.zip -d /usr/local/bin/.

Make the Terraform binary executable by running: sudo chmod +x /usr/local/bin/terraform.

Verify the installation by running terraform version.

Installing Terraform on macOS

Install Terraform using Homebrew by running: brew install terraform.

Verify the installation by running terraform version.

Installing Terraform on Windows

Download the latest Terraform package for Windows from the official Terraform website.

Extract the downloaded package to a directory on your system.

Add the directory containing the Terraform executable to your system’s PATH environment variable.

Verify the installation by running terraform version.

Discussion

Terraform is available for various operating systems and package managers, making it easy to install and use on almost any system. The installation process for Terraform is generally straightforward and can be completed in just a few steps.

When installing Terraform, it’s important to download the appropriate package for your operating system and architecture. The Terraform website provides packages for Linux, macOS, and Windows and binary packages for other operating systems.

Once you’ve downloaded the package, you’ll need to extract it to a directory on your system and make the Terraform binary executable. On Linux and macOS, this can be done using the chmod command. On Windows, you’ll need to add the directory containing the Terraform executable to your system’s PATH environment variable.

After you’ve installed Terraform, you can verify the installation by running the terraform version command. This should display the version of Terraform that you’ve installed and other information about your system and configuration.

In summary, installing Terraform is a simple process that can be completed in just a few steps. Using the appropriate installation method for your operating system or package manager, you can quickly install Terraform and use it to manage your infrastructure as code.

1.4 1.4 Understanding Terraform Providers

Problem

You want to start using Terraform providers in your infrastructure-as-code (IaC) projects, but you don’t know where to begin or how to configure your providers properly.

Solution

To start using Terraform providers, you need to follow these steps:

Choose the provider(s) you want to use. Searching the Terraform Registry is a great place to see all your available provider options.

Add the provider(s) to your Terraform configuration file (ex. main.tf)

Configure the provider(s) with required settings, such as API keys or authentication tokens.

Use the provider(s) in your Terraform resources.

Here’s an example of how to add and configure the aws provider:

# Step 1: Choose the provider (AWS)
provider "aws" {}
# Step 2: Configure the provider with any required settings
provider "aws" {
  region     = "us-west-2"
  access_key = "YOUR_ACCESS_KEY"
  secret_key = "YOUR_SECRET_KEY"
}
# Step 3: Use the provider to create resources
Provider "aws" {
  …
}

resource "aws_instance" "example" {
  ami           = "ami-0c55b159cbfafe1f0"
  instance_type = "t2.micro"
}

In this example, we’ve chosen the aws provider and added it to our configuration file using the provider block. We’ve configured the provider with a region setting and provided AWS access_key and secret_key in the provider block.

We can then use the aws_instance resource to create an EC2 instance, specifying the ami and instance_type settings.

Discussion

Terraform providers are plugins that allow Terraform to interact with different cloud services or other external APIs. The community and the Terraform team maintain providers and can be found in the Terraform Registry.

To choose a provider, visit the Terraform Registry and find the provider you want to use. Each provider has a page with installation instructions, usage examples, and a list of available resources.

You’ll need a provider block to add a provider to your Terraform configuration file. This block tells Terraform which provider to use and how to configure it.

The provider block specifies the provider type (in this case, aws) and any provider-specific settings (in this case, the region, secret_key, and access_key variables). You can add as many distinct provider blocks as needed in your configuration file (ex., aws, kubernetes, helm, etc.).

Some providers require configuring additional settings, such as API keys or authentication tokens. These settings can be specified in the provider block.

Once you’ve added and configured your provider(s), you can use them in your Terraform resources. Each provider has a set of resources that you can use to create and manage infrastructure.

The aws provider provides resources for creating EC2 instances, security groups, and many other AWS resources. We created an aws_instance resource with the identifier example in this example. We’ve specified the ami and instance_type settings for the instance, though many more arguments can be specified and set for the aws_instance resource.

You can download the necessary provider plugins and initialize your configuration using the terraform init command. Once your configuration is initialized, you can use the terraform plan and terraform apply commands to create or modify your infrastructure.

You now know how to start using Terraform providers in your infrastructure-as-code projects. Happy provisioning!

1.5 1.5 Understanding Terraform Modules

Problem

You need to create a new Amazon Web Services (AWS) Virtual Private Cloud (VPC) with a specific Classless Inter-Domain Routing (CIDR) block, subnets, and internet gateway, but you don’t want to start creating this from scratch.

Solution

In this solution, we leverage a pre-built Terraform module to effortlessly provision a VPC along with its associated resources such as subnets and an internet gateway. This approach saves time and ensures conformity to best practices.

module "vpc" {
  source  = "terraform-aws-modules/vpc/aws"
  version = "3.19.0"
  name = "my-vpc"
  cidr = "10.0.0.0/16"
  azs             = ["us-west-2a", "us-west-2b", "us-west-2c"]
  private_subnets = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"]
  public_subnets  = ["10.0.101.0/24", "10.0.102.0/24", "10.0.103.0/24"]
  enable_nat_gateway = true
  tags = {
    Terraform   = "true"
    Environment = "dev"
  }
}

Discussion

We used a Terraform module called vpc, sourced from the official terraform-aws-modules/vpc/aws module. The module takes several inputs, including the VPC’s name, CIDR block, availability zones, subnets, NAT gateway, and tags.

The name input specifies the name of the VPC, while the cidr input specifies the CIDR block to use for the VPC. We’ve chosen a CIDR block of 10.0.0.0/16 in this example.

The azs input specifies the availability zones to use for the VPC. In this example, we’ve chosen three availability zones in the US West (Oregon) region.

The private_subnets and public_subnets inputs specify the CIDR blocks for the VPC’s private and public subnets, respectively. In this example, we’ve chosen three private and three public subnets.

The enable_nat_gateway input specifies whether or not to create a NAT gateway for the VPC. In this example, we’ve set this input to true.

Finally, the tags input specifies any additional tags to apply to the VPC resources.

Once you’ve defined the vpc module in your Terraform configuration, you can apply it by running terraform apply. Terraform will then create the VPC, subnets, and other resources defined in the module.

1.6 1.6 Modifying Terraform State with terraform console

Problem

You need to modify Terraform state manually for testing or debugging purposes.

Solution

Navigate to the directory containing your Terraform configuration files.

Run the command terraform console.

The terraform console prompt will appear.

Enter the following command to load the current Terraform state into the console: state.<module-name>.<resource-name>.<attribute-name>, where <module-name>, <resource-name>, or <attribute-name> are the names of the module, resource, and attribute you want to view.

The current value of the attribute will be displayed.

Discussion

terraform console is a powerful tool that allows you to modify Terraform state using HCL interactively. This is useful for testing or debugging, as it will enable you to introspect your Terraform’s state without modifying the Terraform configuration files.

To use terraform console, navigate to the directory containing your Terraform configuration files and run the command terraform console. This will open an interactive console that allows you to execute HCL expressions.

To modify a specific attribute in your Terraform state, you need to load the attribute into the console using the state.<module-name>.<resource-name>.<attribute-name> syntax. This will display the attribute’s current value, and you can modify it using HCL syntax. Once you’ve modified the value, press enter to apply the change to the Terraform state.

Warning

Modifying Terraform state manually using terraform console should only be done for testing or debugging purposes and should not be used in production environments. Modifying the state manually can cause unexpected results and may put your infrastructure at risk.

1.7 1.7 Using Terraform Cloud to Store Terraform State

Problem

You want to store your Terraform state in a secure location that is accessible for both yourself and any additional team members you work with.

Solution

Use Terraform Cloud as a remote backend for storing your Terraform state. To do this, you must configure your Terraform code to use Terraform Cloud as the backend, then initialize Terraform to use this backend.

Here’s an example of how to configure your Terraform code to use Terraform Cloud as the backend:

terraform {
  cloud {
    organization = "<your-organization-name>"
    workspaces {
      name = "<your-workspace-name>"
    }
  }
}

Note

Replace <your-organization-name> and <your-workspace-name> with your organization and workspace names in Terraform Cloud.

Once you’ve added this configuration to your Terraform code, you can initialize Terraform to use the backend by running the following commands:

terraform init -backend-config="token=<your-terraform-cloud-api-token>"

Note

Replace <your-terraform-cloud-api-token> with your Terraform Cloud API token. You can find your API token in the Terraform Cloud UI under User Settings > Tokens.

Discussion

Storing your Terraform state in a remote location is a best practice for managing infrastructure as code. It ensures that your state is centralized and accessible from anywhere. It provides additional security by keeping your state out of version control systems and on infrastructure where secrets can be accessed.

Terraform Cloud is a popular choice for storing Terraform state remotely, as it provides a secure and managed backend that is explicitly designed for Terraform. To use Terraform Cloud as a backend, you must configure your Terraform code to use the remote backend type and specify your organization and workspace names. You can specify other backend configurations, such as encryption and state versioning.

Using Terraform Cloud to store your Terraform state is a best practice for managing infrastructure as code, and it’s easy to set up by utilizing the commands listed in this recipe.

1.8 1.8 Using Terraform with VS Code

1.9 Problem

You want to use Terraform with Visual Studio Code to write and manage your infrastructure-as-code (IAC) projects.

1.10 Solution

To use Terraform with Visual Studio Code, you need to follow these steps:

If you haven’t already, download and install Visual Studio Code from the Visual Studio Code website.

Install the Terraform extension for Visual Studio Code by:

Opening Visual Studio Code.

Open the Extensions view by clicking the Extensions icon in the sidebar or pressing Ctrl + Shift + X on Windows or Cmd + Shift + X on macOS.

Search for the hashicorp.terraform extension

Click the “Install” button to install the extension (see Figure 1-1).

Figure 1-1. Installing the Terraform extension for Visual Studio Code

Open a directory containing any of your Terraform projects in Visual Studio Code. You can even use the integrated terminal to run Terraform commands.

Discussion

Visual Studio Code’s editing features can be used to write and format your Terraform code. The Terraform extension provides syntax highlighting, code completion, and linting features to help you write correct and well-structured code.

The Terraform extension provides several features to help you manage your project, such as:

Run Terraform commands: You can use the integrated terminal to run Terraform commands, such as terraform init, terraform plan, and terraform apply.

Preview changes: You can use the Terraform extension to preview changes to your infrastructure before applying them. Click the “Terraform” icon in the sidebar and select “Show Terraform Output.”

View resources: You can view your infrastructure resources using the Terraform extension. Click the “Terraform” icon in the sidebar and select “Show Terraform Graph.”

1.11 1.9 Managing Terraform Provider Version Constraints

Problem

You need to manage the version constraints of your Terraform providers to ensure that you have certainty in the ability to reproduce your infrastructure-as-code instantiations.

Solution

Use the version argument in your provider configuration to specify the desired provider version and use version constraints to manage which provider versions are acceptable.

Here’s an example of how to specify a provider version and version constraint in your Terraform code:

provider "aws" {
  version = "~> 5.0"
  region  = "us-west-2"
}

In this example, the version argument specifies a version constraint of ~> 5.0, which means that any version of the provider greater than or equal to 5.0 and less than 6.0 will be accepted. This allows you to automatically upgrade to the latest version of the provider within the 5.x range, but prevents upgrades to 6.0 or higher, which may introduce breaking changes.

You can also specify an exact version of the provider by setting the version argument to a specific version number, like this:

provider "aws" {
  version = "5.1.0"
  region  = "us-west-2"
}

In this example, only version 5.1.0 of the provider will be accepted.

Discussion

Managing the version constraints of your Terraform providers is an essential part of infrastructure as code, as it allows you to ensure that your infrastructure is deployed using a known and tested version of each provider. By specifying version constraints in your provider configuration, you can ensure that you are using a version of the provider that is compatible with your infrastructure code.

Generally, it is recommended to use version constraints that allow for automatic upgrades within a given range but prevent upgrades to higher major versions. This will enable you to take advantage of new features and bug fixes within a given version range but prevents breaking changes from being introduced by major version upgrades.

By specifying version constraints in your provider configuration, you can also prevent unexpected or accidental upgrades of your provider versions. This can help to avoid issues caused by breaking changes introduced by new provider versions.

Managing the version constraints of your Terraform providers is an integral part of infrastructure as code. By using the version argument in your provider configuration and specifying version constraints, you can ensure that you are using the correct provider version for your infrastructure, prevent unexpected upgrades, and take advantage of new features and bug fixes within a given version range.

1.12 1.10 Strategies for version-controlling Terraform code repositories

Problem

You must manage multiple Terraform code bases in a version control system (VCS).

Solution

Initiate with a Single Repository:

Start with a single VCS repository containing a directory for your Terraform code.

Maintain a single Terraform state file to track the infrastructure deployed through this repository.

Modularize Your Terraform Code:

As your infrastructure complexity escalates, identify logical components within your Terraform code, such as networking, storage, and application stacks.

Create separate directories or repositories for each logical component with its own Terraform state file.

Implement Terraform Modules:

Extract common patterns and reusable code segments into Terraform modules.

Store these modules in separate repositories, enabling individual versioning and reuse across different infrastructure setups.

Adopt a Tiered Structure:

Organize your infrastructure deployments into different tiers, such as user management (IAM), networking, storage, and application stacks.

Maintain separate VCS repositories for each tier to allow for focused version control and minimize the impact of changes within each tier.

Utilize Version Tagging:

Employ version tagging for your Terraform modules and repositories.

This practice allows precise version referencing, facilitating a controlled and predictable deployment process.

Discussion

These strategies aim to foster a structured, version-controlled, and modular approach towards managing Terraform code repositories. Initially, a single repository approach simplifies infrastructure management; however, as the complexity burgeons, modularization and tiered structuring become crucial to ensuring manageable and risk-averse infrastructure evolution.

By segregating Terraform code into logical components and employing Terraform modules for common infrastructure patterns, you leverage the DRY (Don’t Repeat Yourself) principle. This modus operandi minimizes code repetition and promotes code reusability and maintainability.

The tiered structuring further delineates different infrastructure aspects into distinct tiers, each encapsulated within its VCS repository. This stratagem significantly reduces the blast radius of changes, rendering the operations faster and more manageable. For instance, an accidental deletion within the networking tier won’t cascade catastrophic ramifications onto other tiers, such as application stacks or storage.

Furthermore, utilizing version tagging imparts a robust version control mechanism, rendering your infrastructure deployments predictable and controlled. This, in turn, facilitates smoother transitions between different versions of infrastructure setups across various environments like production, development, and testing.

1.13 1.11 Strategies for deploying version-controlled Terraform code repositories

Problem

Deploying Infrastructure as Code (IaC) using Terraform necessitates an organized, version-controlled approach to manage code iterations and ensure reproducibility and traceability. Leveraging Version Control Systems (VCS) like Git and GitOps practices allow for systematic, automated, and auditable infrastructure deployment. Identifying recommended approaches and best practices in this domain is crucial for ensuring smooth deployment pipelines and operational efficiency.

Solution

Learn how to set up version controlled Terraform and set up some branch protection to ensure that only valid Terraform code gets applied.

module "vcs_repo" {
  source = "git::https://example.com/vcs-repos/terraform-modules.git//module-path?ref=v1.0.0"
}
provider "github" {
  token        = var.github_token
  organization = var.github_organization
}
resource "github_repository" "terraform_repo" {
  name        = "terraform-repo"
  description = "Terraform repository for managing infrastructure"
}
resource "github_branch_protection" "main" {
  repository     = github_repository.terraform_repo.name
  pattern        = "main"
  enforce_admins = false
  required_status_checks {
    strict   = true
    contexts = ["ci/terraform-run"]
  }
}

Discussion

In the solution provided, we delineate a typical setup where a Terraform module is sourced from a version-controlled repository and utilized within a GitHub organization to manage infrastructure deployments.

Module Source Versioning:

The module block sources a specific version (v1.0.0) of a Terraform module from a Git repository. By referencing a particular version, you ensure the reproducibility of your infrastructure deployments.

GitHub Provider Configuration:

The provider block configures the GitHub provider with credentials and organizational context. This setup is crucial for interfacing with GitHub to manage repositories and other GitHub resources.

GitHub Repository Resource:

The resource "github_repository" block creates a new GitHub repository named terraform-repo, designated for managing Terraform code.

Branch Protection:

The resource "github_branch_protection" block sets up branch protection rules for the main branch of the terraform-repo. Enforcing status checks from a CI/CD pipeline like GitHub Actions ensures that proposed changes pass necessary validation before merging.

The encapsulated GitOps approach within this setup fosters a culture of monitoring and validating changes in a version-controlled manner before applying them to the infrastructure. This structure enables collaboration and code sharing via module versioning, ensuring a systematic, traceable deployment workflow.

1.14 1.11 Deploying a Docker Container with Terraform

Problem

You want to use Terraform to manage and run your Docker containers on your machine or a Docker host.

Solution

Use the Docker provider in Terraform to manage your Docker containers and run them on your Docker host. The Docker provider allows you to define containers, images, and networks in your Terraform code and then apply that code to create or update your infrastructure.

Here’s an example of how to use the Docker provider to run a container:

provider "docker" {}
resource "docker_container" "example" {
  name  = "example"
  image = "nginx"
  ports {
    internal = 80
    external = 8000
  }
}

In this example, the docker_container resource defines a container named example that uses the nginx image and maps port 80 inside the container to port 8000 on the host.

To apply this configuration and create the container, run the following commands:

Initialize Terraform by running terraform init.

Apply the configuration by running terraform apply.

This will create the container on your Docker host and map port 80 inside the container to port 8000 on the host. You can then access the nginx web server running inside the container by visiting http://localhost:8000 in your web browser.

Discussion

The Docker provider is a powerful tool that allows you to manage your Docker containers using Terraform. By defining containers, images, and networks in your Terraform code, you can ensure that your Docker infrastructure is version-controlled and reproducible.

Note that you’ll need to install Docker on your machine or host. Once you install Docker, you can initialize the docker provider in your Terraform code by adding a provider block.

Using Terraform resources, you can then define Docker resources, like containers and images. Each resource has configuration options, like the container name and the image to use, allowing you to customize your Docker use cases.

Once you’ve defined your Docker resources, you can apply the configuration to create or update your infrastructure. Terraform will communicate with the Docker API to create or modify your containers, images, and networks.

1.15 1.12 Updating from legacy Terraform versions to 1.x and onwards

Problem

You are using a legacy version of Terraform, such as v0.12, v0.13, or want to upgrade from v0 to v1.x and beyond.

Solution

Upgrading from legacy versions of Terraform must be a measured process and has to be done step-by-step.

Create a backup of your Terraform state file(s) in case you need to manually restore to a previous version of the state file.

Determine if you are running the latest version of your current major version. For example, if you are on `v0.12`, make sure you are on `v0.12.31`, the latest `0.12` version of Terraform at the time of writing.

Update to the latest version of `v0.13` and run the upgrade command `terraform 0.13upgrade` to make a best-effort upgrade to your code with version-specific syntax changes in mind.

Following the upgrade command, inspect your code and verify line-by-line that all changes are intentional.

Note

Terraform upgrade commands make the best effort to update structures to a more modern version whenever possible. When the upgrade logic cannot determine the best path forward, a comment usually indicates which steps you must take manually.

When all changes look satisfactory, use the Terraform Plan and Apply approach until you can verify that the upgrade has been completed.

At this point, revert to Step 1, create a new backup of your Terraform state file(s), then continue the process, but increment each version.

Once you have updated your code to Terraform `v0.14.0` or later, continue the upgrade process as usual by simply installing a newer version of Terraform.

Next, use the Terraform Plan and Apply approach until you can verify that the upgrade for the current version you are upgrading to has been completed.

Discussion

Upgrading to newer Terraform versions from its predecessors has traditionally been a meticulous task, necessitating a thorough review to prevent the introduction of breaking changes in an infrastructure environment. This has often made version upgrades a labor-intensive endeavor.

However, a significant turn came with the release of Terraform v0.15, followed by Terraform 1.0. HashiCorp, the organization behind Terraform, rolled out a set of compatibility guarantees for the entire Terraform v1.x lifecycle. These guarantees address substantial backward compatibility considerations encompassing a wide range of Terraform functions, language features, changes to the wire protocol, and other elements that could adversely impact the operator experience.

The essence of this compatibility promise is to prevent the introduction of breaking changes by HashiCorp. Nevertheless, it’s imperative for you, as a Terraform codebase operator, to stay updated with relatively recent releases of Terraform.

A practical guideline is to lag by no more than two minor versions—translating to approximately a 9-12 month lag from the latest released version. For instance, if the prevailing Terraform version is v1.6.0, it’s advisable to be on v1.4.0 or a newer version.

We strongly advocate for a proactive approach towards testing and transitioning to contemporary Terraform versions. This not only ensures access to essential bug fixes but also to significant security enhancements.

1.16 1.13 Using Terraform Variables

Problem

You want to create abstractions in your Terraform code to make it more modular and reusable.

Solution

Use Terraform variables to create abstractions in your Terraform code. Variables allow you to parameterize your Terraform code and make it more flexible, allowing you to reuse the same code in different contexts.

Here’s an example of how to use variables to create an abstraction in your Terraform code:

variable "region" {
  type    = string
  default = "us-west-1"
}
provider "aws" {
  region = var.region
}
resource "aws_instance" "example" {
  ami           = "ami-0c55b159cbfafe1f0"
  instance_type = "t2.micro"
  region        = var.region
}

To use this configuration with a different region, simply override the region variable value when applying the configuration:

terraform apply -var region=us-east-1

Discussion

Using variables is a powerful way to create abstractions in your Terraform code. By parameterizing your code, you can make it more flexible and reusable, allowing you to use the same code in different contexts with different values for the variables.

In the example above, the region variable is used to parameterize the region value for the aws provider and the aws_instance resource. Using a variable for the region allows you to reuse the same code with different region values easily.

Variables can also create more complex abstractions, such as modules. Modules are a way to encapsulate Terraform code and construct reusable, shareable components. Modules can take input variables and output values, allowing you to create more complex abstractions that can be used in different contexts.

When using variables, it’s essential to be careful about the scope and visibility of the variable. You should avoid using global variables that can be accessed from anywhere in your Terraform code, as this can make your code harder to understand and maintain. Instead, use variables that are scoped to specific modules or resources and pass values between them using input and output variables.

1.17 1.14 Consistent Code for a Consistent Experience

Problem

When managing infrastructure as code (IaC) with Terraform, it’s imperative to have a well-defined workflow that ensures consistency, repeatability, and ease of collaboration among team members. Understanding the typical Terraform workflow and adhering to best practices is essential for managing infrastructure effectively and ensuring a uniform experience across various environments.

Solution

Utilizing in the required_version directive ensures specific versions of either Terraform or Terraform modules.

terraform {
  required_version = ">= 1.0.0"
  backend "remote" {
    hostname     = "app.terraform.io"
    organization = "example-org"
    workspaces {
      name = "my-workspace"
    }
  }
}
provider "aws" {
  region = "us-west-2"
}
resource "aws_instance" "example" {
  ami           = "ami-abcdefgh"
  instance_type = "t2.micro"
}

Discussion

Version Constraint:

The required_version directive in the terraform block specifies the version of Terraform required for this configuration. Selecting a version constraint is a good practice to ensure consistency across different environments and team members.

Backend Configuration:

The backend "remote" block configures a remote backend for storing the Terraform state. Remote backends like Terraform Cloud or Terraform Enterprise provide a centralized way to store and manage Terraform state, enabling collaboration and consistency.

Provider Configuration:

The provider "aws" block specifies the provider and its configuration. It’s crucial to keep provider configurations consistent to ensure the same behavior across different environments and team members.

Resource Configuration:

The resource "aws_instance" "example" block defines an AWS EC2 instance. Consistently structuring resource blocks and following naming conventions are essential for readable, maintainable, and collaborative code.

The outlined Terraform configuration and workflow encapsulate several best practices for maintaining a consistent experience when managing infrastructure as code. Adhering to a standardized workflow, defining explicit version constraints, utilizing remote backends for state management, and maintaining a consistent provider and resource configuration are critical steps toward achieving a uniform, predictable infrastructure management experience.

Chapter 2. Terraform Basics

A Note for Early Release Readers

With Early Release ebooks, you get books in their earliest form—the author’s raw and unedited content as they write—so you can take advantage of these technologies long before the official release of these titles.

This will be the 2nd chapter of the final book. Please note that the GitHub repo will be made active later on.

If you have comments about how we might improve the content and/or examples in this book, or if you notice missing material within this chapter, please reach out to the editor at mcronin@oreilly.com.

It’s an understatement to say that there are many ways in which folks can use Terraform. From standing up GitHub Organizations, provisioning their cluster management solution, to even curating the perfect Spotify playlist, Terraform is an excellent, declarative way to manage many things, even though it’s not always apparent what the best practices are for writing Terraform or how to construct your code. We’ll dig into the basics of Terraform in this chapter.

2.1 2.1 Understanding Terraform Providers

Problem

You have written some Terraform code, but it needs to be formatted consistently, making it hard to read and understand. You also want to ensure the code is valid before applying it.

Solution

The first step in the solution is to run the terraform console command, which allows for real-time evaluation of Terraform expressions.

# Step 1: Run the Terraform format command
terraform fmt
# Step 2: Run the Terraform validate command
terraform init
terraform validate

Discussion

Terraform provides built-in commands to help you format and validate your code. The terraform fmt command automatically formats your Terraform code to follow the recommended style guidelines, making it easier to read and maintain. This command should be run in the same directory as your Terraform configuration files.

The terraform validate command checks the syntax and semantics of your Terraform code to ensure that it’s valid before applying it. It checks for syntax errors, missing required arguments, and incorrect references. However, it does not check if the configuration will produce the desired infrastructure.

Before running the terraform validate command, you need to terraform init, which initializes your Terraform working directory by downloading the necessary provider plugins and setting up the backend for storing your state. This step is required because the validate command needs to know about the providers and modules used in your configuration.

Running these commands will help you maintain clean and valid Terraform code, reducing the likelihood of errors during the terraform apply stage.

2.2 2.2 Rapid Experimentation with terraform console

Problem

You want to quickly test and experiment with Terraform functions, expressions, or interpolation without modifying your configuration files.

Solution

Start your rapid experimentation journey by running the Terraform format command, a crucial step for ensuring code consistency.

Run the terraform console command:

# Step 1: Start the Terraform console
terraform console

Discussion

The terraform console command provides an interactive console that allows you to evaluate Terraform expressions, functions, and interpolation. This is a helpful tool for rapid experimentation and debugging without affecting your existing configuration files.

When you run terraform console, it starts an interactive REPL (Read-Eval-Print Loop) session in your terminal. You can type Terraform expressions or functions; the console will evaluate them and display the results.

Remember that the terraform console command uses the current workspace’s state, so any resources or outputs defined in your configuration will be accessible within the console. This is helpful for testing expressions that reference existing resources or variables.

2.3 2.3 Improving code quality with tflint

Problem

You want to improve the quality of your Terraform code by detecting potential issues not caught by the built-in terraform validate command.

Solution

Elevate your Terraform development process by installing TFLint, a linter that provides immediate feedback on code quality and potential errors.

# Step 1: Install TFLint
curl -L "$(curl -Ls https://api.github.com/repos/terraform-linters/tflint/releases/latest | grep -o -E "https://.+?_linux_amd64.zip")" -o tflint.zip && unzip tflint.zip && sudo mv tflint /usr/local/bin/ && rm tflint.zip
# Step 2: Run TFLint on your Terraform code
tflint

Discussion

TFLint is a third-party linting tool for Terraform code that helps you identify and fix issues not caught by Terraform’s built-in validation. It can detect unused variables, duplicate resources, and incorrect resource properties.

Before using TFLint, you need to install it. The provided command in the solution downloads the latest TFLint release for Linux from GitHub, unzips the archive, moves the binary to /usr/local/bin, and removes the zip file. Visit the official installation guide for other platforms or installation methods.

Once installed, you can run tflint in the same directory as your Terraform configuration files. By default, TFLint will check all .tf files in the current directory and output any issues it finds.

Using TFLint in combination with Terraform’s built-in validation and formatting commands helps improve the quality of your Terraform code, making it more readable, maintainable, and less prone to errors.

2.4 2.4 Improving code quality with tflint

Problem

You want to improve the security of your Terraform code by identifying and fixing potential security issues that may not be caught by Terraform’s built-in validation or other linting tools.

Solution

# Step 1: Install TFSec
curl -L "$(curl -Ls https://api.github.com/repos/tfsec/tfsec/releases/latest | grep -o -E "https://.+?_linux_amd64.tar.gz")" -o tfsec.tar.gz && tar -xzf tfsec.tar.gz && sudo mv tfsec /usr/local/bin/ && rm tfsec.tar.gz
# Step 2: Run TFSec on your Terraform code
tfsec

Discussion

TFSec is a third-party security scanner for Terraform code that helps you identify and fix potential security issues not caught by Terraform’s built-in validation or other linting tools. It scans your Terraform configuration files for insecure patterns, misconfigurations, and non-compliant settings, providing recommendations to improve security.

Once installed, you can run tfsec in the same directory as your Terraform configuration files. By default, TFSec will check all .tf files in the current directory and output any issues it finds, along with suggestions to fix them.

TFSec can be configured using a .tfsec.yml or .tfsec.yaml file to customize the scanner’s behavior, ignore specific checks, or define custom checks.

2.5 2.5 Validating code with preconditions and postconditions

Problem

You want to validate your Terraform code by ensuring that specific conditions are met before applying the configuration (preconditions) and after applying the configuration (postconditions).

Solution

Utilize Terraform’s pre and post conditions to enhance your Terraform code and module creation.

variable "instance_type" {
  type        = string
  description = "The instance type for the EC2 instance."
  # Define a precondition using validation block
  validation {
    condition     = contains(["t2.micro", "t2.small", "t2.medium"], var.instance_type)
    error_message = "The instance_type must be one of: t2.micro, t2.small, t2.medium."
  }
}
resource "aws_instance" "example" {
  ami           = "ami-0c947788c95c71c99" # Example Amazon Linux 2 AMI ID
  instance_type = var.instance_type
  # Define a postcondition using a lifecycle block with a create_before_destroy rule
  lifecycle {
    create_before_destroy = true
  }
  tags = {
    Name = "example-instance"
  }
}
# Define a null_resource to simulate postcondition checks
resource "null_resource" "postcondition_check" {
  # Use a local-exec provisioner to simulate postcondition checks
  provisioner "local-exec" {
    command = "echo 'Postcondition: Verifying instance tags...'; test '${aws_instance.example.tags.Name}' == 'example-instance'"
  }
}

Discussion

We define preconditions using the validation block within the variable block and postconditions using a null_resource with a local-exec provisioner.

The validation block allows you to specify a condition that must be true for the variable’s value to be considered valid. In this example, we define a precondition that checks whether the value of the instance_type variable is one of the allowed values: “t2.micro”, “t2.small”, or “t2.medium”. If the condition is not met, Terraform will display the specified error message and halt the execution.

The postcondition is simulated using a null_resource with a local-exec provisioner that executes a shell command. In this case, the shell command uses the test command to verify that the “Name” tag of the aws_instance.example resource is set to “example-instance”. If the check fails, the shell command will return a non-zero exit code, and Terraform will report an error.

The local-exec provisioner is only used here for demonstration purposes to simulate postcondition checks. It’s important to note that it executes locally on the machine running Terraform, and its use is unsuitable for most production use cases.

The validation block is available starting from Terraform 0.13, so make sure you are using a compatible version of Terraform.

In a real-world scenario, postconditions could be verified using external tools, scripts, or testing frameworks that can validate the state of the infrastructure and ensure it meets the expected criteria.

Please note that the “ami” value used in the aws_instance resource is just an example, and you should replace it with the appropriate AMI ID for your use case.

2.6 2.6 Validating code with Open Policy Agent

Problem

You are developing Terraform configurations and want to ensure they comply with specific policies and best practices. To achieve this, you want to use Open Policy Agent (OPA) to validate your Terraform code against defined policies before applying the configuration. You need a solution to implement policy-as-code validation using OPA.

Solution

Dive deep into policy as code by defining a Terraform variable named instance_type, a foundational element for Open Policy Agent rules.

> rego file

# terraform.rego
package terraform
# Define a policy to restrict allowed EC2 instance types
deny["Instance type must be t2.micro or t2.small"] {
    resource := input.resource_changes[_]
    resource.type == "aws_instance"
    instance_type := resource.change.after.instance_type
    not instance_type == "t2.micro"
    not instance_type == "t2.small"
}
# Define a policy to enforce tagging for EC2 instances
deny["All EC2 instances must have a Name tag"] {
    resource := input.resource_changes[_]
    resource.type == "aws_instance"
    tags := resource.change.after.tags
    not tags.Name
}

> shell

# Convert Terraform plan JSON output to a file
$ terraform init
$ terraform plan -out=tfplan
$ terraform show -json tfplan > tfplan.json
# Evaluate the Terraform plan against OPA policies
$ opa eval --format=pretty --data terraform.rego --input tfplan.json "data.terraform.deny"

Discussion

OPA is a general-purpose policy engine that allows you to define and enforce policies across your infrastructure. You can use OPA with Terraform to implement policy-as-code and validate your Terraform configurations against custom policies.

In the code above, we defined two example policies in a Rego file named terraform.rego. The first policy restricts the allowed EC2 instance types to “t2.micro” and “t2.small”, while the second policy enforces that all EC2 instances must have a “Name” tag. The deny rules specify conditions under which a policy violation occurs.

The policy rules use the input.resource_changes array to represent the set of changes that Terraform plans to make. Each element in the array contains information about a planned change, including the type of resource, the change action (e.g., create, update, delete), and the before and after states of the resource.

Once the policies are defined, you can evaluate them against your Terraform plan using the opa eval command. To do this, you must generate a JSON representation of the Terraform plan using the terraform show -json command. The generated JSON file (tfplan.json in the example) is then provided as input to the opa eval command and the Rego policy file terraform.rego.

The opa eval command evaluates the policies against the Terraform plan and reports any policy violations. If there are no violations, the output will be empty. The output will show the messages defined in the deny rules if there are violations.

OPA provides a powerful way to enforce governance and compliance across your infrastructure code. It can be integrated into CI/CD pipelines to automate policy checks as part of the deployment process.

2.7 2.7 Documenting code with terraform-docs

Problem

You are developing Terraform modules and want to generate documentation automatically for your code. The documentation should include information about inputs, outputs, providers, and resources defined in the module. You need a solution to automate the generation of module documentation using the terraform-docs tool.

Solution

Strengthen your code documentation by leveraging terraform-docs, a unique approach for annotating your Terraform configuration.

# main.tf
provider "aws" {
  region = "us-west-2"
}
variable "instance_type" {
  type        = string
  description = "The instance type for the EC2 instance."
  default     = "t2.micro"
}
resource "aws_instance" "example" {
  ami           = "ami-0c947788c95c71c99" # Example Amazon Linux 2 AMI ID
  instance_type = var.instance_type
  tags = {
    Name = "example-instance"
  }
}
output "instance_public_ip" {
  value       = aws_instance.example.public_ip
  description = "The public IP address of the EC2 instance."
}
# Generate module documentation using terraform-docs
$ terraform-docs markdown ./ > README.md

Discussion

The terraform-docs tool is a third-party utility that automatically generates documentation for Terraform modules. It produces documentation based on the module’s inputs, outputs, providers, resources, and other elements defined in the Terraform code. The tool can generate documentation in various formats, including Markdown, JSON, YAML, and HTML.

We provided an example Terraform module (main.tf) that includes a provider, variable, resource, and output. The module creates an AWS EC2 instance and exports the instance’s public IP address as an output.

To generate documentation for this module, we used the terraform-docs command followed by the desired output format (markdown) and the path to the module directory (./). The generated documentation is then redirected to a file named README.md in the module directory. The README.md file will contain the generated documentation detailing the module’s inputs, outputs, providers, and resources.

It’s important to note that terraform-docs uses the comments and descriptions in your Terraform code to generate the documentation. Therefore, providing meaningful descriptions for variables, outputs, and resources in your Terraform code is good practice to ensure the generated documentation is clear and informative.

2.8 2.8 Automating Code Validation with GitHub Actions

Problem

You want to automate the validation of your Terraform code whenever changes are pushed to your GitHub repository, ensuring that the code is valid before allowing it to be merged into the main branch.

Solution

Set the stage for automated code validation, where GitHub Actions workflows are often defined.

# .github/workflows/terraform-validate.yml
name: Terraform Validate
on:
  push:
    branches:
      - main
  pull_request:
jobs:
  validate:
    runs-on: ubuntu-latest
    steps:
    - name: Checkout repository
      uses: actions/checkout@v3
    - name: Setup Terraform
      uses: hashicorp/setup-terraform@v2
      with:
        terraform_version: 1.4
    - name: Terraform Init
      run: terraform init
    - name: Terraform Validate
      run: terraform validate

Discussion

The provided solution is a GitHub Actions workflow file named terraform_validation.yml, which should be placed in your GitHub repository’s .github/workflows directory.

The workflow is triggered by push events to the main branch and all pull_request events. It defines a single job, terraform_validation, which runs on an ubuntu-latest virtual environment.

The job consists of the following steps:

Checkout repository: This step uses the actions/checkout@v3 action to check out your repository’s code on the runner.

Setup Terraform: This step uses the hashicorp/setup-terraform@v2 action to set up the specified version of Terraform (in this case, version 1.4) on the runner.

Initialize Terraform: This step runs the terraform init command, initializing the Terraform working directory.

Validate Terraform configuration: This step runs the terraform validate command, which checks if the Terraform configuration files are syntactically valid and internally consistent.

If the Terraform validation fails, the GitHub Actions workflow will show an error, preventing the changes from being merged until the issues are fixed. This ensures that only valid Terraform code is merged into the main branch.

2.9 2.9 Using Dependabot for provider version updates

Problem

You want to automatically keep your Terraform provider versions up-to-date by using GitHub’s Dependabot, ensuring that your infrastructure code always uses the latest available features and bug fixes.

Solution

Prepare for a comprehensive process that involves multiple steps, all aimed at configuring Dependabot for automated provider updates.

# .github/dependabot.yml
version: 2
updates:
  - package-ecosystem: "terraform"
    directory: "/"
    schedule:
      interval: "weekly"

Discussion

The provided solution is a Dependabot configuration file named dependabot.yml, which should be placed in the .github directory of your GitHub repository.

This configuration file specifies the following settings:

version: Indicates the version of the configuration schema being used. In this case, version 2.

updates: A list of update configurations, each specifying an ecosystem, directory, and update schedule. In this case, we’ve specified a single update configuration for the Terraform ecosystem.

package-ecosystem: Indicates the package ecosystem for which Dependabot should check for updates. We’ve set it to “terraform” for updating Terraform providers.

directory: The directory in which the configuration files for the specified package ecosystem are located. We’ve specified the root directory (“/”) in this case.

schedule: The update schedule configuration. It specifies the frequency at which Dependabot should check for updates. In this example, we’ve set the interval to “weekly” to check for updates once a day.

With this configuration in place, Dependabot will automatically check for updates to your Terraform providers daily, create pull requests with the updates, and target the main branch. This helps ensure that your infrastructure code remains up-to-date and secure.

2.10 2.10 Using GitHub Codespaces and DevContainers

Problem

You want to set up a consistent development environment for your Terraform project using GitHub Codespaces and DevContainers, ensuring all contributors have the same toolset and dependencies.

Solution

Getting this recipe working contains several steps, so buckle up and prepare for all these moving parts. We’ll be using GitHub, Visual Studio Code (VSC), and a browser to complete these steps, so be sure to sign up and download those as you need.

To begin, create a .devcontainer folder at the root of your directory, then an empty Dockerfile and devcontainer.json file (Figure 2-1).

Figure 2-1. When viewing your files in VSC, it should look like this.

We’ll start by editing the Dockerfile and adding just a few key lines of configuration.

# See https://hub.docker.com/r/hashicorp/terraform for more information
FROM hashicorp/terraform:1.2.6

Next, with the following lines, we specify the JSON configuration for how our codespace container will work in the devcontainer.json file.

// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at:
// https://github.com/microsoft/vscode-dev-containers/tree/v0.238.1/containers/codespaces-linux
{
	"name": "GitHub Codespaces (Default)",
	"build": {
		"dockerfile": "Dockerfile"
	},
	// Configure tool-specific properties.
	"customizations": {
		// Configure properties specific to VS Code.
		"vscode": {
			// Add the IDs of extensions you want installed when the container is created.
			"extensions": [
				"GitHub.vscode-pull-request-github",
        "GitHub.copilot",
        "HashiCorp.terraform"
			]
		}
	}
}

Now, commit and push your changes to the repository you’re working with on GitHub.

Note

Ensure your changes are pushed to GitHub, or these next steps will not work.

Once your code is on GitHub, you will notice that you have unlocked a new capability, CodeSpaces.

To create your codespace, you can use the button within the GitHub UI (Figure 2-2), or you can try some of the other creation options.

Figure 2-2. Create your codespace using the button within the GitHub UI

The first time you load your Codespace, it may take a few moments for it to build and load within your browser or VSC (Figure 2-3).

Figure 2-3. Your codespace will look like this when building.

Once the Codespace has loaded, you will see something resembling Figure 2-4 in your browser (or within VSC).

Figure 2-4. Resulting page after the Codespace has loaded.

As a final step, you can run terraform version in the terminal window of your Codespace and confirm that you can use Terraform in the cloud.

Note

If you’re using Terraform Cloud to manage your state, you must set a TF_TOKEN_APP_TERRAFORM_IO environment variable containing your credentials from running terraform login. This step will allow you to modify your Terraform state stored in Terraform Cloud (Figure 2-5).

Figure 2-5. Set a TF_TOKEN_APP_TERRAFORM_IO environment variable to modify your Terraform state.

Discussion

In this recipe, we took many steps to set up a Codespace within GitHub. Once completed, this allows for you to work with your IaC workflows in the cloud without having to worry about which machine it is being run on or even the credentials for your cloud environments.

We first created a metadata folder that houses configuration information that installs the Terraform binary and links together a place where we can work with our IaC, plan, validate, apply, and perform edits in real-time. This example shows the minimal configuration required to run this Codespace. However, you can edit this for just about any workflow you can imagine or install any other binaries or tools needed.

2.11 2.11 Limiting Blast Radius with Terraform

Problem

You want to limit the blast radius of your Terraform-managed infrastructure, minimizing the potential impact of an erroneous change or configuration mistake.

Solution

Brace yourself for an intricate recipe that requires several steps to limit the impact of your Terraform configurations successfully.

# main.tf
provider "aws" {
  region = var.aws_region
}
module "application_one" {
  source = "./modules/application_one"
}
module "application_two" {
  source = "./modules/application_two"
}
# variables.tf
variable "aws_region" {
  description = "The AWS region for the infrastructure."
  type        = string
  default     = "us-west-2"
}
# modules/application_one/main.tf
resource "aws_s3_bucket" "app_one_bucket" {
  bucket = "app-one-bucket"
  acl    = "private"
}
# modules/application_two/main.tf
resource "aws_s3_bucket" "app_two_bucket" {
  bucket = "app-two-bucket"
  acl    = "private"
}

Discussion

To limit the blast radius of your Terraform-managed infrastructure, you can apply the following best practices:

Modularize your code: Break down your infrastructure into smaller, manageable modules. This approach helps ensure that changes made to one module have a minimal impact on other modules. In the provided example, we have created two separate modules (application_one and application_two) for managing different parts of the infrastructure.

Use multiple Terraform workspaces: Separate your environments (e.g., development, staging, and production) using different Terraform workspaces. This practice isolates changes made in one environment from affecting others.

Use IAM roles and policies: Implement fine-grained access control for your cloud resources using Identity and Access Management (IAM) roles and policies. This limits the potential damage caused by a misconfigured Terraform resource or user error.

Regularly review and audit your infrastructure: Review your Terraform code and infrastructure periodically, ensuring that resources are correctly configured and follow best practices. Tools like terraform fmt, terraform validate, and terraform plan can help ensure your code is valid and that it provides the expected results.

By following these best practices, you can minimize the blast radius of your Terraform-managed infrastructure and reduce the risk of unintended consequences when making changes.

About the Authors

Kerim Satirli is a senior developer advocate at HashiCorp, where he works on creating a positive experience for developers and infrastructure engineers. He is passionate about complex systems and enjoys the challenge of codifying them. Kerim enjoys aerial photography, rock climbing, and cake baking.

Taylor Dolezal works at the Cloud Native Computing Foundation (CNCF) as the head of ecosystem. He works closely with open source communities, and is passionate about Kubernetes, Terraform, and solving distributed systems problems. You can usually find Taylor buried deep in a book, preparing a technical talk, or running with his partner, Hannabeth, and two pups.