DevOps Track Β· Day 4 Modular Design Β· 20 min
Modular Architecture
DevOps Terraform Sprint β Day 4
Refactor monolithic HCL definitions into self-contained reusable modules with explicit input contracts and outputs
About Today's 20-Minute Lab
As infrastructure grows across environments (Development, Staging, Production), copying and pasting main.tf files causes code duplication and drift. Today on Day 4, you will package infrastructure into a reusable local module (./modules/docker_web_app). Modules encapsulate resources behind clean, reusable API interfaces.
π¬ Day 4 Video Walkthrough
π¦Sample Codebase & Working Solution (Day 4)Available Now
View on GitHubThe verified Day 4 sample code is live in the public GitHub repository. Clone it to get the complete working solution immediately.
Git Clone β Available Now
git clone https://github.com/letstrnsfrm-ai/devops-terraform-roadmap.git
cd devops-terraform-roadmap/day-4-modules1
Module Directory & Child Blueprint
GOAL
Create the child module directory structure in
./modules/docker_web_app.DIR SETUP
Create module folder structure:
Terminal β Create Module Directory
mkdir -p ~/terraform-labs/day-4-modules/modules/docker_web_app
cd ~/terraform-labs/day-4-modulesCHILD MAIN.TF
Create
./modules/docker_web_app/main.tf:HCL β ./modules/docker_web_app/main.tf
terraform {
required_providers {
docker = {
source = "kreuzwerker/docker"
version = "~> 3.0.1"
}
}
}
resource "docker_image" "app" {
name = var.image_name
keep_locally = false
}
resource "docker_container" "app" {
name = var.app_name
image = docker_image.app.image_id
ports {
internal = var.internal_port
external = var.external_port
}
}CHILD VARIABLES.TF
Create
./modules/docker_web_app/variables.tf:HCL β ./modules/docker_web_app/variables.tf
variable "app_name" { type = string }
variable "image_name" { type = string, default = "nginx:alpine" }
variable "internal_port" { type = number, default = 80 }
variable "external_port" { type = number }πProduction Context & Enterprise Real-World Implementation
π‘ Why We Are Doing This: Encapsulation isolates complex underlying resource structures behind simple input contracts, standardizing infrastructure quality across enterprise development teams.
π’ Real-World Production Usecase: Platform teams (at Spotify or AirBnB) author standardized Terraform modules for microservices, database clusters, and VPCs. Developer teams simply consume approved modules without re-inventing security rules.
βοΈ How Implemented in Production: Production child modules are hosted in private Git repositories or Terraform Private Registries, enabling semantic versioning (e.g. `source = 'git::https://github.com/org/tf-modules.git//web_app?ref=v2.1.0'`).
2
Instantiating Modules in Root Blueprint
GOAL
Instantiate multiple instances of your child module (e.g. Frontend Web and Admin Portal) in the root
main.tf.ROOT MAIN.TF
Create root
main.tf referencing the child module twice:HCL β ./main.tf
terraform {
required_providers {
docker = {
source = "kreuzwerker/docker"
version = "~> 3.0.1"
}
}
}
provider "docker" {}
# Module Instance 1: Frontend Web App
module "frontend_app" {
source = "./modules/docker_web_app"
app_name = "qe_frontend_service"
external_port = 8086
}
# Module Instance 2: Admin Portal Service
module "admin_app" {
source = "./modules/docker_web_app"
app_name = "qe_admin_portal_service"
external_port = 8087
}πProduction Context & Enterprise Real-World Implementation
π‘ Why We Are Doing This: Module reusability eliminates thousands of lines of duplicated HCL, making infrastructure readable, maintainable, and easy to audit.
π’ Real-World Production Usecase: SaaS companies instantiate the same core microservice module tens or hundreds of times across different regional data centers (US-East, EU-Central, AP-South).
βοΈ How Implemented in Production: In root production code, modules are instantiated with environment tags, cost-center allocation tags, and monitoring integration hooks built right into the module contract.
3
Module Initialization & Provisioning
GOAL
Run
terraform init to index local module sources and provision both container instances.PREREQ
Ensure your Docker daemon is running before applying:
Terminal β Docker Daemon Setup (choose your OS)
# macOS β Docker Desktop (GUI: open app) OR Colima (CLI):
colima start
export DOCKER_HOST="unix://$HOME/.colima/default/docker.sock"
# Windows β ensure Docker Desktop is running with WSL2 backend (no extra command needed)
# Linux:
sudo systemctl start docker
# then verify: docker psWORKFLOW
Initialize and apply the root module blueprint:
Terminal β Init & Apply Modules
# 1. Index local modules
terraform init
# 2. Plan and provision both instances
terraform apply -auto-approveπProduction Context & Enterprise Real-World Implementation
π‘ Why We Are Doing This: `terraform init` downloads and indexes child module dependencies into the `.terraform/modules` directory, creating an immutable dependency tree.
π’ Real-World Production Usecase: Automated deployment pipelines verify that all module version locks match security guidelines before planning changes.
βοΈ How Implemented in Production: Production CI systems run `terraform init -get-plugins=true` inside isolated ephemeral containers to guarantee clean, un-tampered module downloading.
4
Multi-Instance Verification & Teardown
GOAL
Verify both module container endpoints and destroy the multi-instance environment.
VERIFICATION
Wait a moment for port binding, then curl both endpoints:
Terminal β Verify Both Containers
# Allow container port binding to settle
sleep 2
# macOS / Linux β Test Frontend App (Port 8086)
curl -I http://localhost:8086
# macOS / Linux β Test Admin App (Port 8087)
curl -I http://localhost:8087
# Windows PowerShell equivalent:
# Invoke-WebRequest -Uri http://localhost:8086
# Invoke-WebRequest -Uri http://localhost:8087
# Destroy managed modular resources
terraform destroy -auto-approveEXPECTED OUTPUT
Verification Success
HTTP/1.1 200 OK returned on both port 8086 and 8087.Destroy complete! Resources: 4 destroyed.πProduction Context & Enterprise Real-World Implementation
π‘ Why We Are Doing This: Verifying independent instances spawned from a shared module guarantees that module parameters do not collide or overwrite global network names.
π’ Real-World Production Usecase: High-scale platforms verify that multi-region microservice deployments spun up via modules pass health checks independently.
βοΈ How Implemented in Production: Automated integration testing tools (like Terratest in Go) spin up ephemeral module instances, run HTTP/API assertions, and run `destroy` upon test completion.