TFLAB23-Module Sources and Versioning

Module Sources and Versioning

🔧 Terraform Core ⭐ Intermediate Module Sources Terraform Registry Git Source version constraint

Scenario

Your team has been using a local module for S3 buckets. Now you want to try a public module from the Terraform Registry for tagging, and a Git-hosted module from your organization's internal repo. You also need to pin module versions to prevent unexpected changes when the module author publishes updates.

Your Objectives
  • Reference a module from a local path: source = "./modules/bucket".
  • Reference a module from the Terraform Registry: source = "terraform-aws-modules/s3-bucket/aws" with a version constraint.
  • Understand Git-based sources: source = "git::https://..." with ?ref=v1.0.0.
  • Pin versions using version = "~> 3.0" (pessimistic constraint).

Additional Context

Module source types:

  • Local: source = "./modules/name" — relative path.
  • Registry: source = "namespace/name/provider" — from registry.terraform.io. Supports version.
  • GitHub: source = "github.com/org/repo//modules/name" — the // separates the repo from the subdirectory path.
  • Git: source = "git::https://example.com/repo.git?ref=v1.0"
  • S3: source = "s3::https://bucket.s3.amazonaws.com/module.zip"

The version argument is only supported for registry modules. For Git sources, use ?ref=tag to pin to a specific tag, branch, or commit.

main.tf

terraform {
  required_version = ">= 1.5.0"
  required_providers {
    aws = { source = "hashicorp/aws", version = "~> 5.0" }
  }
}

provider "aws" { region = "us-east-1" }
data "aws_caller_identity" "current" {}

# ─── SOURCE 1: Terraform Registry Module ───
# Format: "namespace/name/provider"
# 'version' pinning is ONLY available for registry modules.
module "s3_bucket_registry" {
  source  = "terraform-aws-modules/s3-bucket/aws"
  version = "~> 4.0"    # Allow 4.x, block 5.0+

  bucket = "registry-demo-${data.aws_caller_identity.current.account_id}"

  versioning = {
    enabled = true
  }

  tags = { Source = "registry", ManagedBy = "terraform" }
}

# ─── SOURCE 2: Local Path ───
# No version argument — it uses whatever is on disk.
# module "local_bucket" {
#   source      = "./modules/s3-bucket"
#   bucket_name = "local-demo-${data.aws_caller_identity.current.account_id}"
# }

# ─── SOURCE 3: Git URL (example — commented) ───
# Pin with ?ref= to a tag, branch, or commit SHA.
# module "from_git" {
#   source = "git::https://github.com/your-org/terraform-modules.git//modules/s3-bucket?ref=v1.2.0"
#   bucket_name = "git-demo"
# }

# ─── SOURCE 4: GitHub shorthand (example — commented) ───
# module "from_github" {
#   source = "github.com/terraform-aws-modules/terraform-aws-s3-bucket//modules/object?ref=v4.1.0"
# }

outputs.tf

output "registry_bucket_id"  { value = module.s3_bucket_registry.s3_bucket_id }
output "registry_bucket_arn" { value = module.s3_bucket_registry.s3_bucket_arn }

Workflow Commands

# terraform init downloads the registry module
terraform init

# See where the module was downloaded
ls .terraform/modules/

terraform plan
terraform apply
terraform destroy
✓ Expected Output After Init
Initializing modules...
Downloading registry.terraform.io/terraform-aws-modules/s3-bucket/aws 4.x.x for s3_bucket_registry...
- s3_bucket_registry in .terraform/modules/s3_bucket_registry

Initializing provider plugins...

Terraform has been successfully initialized!