Root vs Child Modules
Module = group ของ Terraform code ที่ reuse ได้ — เหมือน function ในการเขียนโปรแกรม
Module คืออะไร?
ทุก folder ที่มีไฟล์ .tf = module ในตัวของมันเอง
my-project/
├── main.tf # ← root module
├── variables.tf
└── outputs.tf
Root Module
Root module = directory ที่คุณรัน terraform apply
my-project/ # ← root module
├── main.tf
├── variables.tf
└── outputs.tf
ตอนรัน:
cd my-project
terraform init
terraform apply
→ Terraform เรียก main.tf, variables.tf, outputs.tf ใน folder นี้
Child Module
Child module = module ที่ root module เรียกใช้
my-project/
├── main.tf # ← root module
├── variables.tf
├── outputs.tf
└── modules/
└── network/ # ← child module
├── main.tf
├── variables.tf
└── outputs.tf
main.tf (root)
module "network" {
source = "./modules/network"
vpc_cidr = "10.0.0.0/16"
azs = ["ap-southeast-1a", "ap-southeast-1b"]
}
→ Root เรียก network module, ส่ง variables เข้าไป, รับ outputs ออกมา
Hierarchy
Module Structure (Convention)
modules/network/
├── README.md # docs
├── main.tf # main resources
├── variables.tf # input variables
├── outputs.tf # output values
├── versions.tf # provider requirements
└── examples/ # usage examples
└── basic/
└── main.tf
ตัวอย่างเต็ม: Root + Child
Child Module: Network
modules/network/variables.tf
variable "vpc_cidr" {
type = string
description = "VPC CIDR block"
}
variable "azs" {
type = list(string)
description = "Availability zones"
}
variable "name" {
type = string
description = "Network name prefix"
}
modules/network/main.tf
resource "aws_vpc" "main" {
cidr_block = var.vpc_cidr
tags = {
Name = "${var.name}-vpc"
}
}
resource "aws_subnet" "public" {
count = length(var.azs)
vpc_id = aws_vpc.main.id
cidr_block = cidrsubnet(var.vpc_cidr, 8, count.index)
availability_zone = var.azs[count.index]
tags = {
Name = "${var.name}-public-${count.index + 1}"
}
}
resource "aws_internet_gateway" "main" {
vpc_id = aws_vpc.main.id
tags = {
Name = "${var.name}-igw"
}
}
modules/network/outputs.tf
output "vpc_id" {
value = aws_vpc.main.id
}
output "public_subnet_ids" {
value = aws_subnet.public[*].id
}
output "vpc_cidr" {
value = aws_vpc.main.cidr_block
}
Root Module
main.tf
provider "aws" {
region = "ap-southeast-1"
}
module "network" {
source = "./modules/network"
name = "prod"
vpc_cidr = "10.0.0.0/16"
azs = ["ap-southeast-1a", "ap-southeast-1b"]
}
resource "aws_instance" "web" {
ami = "ami-12345"
instance_type = "t2.micro"
subnet_id = module.network.public_subnet_ids[0] # ← ใช้ output จาก module
tags = {
Name = "web"
}
}
outputs.tf (root)
output "vpc_id" {
value = module.network.vpc_id
}
output "web_instance_id" {
value = aws_instance.web.id
}
Module Sources
ตัว source รองรับหลายรูปแบบ:
1. Local Path
module "network" {
source = "./modules/network"
}
module "shared" {
source = "../../modules/shared"
}
2. Terraform Registry
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "5.13.0"
}
3. GitHub
module "vpc" {
source = "github.com/myorg/terraform-modules//vpc?ref=v1.2.0"
}
module "vpc_https" {
source = "git::https://github.com/myorg/terraform-modules.git//vpc?ref=v1.2.0"
}
module "vpc_ssh" {
source = "git::ssh://[email protected]/myorg/terraform-modules.git//vpc?ref=v1.2.0"
}
4. Bitbucket / GitLab
module "vpc" {
source = "git::https://example.gitlab.com/group/repo.git//modules/vpc?ref=v1.0"
}
5. HTTP (zip/tar.gz)
module "vpc" {
source = "https://example.com/modules/vpc-1.0.0.zip"
}
6. S3
module "vpc" {
source = "s3::https://my-bucket.s3.amazonaws.com/modules/vpc.zip"
}
7. Mercurial
module "vpc" {
source = "hg::http://example.com/repo"
}
Module Versioning
Local Module
ไม่มี versioning — ใช้ git tag/branch ของ repo เอง
Registry Module ⭐ (recommended for prod)
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "~> 5.13" # version constraint
}
Git Module
module "vpc" {
source = "git::https://github.com/myorg/modules.git//vpc?ref=v1.2.0"
^^^^^^^^^^
ใช้ tag, branch, หรือ commit hash
}
Pin Module Version!
ห้าม use master / main branch — ใช้ tag หรือ commit hash:
# ❌
source = "git::...//vpc?ref=main"
# ✅
source = "git::...//vpc?ref=v1.2.0"
When to Use Modules?
✅ ใช้ Module เมื่อ:
- Deploy resource pattern เดิมหลายครั้ง (VPC ในหลาย env)
- ต้องการ standardize ข้ามทีม
- เป็น "platform team" ที่ provide infrastructure ให้ทีมอื่น
- Code เกิน 200-300 บรรทัด
❌ ไม่ใช้ Module เมื่อ:
- Code เล็ก ไม่ reuse
- Resource เดียวที่ unique
- Premature abstraction (ทำ module ก่อนรู้ว่าจะ reuse จริงไหม)
Module vs Resource (อย่าสับสน)
# Resource = single resource
resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
}
# Module = collection of resources
module "network" {
source = "./modules/network"
# ใน module มี aws_vpc + aws_subnet + aws_internet_gateway + ...
}
ตัวอย่าง: Multi-Module Project
my-app/
├── main.tf # root — wire modules together
├── variables.tf
├── outputs.tf
└── modules/
├── network/ # VPC + subnets + IGW
│ ├── main.tf
│ ├── variables.tf
│ └── outputs.tf
├── database/ # RDS + ElastiCache
│ ├── main.tf
│ ├── variables.tf
│ └── outputs.tf
└── compute/ # EC2 + ASG + ALB
├── main.tf
├── variables.tf
└── outputs.tf
main.tf (root)
module "network" {
source = "./modules/network"
# ...
}
module "database" {
source = "./modules/database"
vpc_id = module.network.vpc_id
subnet_ids = module.network.private_subnet_ids
}
module "compute" {
source = "./modules/compute"
vpc_id = module.network.vpc_id
subnet_ids = module.network.public_subnet_ids
db_endpoint = module.database.endpoint
}
สรุป
- Root module = directory ที่รัน
terraform apply - Child module = module ที่ root เรียกใช้
- Source: local, registry, GitHub, Git, HTTP, S3
- Pin version เสมอ (especially for production)
- Module = function สำหรับ infrastructure — reuse + standardize
ต่อไป → Using Published Modules