๐Ÿ—๏ธ

Terraform

Terraform CLI commands and HCL patterns for infrastructure as code

Core Workflow

The essential init โ†’ plan โ†’ apply โ†’ destroy lifecycle

bashยทInitialize working directory
terraform init
bashยทRe-init and upgrade providers
terraform init -upgrade
bashยทValidate configuration
terraform validate
bashยทFormat all .tf files
terraform fmt -recursive
bashยทPlan changes
terraform plan
bashยทPlan and save to file
terraform plan -out=tfplan
bashยทApply saved plan
terraform apply tfplan
bashยทApply without confirmation prompt
terraform apply -auto-approve
bashยทDestroy all resources
terraform destroy
bashยทDestroy without confirmation prompt
terraform destroy -auto-approve

Targeting & Partial Changes

Apply or destroy specific resources without touching everything

bashยทPlan only a specific resource
terraform plan -target=aws_instance.web
bashยทApply only a specific resource
terraform apply -target=aws_instance.web
bashยทDestroy only a specific resource
terraform destroy -target=aws_instance.web
bashยทTarget a module
terraform apply -target=module.vpc
bashยทReplace (taint) a resource
terraform apply -replace=aws_instance.web

State Management

Inspect, move, import and manipulate Terraform state

bashยทList all resources in state
terraform state list
bashยทShow a specific resource in state
terraform state show aws_instance.web
bashยทMove resource in state (rename)
terraform state mv aws_instance.old aws_instance.new
bashยทRemove resource from state (keep real infra)
terraform state rm aws_instance.web
bashยทPull remote state to stdout
terraform state pull
bashยทPush local state to remote
terraform state push terraform.tfstate
bashยทImport existing resource into state
terraform import aws_instance.web i-1234567890abcdef0
bashยทShow full state as JSON
terraform show -json | jq .
bashยทUnlock stuck state
terraform force-unlock <lock-id>

Variables & Outputs

Pass variables in and read outputs out

bashยทPass variable on CLI
terraform apply -var='instance_type=t3.medium'
bashยทPass variables from file
terraform apply -var-file=prod.tfvars
bashยทShow all outputs
terraform output
bashยทGet a specific output value
terraform output instance_ip
bashยทGet output as raw string (no quotes)
terraform output -raw instance_ip
bashยทGet output as JSON
terraform output -json
hclยทVariable with validation (HCL)
variable "environment" {
  type        = string
  description = "Deployment environment"
  default     = "dev"

  validation {
    condition     = contains(["dev", "staging", "prod"], var.environment)
    error_message = "Must be dev, staging, or prod."
  }
}
hclยทSensitive output (HCL)
output "db_password" {
  value     = aws_db_instance.main.password
  sensitive = true
}

Workspaces

Manage multiple state environments with workspaces

bashยทList workspaces
terraform workspace list
bashยทShow current workspace
terraform workspace show
bashยทCreate workspace
terraform workspace new staging
bashยทSwitch workspace
terraform workspace select prod
bashยทDelete workspace
terraform workspace delete staging
hclยทUse workspace name in config (HCL)
resource "aws_s3_bucket" "data" {
  bucket = "my-app-${terraform.workspace}-data"
}

Providers & Modules

Lock, install and manage providers and modules

bashยทList installed providers
terraform providers
bashยทLock provider versions
terraform providers lock -platform=linux_amd64 -platform=darwin_arm64
bashยทDownload modules and providers
terraform get
bashยทUpdate modules to latest matching version
terraform get -update
hclยทProvider version constraints (HCL)
terraform {
  required_version = ">= 1.6.0"

  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}
hclยทCall a child module (HCL)
module "vpc" {
  source  = "terraform-aws-modules/vpc/aws"
  version = "5.1.2"

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

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

Remote Backends

Configure S3 and Terraform Cloud backends for shared state

hclยทS3 backend with DynamoDB locking (HCL)
terraform {
  backend "s3" {
    bucket         = "my-tf-state"
    key            = "prod/terraform.tfstate"
    region         = "us-east-1"
    encrypt        = true
    dynamodb_table = "terraform-lock"
  }
}
hclยทTerraform Cloud backend (HCL)
terraform {
  cloud {
    organization = "my-org"

    workspaces {
      name = "my-app-prod"
    }
  }
}
bashยทMigrate state to new backend
terraform init -migrate-state
bashยทReconfigure backend (skip state migration)
terraform init -reconfigure

Common HCL Patterns

Loops, conditionals, locals and dynamic blocks

hclยทfor_each over a map
variable "buckets" {
  default = {
    logs    = "us-east-1"
    backups = "us-west-2"
  }
}

resource "aws_s3_bucket" "this" {
  for_each = var.buckets
  bucket   = "my-app-${each.key}"
  region   = each.value
}
hclยทcount with conditional
resource "aws_eip" "nat" {
  count  = var.environment == "prod" ? 1 : 0
  domain = "vpc"
}
hclยทlocals block
locals {
  common_tags = {
    Project     = var.project_name
    Environment = var.environment
    ManagedBy   = "terraform"
  }

  is_prod = var.environment == "prod"
}

resource "aws_instance" "web" {
  ami           = data.aws_ami.ubuntu.id
  instance_type = local.is_prod ? "t3.large" : "t3.micro"
  tags          = local.common_tags
}
hclยทdynamic block
variable "ingress_rules" {
  default = [
    { port = 80,  cidr = "0.0.0.0/0" },
    { port = 443, cidr = "0.0.0.0/0" },
  ]
}

resource "aws_security_group" "web" {
  name = "web-sg"

  dynamic "ingress" {
    for_each = var.ingress_rules
    content {
      from_port   = ingress.value.port
      to_port     = ingress.value.port
      protocol    = "tcp"
      cidr_blocks = [ingress.value.cidr]
    }
  }
}
hclยทdepends_on and lifecycle
resource "aws_instance" "app" {
  ami           = data.aws_ami.ubuntu.id
  instance_type = "t3.micro"

  depends_on = [aws_iam_role_policy.app]

  lifecycle {
    create_before_destroy = true
    ignore_changes        = [ami]
    prevent_destroy       = true
  }
}
hclยทData source lookup
data "aws_ami" "ubuntu" {
  most_recent = true
  owners      = ["099720109477"] # Canonical

  filter {
    name   = "name"
    values = ["ubuntu/images/hvm-ssd/ubuntu-*-22.04-amd64-server-*"]
  }
}

resource "aws_instance" "web" {
  ami           = data.aws_ami.ubuntu.id
  instance_type = "t3.micro"
}

Debugging & Inspection

Inspect expressions, enable logging and troubleshoot plans

bashยทInteractive console (evaluate expressions)
terraform console
bashยทShow planned changes as JSON
terraform plan -out=tfplan && terraform show -json tfplan | jq .
bashยทEnable detailed logging
TF_LOG=DEBUG terraform apply
bashยทLog to file
TF_LOG=INFO TF_LOG_PATH=./tf.log terraform plan
bashยทGraph resource dependencies
terraform graph | dot -Tsvg > graph.svg
hclยทPrint value during plan with check (HCL)
check "bucket_name_valid" {
  assert {
    condition     = length(var.bucket_name) <= 63
    error_message = "S3 bucket name must be 63 characters or fewer."
  }
}