Terraform IaC

Terraform cheatsheet — provision infrastructure as code. terraform init, plan, apply, destroy, state list, import. Manage AWS/GCP/Azure resources with declarative HCL config.

7 min read

What it is

Terraform is a tool for safely and predictably creating, changing, and improving infrastructure across cloud and private environments. You reach for it when you need to define and manage your infrastructure as code.

Installation

Linux

# Using apt (Debian/Ubuntu)
sudo apt update
sudo apt install terraform

# Using yum/dnf (Fedora/CentOS/RHEL)
sudo yum install terraform
# or
sudo dnf install terraform

# Using Homebrew (if installed)
brew install terraform

macOS

# Using Homebrew
brew install terraform

Windows

# Using Chocolatey
choco install terraform

# Using Scoop
scoop install terraform

# Manual Download (extract to PATH)
# Download the latest zip archive from https://www.terraform.io/downloads.html
# Extract it and place the terraform executable in a directory included in your system's PATH.

Core Concepts

Configuration Files

Terraform code is written in HashiCorp Configuration Language (HCL) or JSON. Files typically end with .tf or .tf.json.

Providers

Providers are plugins that Terraform uses to interact with specific cloud or SaaS providers (e.g., AWS, Azure, GCP, Kubernetes, GitHub). You declare the providers you’ll use in your configuration.

Resources

Resources are the fundamental infrastructure objects that Terraform manages. Examples include virtual machines, databases, networks, DNS records. They are defined in your .tf files.

State

Terraform maintains a state file (usually terraform.tfstate) that records the mapping between your configuration and the real-world resources it manages. This is crucial for tracking changes and preventing drift.

Modules

Modules are self-contained packages of Terraform configurations that are managed as a group. They allow you to abstract and reuse infrastructure patterns.

Commands / Usage

Initialization

# Initialize a Terraform working directory
terraform init

This command downloads the provider plugins required by your configuration and initializes the backend for state storage. It must be run in the directory containing your .tf files.

Planning

# Generate an execution plan
terraform plan

This command creates an execution plan, which shows you what Terraform will do to achieve the desired state described in your configuration. It doesn’t make any changes to your infrastructure.

# Generate an execution plan and save it to a file
terraform plan -out=tfplan

Saving the plan allows you to apply exactly that plan later, ensuring that the infrastructure matches what was planned.

Applying

# Apply the changes described in the execution plan
terraform apply

# Apply a saved execution plan
terraform apply tfplan

This command provisions the infrastructure as described in your configuration or applies the saved execution plan. It will prompt for confirmation unless -auto-approve is used.

# Apply changes without interactive confirmation
terraform apply -auto-approve

Destroying

# Destroy all infrastructure managed by the current configuration
terraform destroy

# Destroy infrastructure without interactive confirmation
terraform destroy -auto-approve

This command removes all resources managed by the Terraform configuration. It will prompt for confirmation unless -auto-approve is used.

State Management

# Show the current state of the managed infrastructure
terraform state list

Lists all resources currently tracked in the Terraform state.

# Show detailed information about a specific resource in the state
terraform state show aws_instance.example

Displays the attributes of a specific resource from the state file.

# Move a resource within the state (e.g., renaming)
terraform state mv 'old.resource.name' 'new.resource.name'

Useful for refactoring your Terraform code.

# Remove a resource from the state (use with extreme caution)
terraform state rm 'resource.name'

This command removes a resource from Terraform’s state without destroying the actual infrastructure. This is dangerous and should only be used if you are certain the resource is no longer managed by Terraform and will not be managed by any other tool.

# Pull state from a remote backend
terraform init -reconfigure -upgrade

If you switch or reconfigure your remote backend, terraform init will prompt you to pull the state.

Formatting and Validation

# Format Terraform configuration files
terraform fmt

This command rewrites Terraform configuration files to a canonical format and style.

# Validate the syntax of Terraform configuration files
terraform validate

Checks whether the Terraform configuration is syntactically valid and internally consistent.

Workspace Management

# List available workspaces
terraform workspace list

Shows all available workspaces in your Terraform project.

# Create a new workspace
terraform workspace new staging

Creates a new, empty workspace named staging.

# Select a workspace
terraform workspace select production

Switches the active workspace to production.

# Delete a workspace
terraform workspace delete dev

Removes the dev workspace.

Providers

# Show information about Terraform providers
terraform providers

Lists the providers used in the current configuration and their versions.

Output Values

# Display output values from the current Terraform state
terraform output

Shows the values of output variables defined in your Terraform configuration.

# Display a specific output value
terraform output instance_ip_address

Shows the value of the instance_ip_address output variable.

Import

# Import existing infrastructure into Terraform state
terraform import aws_instance.example i-0abcdef1234567890

