Stage 4 · Provision
Advanced Terraform Patterns
Environment Promotion
Dev to Staging to Prod pipelines with Terragrunt or native workspaces — moving changes safely between environments.
Promotion Patterns
Environment promotion is the process of moving validated infrastructure changes from dev to staging to production. The goal is to test changes in progressively more realistic environments before reaching production.
There are three primary patterns: directory-per-environment, CLI workspaces, and Terragrunt. Each has tradeoffs in terms of code duplication, flexibility, and operational complexity.
Directory-per-Environment
Each environment has its own directory with its own .tf files. The directories share modules but have independent state files and variable values. This is the simplest and most explicit pattern.
environments/
dev/
main.tf # Calls shared modules with dev values
variables.tf
terraform.tfvars
backend.tf
staging/
main.tf # Same structure, different values
terraform.tfvars
backend.tf
prod/
main.tf
terraform.tfvars
backend.tfEach directory is a separate Terraform root module with its own state. Changes to dev do not affect staging or prod. The downside is code duplication — main.tf files across environments are nearly identical.
module "vpc" {
source = "../../modules/vpc"
cidr_block = var.vpc_cidr
env = "prod"
}
module "eks" {
source = "../../modules/eks"
cluster_name = "prod-cluster"
cluster_version = "1.29"
vpc_id = module.vpc.vpc_id
subnet_ids = module.vpc.private_subnet_ids
}The prod directory calls the same modules as dev but with production-appropriate values. The source path points to the shared modules directory. Backend configuration points to a prod-specific state file.
Using Workspaces
Workspaces use the same directory with different state files per environment. This eliminates code duplication but limits flexibility — all environments share the same configuration.
locals {
env = terraform.workspace
instance_type = terraform.workspace == "prod" ? "m5.large" : "t3.micro"
min_count = terraform.workspace == "prod" ? 3 : 1
}
resource "aws_instance" "web" {
instance_type = local.instance_type
tags = {
Environment = local.env
}
}terraform.workspace returns the current workspace name. Use locals to map workspace names to environment-specific values. This keeps the configuration in one place but limits per-environment customization.
Terragrunt Promotion
Terragrunt is a thin wrapper for Terraform that adds DRY configuration, dependency management, and environment promotion. It is the most sophisticated approach for multi-environment setups.
terragrunt/
_envcommon/ # Shared module configurations
vpc.hcl
eks.hcl
dev/
env.hcl # Dev-specific values
vpc/
terragrunt.hcl
eks/
terragrunt.hcl
staging/
env.hcl
vpc/
terragrunt.hcl
prod/
env.hcl
vpc/
terragrunt.hcl
terragrunt.hcl # Root configurationTerragrunt uses _envcommon for shared module definitions and per-environment directories for values. The env.hcl file defines environment-specific variables. This eliminates duplication while maintaining isolation.
# terragrunt/prod/vpc/terragrunt.hcl
include "root" {
path = find_in_parent_folders()
}
terraform {
source = "../../modules//vpc"
}
# terragrunt/prod/eks/terragrunt.hcl
dependency "vpc" {
config_path = "../vpc"
}
inputs = {
vpc_id = dependency.vpc.outputs.vpc_id
subnet_ids = dependency.vpc.outputs.private_subnet_ids
}The dependency block tells Terragrunt that EKS depends on VPC. Terragrunt ensures VPC is applied before EKS and passes the outputs automatically. This eliminates manual wiring between modules.
Pipeline Design
A promotion pipeline runs Terraform plan and apply in dev first, then staging, then prod. Each stage gates on the previous stage passing. The pipeline should be triggered by merging to specific branches or by manual approval.
name: Promote to Production
on:
workflow_dispatch:
inputs:
pr_number:
description: 'PR number with changes to promote'
required: true
jobs:
dev:
uses: ./.github/workflows/terraform-apply.yml
with:
environment: dev
working_dir: environments/dev
staging:
needs: dev
uses: ./.github/workflows/terraform-apply.yml
with:
environment: staging
working_dir: environments/staging
prod:
needs: staging
uses: ./.github/workflows/terraform-apply.yml
with:
environment: prod
working_dir: environments/prodEach job depends on the previous one. If dev fails, staging and prod do not run. The workflow_dispatch trigger allows manual promotion with a PR reference for audit trail.
Promotion Gates
- terraform validate must pass — no syntax or type errors.
- TFLint and policy checks must pass — no violations.
- terraform plan must show expected changes — no surprises.
- Manual approval required for prod — human review before production.
- Plan artifact must be stored — auditable record of what was applied.
Production changes should always require human approval. Even with perfect CI, auto-apply to prod removes the last safety net. Use GitHub environment protection rules or manual approval steps.
For strict promotion, run terraform plan in dev and apply the same plan artifact in staging and prod. This ensures exactly the same changes reach production. Use terraform plan -out=plan.bin and terraform apply plan.bin.
Mark this lesson complete to store local progress and unlock a cleaner resume path the next time you visit.