Advanced Terraform Module Usage: Versioning, Nesting, and Reuse Across Environments

Day 9: Continuing Reuse of Infrastructure with Modules
Welcome back to our Terraform learning journey! On Day 9, we’re diving deeper into module reuse, exploring module gotchas, versioning, and multi-environment deployments. We’ll also enhance our existing module with versioning support and deploy it across different environments. Reading: Module Gotchas & Versioning (Chapter 4, Pages 115-139)
Activity
Enhance my main.tf to support multiple multiple environments
terraform {
required_version = ">= 1.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
provider "aws" {
region = var.aws_region
default_tags {
tags = var.tags
}
}
locals {
name_prefix = "${var.environment}-web"
common_tags = merge(var.tags, {
Environment = var.environment
ManagedBy = "terraform"
})
}
data "aws_ami" "amazon_linux" {
most_recent = true
owners = ["amazon"]
filter {
name = "name"
values = ["amzn2-ami-hvm-*-x86_64-gp2"]
}
filter {
name = "virtualization-type"
values = ["hvm"]
}
}
module "security_group" {
source = "./modules/security_group"
environment = var.environment
name_prefix = local.name_prefix
tags = local.common_tags
}
module "ec2" {
source = "./modules/ec2"
environment = var.environment
name_prefix = local.name_prefix
ami_id = data.aws_ami.amazon_linux.id
instance_type = var.instance_type[var.environment]
security_group_id = module.security_group.security_group_id
tags = local.common_tags
}
module "alb" {
source = "./modules/alb"
environment = var.environment
name_prefix = local.name_prefix
security_group_id = module.security_group.security_group_id
instance_id = module.ec2.instance_id
tags = local.common_tags
}
output "public_ip" {
description = "Public IP address of the EC2 instance"
value = module.ec2.public_ip
}
output "public_dns" {
description = "Public DNS name of the EC2 instance"
value = module.ec2.public_dns
}
output "alb_dns_name" {
description = "DNS name of the Application Load Balancer"
value = module.alb.alb_dns_name
}
output "environment" {
description = "Current environment"
value = var.environment
}
The variables.tf
variable "environment" {
description = "Environment name (dev, staging, production)"
type = string
validation {
condition = contains(["dev", "staging", "production"], var.environment)
error_message = "Environment must be dev, staging, or production."
}
}
variable "aws_region" {
description = "AWS region"
type = string
default = "us-west-2"
}
variable "instance_type" {
description = "EC2 instance type per environment"
type = map(string)
default = {
dev = "t3.micro"
staging = "t3.small"
production = "t3.medium"
}
}
variable "min_size" {
description = "Minimum number of instances per environment"
type = map(number)
default = {
dev = 1
staging = 2
production = 3
}
}
variable "max_size" {
description = "Maximum number of instances per environment"
type = map(number)
default = {
dev = 2
staging = 4
production = 6
}
}
variable "tags" {
description = "Common tags for all resources"
type = map(string)
default = {}
}
And set up environments/terraform.tfvars for dev/staging/production
environment = "dev" / "prod"/ "staging"
aws_region = "us-west-2"
tags = {
Environment = "development"
Project = "30-day-terraform-challenge"
Owner = "simi-ops"
CostCenter = "engineering"
}
And ec2.tf to support the environments
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
locals {
module_version = "1.0.0"
instance_name = "${var.name_prefix}-instance"
}
resource "aws_instance" "web" {
ami = var.ami_id
instance_type = var.instance_type
vpc_security_group_ids = [var.security_group_id]
user_data = base64encode(templatefile("${path.module}/user_data.sh", {
environment = var.environment
}))
tags = merge(var.tags, {
Name = local.instance_name
Module = "ec2"
ModuleVersion = local.module_version
})
}
This repository contains an enhanced Terraform configuration with support for multiple environments (dev, staging, production) and module versioning.
Repository Structure
terraform/
├── main.tf
├── variables.tf
├── deploy.sh
├── README.md
├── environments/
│ ├── dev/
│ │ └── terraform.tfvars
│ ├── staging/
│ │ └── terraform.tfvars
│ └── production/
│ └── terraform.tfvars
├── modules/
│ ├── alb/
│ │ ├── main.tf
│ │ ├── variables.tf
│ │ └── outputs.tf
│ ├── ec2/
│ │ ├── main.tf
│ │ ├── variables.tf
│ │ ├── outputs.tf
│ │ └── user_data.sh
│ └── security_group/
│ ├── main.tf
│ ├── variables.tf
│ └── outputs.tf
Features
Multi-environment support (dev, staging, production)
Module versioning with consistent tagging
Environment-specific configurations
Automated deployment scripts
Cost optimization with environment-specific instance types
Security best practices with environment-specific access controls
Consistent resource naming with environment prefixes
Initialize Terraform
# Make script executable
chmod +x deploy.sh
# Deploy to different environments
./deploy.sh dev plan
./deploy.sh dev apply
./deploy.sh staging plan
./deploy.sh staging apply
./deploy.sh production plan
./deploy.sh production apply

Environment Differences
| Environment | Instance Type | Region | SSH Access | ALB Protection |
| dev | t3.micro | us-west-2 | 0.0.0.0/0 | Disabled |
| staging | t3.small | us-west-2 | 0.0.0.0/0 | Disabled |
| production | t3.medium | us-east-1 | 10.0.0.0/8 | Enabled |

Module Versions
All modules are tagged with version 1.0.0 and include:
Consistent variable interfaces
Comprehensive outputs
Environment-aware configurations
Proper resource tagging
Cleanup
To remove all resources:
./scripts/deploy.sh dev destroy
./scripts/deploy.sh staging destroy
./scripts/deploy.sh production destroy