This command imports an existing infrastructure resource into your Terraform state, allowing you to manage it with Terraform. You need to define the resource in your .tf files first.

Common Patterns

Creating a simple AWS EC2 instance

main.tf:

provider "aws" {
  region = "us-east-1"
}

resource "aws_instance" "example" {
  ami           = "ami-0c55b159cbfafe1f0" # Example Amazon Linux 2 AMI
  instance_type = "t2.micro"

  tags = {
    Name = "HelloWorld"
  }
}

output "instance_id" {
  value = aws_instance.example.id
}

Commands:

# Initialize
terraform init

# See what will be created
terraform plan

# Create the instance
terraform apply

# See the instance ID
terraform output instance_id

Managing a Kubernetes Deployment

main.tf:

provider "kubernetes" {
  config_path = "~/.kube/config"
}

resource "kubernetes_deployment" "nginx" {
  metadata {
    name = "nginx-deployment"
    labels = {
      app = "nginx"
    }
  }

  spec {
    replicas = 3

    selector {
      match_labels = {
        app = "nginx"
      }
    }

    template {
      metadata {
        labels = {
          app = "nginx"
        }
      }

      spec {
        container {
          image = "nginx:latest"
          name  = "nginx-container"

          ports {
            container_port = 80
          }
        }
      }
    }
  }
}

output "deployment_name" {
  value = kubernetes_deployment.nginx.metadata[0].name
}

Commands:

# Initialize (if not already done)
terraform init

# See what will be created/applied
terraform plan

# Apply the deployment
terraform apply

# See the deployment name
terraform output deployment_name

Working with Remote State (AWS S3 Backend)

versions.tf:

terraform {
  backend "s3" {
    bucket = "my-terraform-state-bucket-12345"
    key    = "global/s3/terraform.tfstate"
    region = "us-east-1"
  }
}

Commands:

# Initialize the backend (run in the directory with versions.tf)
terraform init
# Terraform will prompt to configure the S3 bucket.
# If the bucket does not exist, you'll need to create it manually or using another Terraform configuration.

# Subsequent commands (plan, apply, destroy) will now use the remote state.
terraform plan
terraform apply

Using Terraform Modules

main.tf:

provider "aws" {
  region = "us-east-1"
}

module "vpc" {
  source  = "terraform-aws-modules/vpc/aws"
  version = "3.14.2" # Specify a version for reproducibility

  name = "my-vpc"
  cidr = "10.0.0.0/16"

  azs             = ["us-east-1a", "us-east-1b", "us-east-1c"]
  public_subnets  = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"]
  private_subnets = ["10.0.101.0/24", "10.0.102.0/24", "10.0.103.0/24"]

  enable_nat_gateway   = true
  single_nat_gateway = true
  enable_dns_hostnames = true
}

output "vpc_id" {
  value = module.vpc.vpc_id
}

Commands:

# Initialize to download the module
terraform init

# Plan and apply as usual
terraform plan
terraform apply

Gotchas

State File is Critical

Never manually edit the terraform.tfstate file unless you really know what you’re doing and have backups. Incorrect manual edits can lead to Terraform losing track of resources or attempting to recreate them.

terraform plan vs terraform apply

Always run terraform plan before terraform apply. The plan is your chance to review exactly what changes will be made. terraform apply will execute the plan, and it’s easy to overlook something in the output.

Resource Replacement

When a resource’s configuration changes in a way that requires it to be replaced (e.g., changing the instance type of an EC2 instance), Terraform will destroy the old resource and create a new one. This can lead to downtime if not managed carefully (e.g., using rolling updates or blue/green deployments).

Provider Version Pinning

It’s highly recommended to pin provider versions in your versions.tf or main.tf to ensure consistent behavior and avoid unexpected changes when providers are updated.

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 4.0" # Use a version constraint
    }
  }
}

Implicit Dependencies

Terraform automatically infers dependencies between resources. However, sometimes you might need to explicitly define a dependency using depends_on if Terraform cannot infer it correctly.

resource "aws_instance" "web" {
  # ...
}

resource "aws_eip" "web_ip" {
  instance = aws_instance.web.id
  depends_on = [aws_instance.web] # Explicit dependency
}

Locking

When using remote state backends (like S3), Terraform often employs locking mechanisms to prevent multiple users from applying changes concurrently, which could corrupt the state. Ensure your backend is configured for locking if applicable.

Sensitive Data

Avoid storing sensitive data (like passwords or API keys) directly in your Terraform code or state file. Use tools like HashiCorp Vault, AWS Secrets Manager, Azure Key Vault, or environment variables. Mark outputs as sensitive if they contain secrets.

output "db_password" {
  value     = aws_db_instance.main.password
  sensitive = true
}