Terraform Modules: Source, Versioning, Composition
Modules are reusable, encapsulated Terraform configurations. They package infrastructure patterns into composable components.
A module structure:
modules/
networking/
main.tf
variables.tf
outputs.tf
ecs-service/
main.tf
variables.tf
outputs.tf
Calling a module:
module "networking" {
source = "./modules/networking"
vpc_cidr = "10.0.0.0/16"
environment = var.environment
}
module "api_service" {
source = "./modules/ecs-service"
vpc_id = module.networking.vpc_id
subnet_ids = module.networking.private_subnet_ids
container_image = var.api_image
}
Use the Terraform Registry for community modules: source = "hashicorp/consul/aws". Pin module versions for reproducibility. Module composition combines smaller modules into larger architectures.
Remote State with S3 and DynamoDB
Remote state enables team collaboration. The S3 backend stores state files with encryption and versioning. DynamoDB provides state locking.
Setup:
- Create an S3 bucket with versioning and encryption enabled
- Create a DynamoDB table with LockID as the partition key
- Configure the backend in Terraform
State isolation per environment: use different key paths like prod/vpc/terraform.tfstate and staging/vpc/terraform.tfstate. Cross-account state access requires IAM policies on the S3 bucket.
State manipulation commands: state list, state show, state mv, state rm. Use terraform import to bring existing resources under Terraform management.
for_each, dynamic Blocks, and Import
for_each creates multiple instances from a map or set:
resource "aws_instance" "web" {
for_each = toset(["web-1", "web-2", "web-3"])
ami = data.aws_ami.latest.id
instance_type = var.instance_type
tags = { Name = each.value }
}
Use for_each instead of count when resources have unique configurations. count creates identical instances; for_each creates named instances.
Dynamic blocks generate nested blocks from lists:
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
}
}
Import brings existing resources under Terraform management:
terraform import aws_instance.web i-0123456789abcdef0
Terraform 1.5+ supports import blocks in configuration for declarative imports.
Drift Detection and Custom Providers
Drift detection identifies resources where actual infrastructure differs from Terraform state. Run terraform plan regularly to catch drift.
Remediation options: update Terraform config to match reality, or revert the infrastructure to match your config. For accidental changes, revert. For intentional changes, update your Terraform configuration.
Custom providers extend Terraform to manage resources from any API. Write providers in Go using the Terraform Plugin SDK. Common use cases: managing SaaS resources, internal platforms, or niche cloud services.
Testing: use Terratest for automated testing of Terraform modules. Create infrastructure, run tests, then destroy. This validates that your Terraform code produces the expected infrastructure.
Quiz
1. When should you use for_each instead of count in Terraform?
2. What is the purpose of Terraform modules?
3. How do you bring existing resources under Terraform management?
Flashcards
Question
What is a Terraform module?
Click to reveal answer
Answer
A reusable, encapsulated Terraform configuration that packages infrastructure patterns into composable components.
Question
for_each vs count?
Click to reveal answer
Answer
for_each creates named instances from maps/sets with unique configs. count creates identical numbered instances.
Question
What is terraform import?
Click to reveal answer
Answer
Brings existing resources under Terraform management by adding them to the state file.
Question
What is drift in Terraform?
Click to reveal answer
Answer
When actual infrastructure differs from what Terraform state records, detected via terraform plan.
Revision Notes
Key Takeaways
- 1. Modules package reusable infrastructure patterns for composition
- 2. S3 backends with DynamoDB locking enable team collaboration
- 3. for_each for named instances, count for identical instances
- 4. Regular plan runs catch drift before it causes issues
Interview Tips
- • Design a module structure for a microservices platform
- • Explain how you would handle state for a multi-team organization
- • Describe the difference between for_each and count with examples
- • Explain how you would remediate drift in production
Cheat Sheet
Modules: reusable configs with source, version, outputs. Remote state: S3 (storage) + DynamoDB (locking). for_each: named instances from maps. Import: bring existing resources under management. Drift: actual vs state diverges, catch with plan.