Terraform Advanced — Modules at Scale Exercises

1 minute read

Practice module design and versioning.


Exercise 1 — Module Interface

Task: Design the variables.tf and outputs.tf for an S3 bucket module with: bucket name, versioning toggle, encryption type, and tags.

Answer
# variables.tf
variable "name"       { type = string; description = "Bucket name" }
variable "versioning" { type = bool; default = true; description = "Enable versioning" }
variable "encryption" {
  type    = string
  default = "aws:kms"
  description = "SSE algorithm (aws:kms or AES256)"
  validation {
    condition     = contains(["aws:kms", "AES256"], var.encryption)
    error_message = "Must be aws:kms or AES256"
  }
}
variable "tags" { type = map(string); default = {} }

# outputs.tf
output "bucket_id"  { value = aws_s3_bucket.this.id }
output "bucket_arn" { value = aws_s3_bucket.this.arn }
output "bucket_url" { value = "s3://${aws_s3_bucket.this.id}" }

Exercise 2 — Version Constraint

Task: Write the version constraint that allows minor and patch updates but blocks major version bumps from v2.

Answer
module "vpc" {
  source  = "org/vpc/aws"
  version = ">= 2.0.0, < 3.0.0"
  # Or equivalently:
  # version = "~> 2.0"
}

Exercise 3 — Wrapper Module

Task: Write a wrapper module for terraform-aws-modules/s3-bucket/aws that enforces: versioning always on, SSE-KMS always on, public access always blocked.

Answer
module "s3" {
  source  = "terraform-aws-modules/s3-bucket/aws"
  version = "4.1.0"

  bucket = var.name

  # Enforced: always on
  versioning = { enabled = true }

  server_side_encryption_configuration = {
    rule = {
      apply_server_side_encryption_by_default = {
        sse_algorithm = "aws:kms"
      }
    }
  }

  block_public_acls       = true
  block_public_policy     = true
  ignore_public_acls      = true
  restrict_public_buckets = true

  tags = merge(var.tags, { ManagedBy = "terraform" })
}

Chapter Summary

  • Single responsibility: One module, one concern
  • Stable interface: Don’t rename/remove variables in minor versions
  • Semantic versioning: MAJOR (breaking), MINOR (new feature), PATCH (bugfix)
  • Registry publishing: terraform-<provider>-<name> naming convention
  • Wrapper pattern: Enforce org standards around community modules

← Module Wrapper Pattern Chapter 5: Terragrunt →

Updated: