DEV Community

Aisalkyn Aidarova
Aisalkyn Aidarova

Posted on

Terraform at Scale + Advanced Concepts

๐ŸŽฏ Lab Goal

You will build a modular Terraform project that:

  • Creates IAM users + EC2
  • Uses for_each, count, zipmap
  • Demonstrates lifecycle rules
  • Shows dependency handling
  • Simulates scale practices
  • Uses targeting & refresh control

๐Ÿงฑ STEP 1 โ€” Project Structure (Scaling Best Practice)

terraform-scale-lab/
โ”‚
โ”œโ”€โ”€ iam/
โ”‚   โ””โ”€โ”€ main.tf
โ”‚
โ”œโ”€โ”€ ec2/
โ”‚   โ””โ”€โ”€ main.tf
โ”‚
โ”œโ”€โ”€ shared/
โ”‚   โ””โ”€โ”€ variables.tf
โ”‚
โ””โ”€โ”€ root/
    โ””โ”€โ”€ main.tf
Enter fullscreen mode Exit fullscreen mode

โœ… This simulates:

  • Large infra split into modules
  • Reduces API calls
  • Used in real companies

๐Ÿงฉ STEP 2 โ€” Shared Variables (Object + Map + Set)

๐Ÿ“„ shared/variables.tf

variable "users" {
  type = set(string)
  default = ["alice", "bob", "john"]
}

variable "amis" {
  type = map(string)
  default = {
    dev  = "ami-123456"
    prod = "ami-654321"
  }
}

variable "instance_config" {
  type = object({
    instance_type = string
    count         = number
  })

  default = {
    instance_type = "t2.micro"
    count         = 2
  }
}
Enter fullscreen mode Exit fullscreen mode

โœ… Covers:

  • set
  • map
  • object

๐Ÿ‘ค STEP 3 โ€” IAM Module (for_each + zipmap)

๐Ÿ“„ iam/main.tf

# Create IAM users using for_each
resource "aws_iam_user" "users" {
  for_each = var.users
  name     = each.value
}

# Output names
output "user_names" {
  value = [for u in aws_iam_user.users : u.name]
}

# Output ARNs
output "user_arns" {
  value = [for u in aws_iam_user.users : u.arn]
}

# โœ… zipmap usage (IMPORTANT FOR EXAM)
output "user_map" {
  value = zipmap(
    [for u in aws_iam_user.users : u.name],
    [for u in aws_iam_user.users : u.arn]
  )
}
Enter fullscreen mode Exit fullscreen mode

โœ… You just implemented:

  • for_each
  • splat alternative (for loop)
  • zipmap()

๐Ÿ–ฅ๏ธ STEP 4 โ€” EC2 Module (count + lifecycle + depends_on)

๐Ÿ“„ ec2/main.tf

# Security group
resource "aws_security_group" "web" {
  name = "web-sg"

  ingress {
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

# EC2 instances using count
resource "aws_instance" "web" {
  count         = var.instance_config.count
  ami           = var.amis["dev"]
  instance_type = var.instance_config.instance_type

  vpc_security_group_ids = [aws_security_group.web.id]

  tags = {
    Name = "web-${count.index}"
  }

  # โœ… lifecycle rules
  lifecycle {
    create_before_destroy = true
    ignore_changes        = [tags]
  }
}
Enter fullscreen mode Exit fullscreen mode

โœ… You covered:

  • count
  • lifecycle
  • implicit dependency (SG โ†’ EC2)

๐Ÿ”— STEP 5 โ€” ROOT MODULE (Connect Everything)

๐Ÿ“„ root/main.tf

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

# Import shared variables
module "iam" {
  source = "../iam"
  users  = var.users
}

module "ec2" {
  source = "../ec2"

  amis             = var.amis
  instance_config  = var.instance_config

  # Explicit dependency example
  depends_on = [module.iam]
}
Enter fullscreen mode Exit fullscreen mode

๐Ÿงช STEP 6 โ€” Initialize & Run

cd root

terraform init
terraform plan
terraform apply
Enter fullscreen mode Exit fullscreen mode

๐Ÿšจ STEP 7 โ€” API THROTTLING SIMULATION

Now simulate large infra behavior:

terraform plan
Enter fullscreen mode Exit fullscreen mode

๐Ÿ‘‰ Terraform will:

  • Refresh ALL resources
  • Call AWS APIs multiple times

โšก STEP 8 โ€” SOLUTION 1: TARGETING

terraform apply -target=module.ec2
Enter fullscreen mode Exit fullscreen mode

โœ… Only EC2 runs
โœ… Less API calls


โšก STEP 9 โ€” SOLUTION 2: DISABLE REFRESH

terraform plan -refresh=false
Enter fullscreen mode Exit fullscreen mode

โœ… Skips API calls
โš ๏ธ Use only when state is trusted


โšก STEP 10 โ€” TEST LIFECYCLE (IMPORTANT)

Change tag manually in AWS Console:

Add:

Env = production
Enter fullscreen mode Exit fullscreen mode

Now run:

terraform apply
Enter fullscreen mode Exit fullscreen mode

๐Ÿ‘‰ Terraform will NOT remove it

โœ… Because:

ignore_changes = [tags]
Enter fullscreen mode Exit fullscreen mode

โšก STEP 11 โ€” TEST create_before_destroy

Change AMI:

ami = "new-ami-id"
Enter fullscreen mode Exit fullscreen mode

Run:

terraform apply
Enter fullscreen mode Exit fullscreen mode

๐Ÿ‘‰ Terraform:

  1. Creates new instance
  2. Then destroys old

โœ… No downtime


โšก STEP 12 โ€” TEST DEPENDENCY

Remove depends_on and observe order.

Then add back:

depends_on = [module.iam]
Enter fullscreen mode Exit fullscreen mode

๐Ÿ‘‰ Forces IAM creation first


โšก STEP 13 โ€” COMMENTS PRACTICE

Add all types:

# Single line comment

// Another comment

/*
Multi-line comment
for disabling resources
*/
Enter fullscreen mode Exit fullscreen mode

๐Ÿง  STEP 14 โ€” count vs for_each TEST

Change IAM to count (bad practice test)

count = 3
name  = "user-${count.index}"
Enter fullscreen mode Exit fullscreen mode

๐Ÿ‘‰ Then reorder list โ†’ resources recreated

โœ… This is WHY for_each is better


๐ŸŽฏ FINAL

You just practiced:

Scaling & API

  • Terraform refresh causes API throttling
  • Split projects
  • Use -target
  • Use -refresh=false carefully

Data Handling

  • zipmap()
  • map, set, object

Resource Behavior

  • lifecycle:

    • ignore_changes
    • create_before_destroy
    • prevent_destroy (concept)

Dependencies

  • implicit vs explicit

Iteration

  • count (risky)
  • for_each (production-safe)

Q: Why does Terraform cause API throttling?

Answer:
Because every plan performs a state refresh, calling cloud APIs for every resource, which can exceed provider rate limits in large infrastructures.

Top comments (0)