เมื่อ project HCL เริ่มใหญ่ขึ้น การจัดระเบียบโครงสร้างให้ดีเป็นสิ่งสำคัญ — ถ้าไฟล์กระจัดกระจายหรือตั้งชื่อมั่วจะทำให้ทีมทำงานร่วมกันได้ยาก, review code ช้า, และเสี่ยงเกิด drift ระหว่าง environment
บทความนี้รวบรวม best practices สำหรับการจัดระเบียบโครงสร้าง project ที่ใช้งานจริงในทีม — ตั้งแต่การตั้งชื่อไฟล์ การแยก environment การจัด module และการทำให้ code อ่านง่ายและ maintain ได้ในระยะยาว
โครงสร้างพื้นฐานของ Project
โครงสร้างมาตรฐานที่แนะนำในทุก project (ไม่ว่าขนาดเล็กหรือใหญ่):
project-root/
├── main.tf # resource หลัก
├── variables.tf # input variable ทั้งหมด
├── outputs.tf # output value ทั้งหมด
├── versions.tf # required_providers, required_version
├── locals.tf # local values (ถ้ามี)
├── terraform.tfvars # ค่า variable สำหรับ env ปัจจุบัน
├── README.md # อธิบายการใช้
└── .gitignore # ignore state, .terraform/
การตั้งชื่อไฟล์ (File Naming)
ใช้ชื่อไฟล์ตามหน้าที่ (role-based) ไม่ใช่ตาม technology — ทำให้อ่านง่ายกว่า
- main.tf — resource หลักของ config นี้
- variables.tf — ทุก
variableblock รวมที่เดียว - outputs.tf — ทุก
outputblock รวมที่เดียว - versions.tf —
terraform {}block (required_version, required_providers) - locals.tf —
locals {}block (ถ้ามีหลายตัว) - providers.tf —
providerblock (ถ้าไม่ต้องการปนกับ versions.tf)
สำหรับ project ใหญ่ที่ main.tf ยาวเกินไป ให้แยกตาม domain: network.tf, compute.tf, database.tf, iam.tf — หลีกเลี่ยงการแยกตาม resource ตัวเดียว (เช่น vpc.tf, subnet.tf) เพราะจะมีไฟล์เยอะเกินจำเป็น
แยก Environment (dev / staging / prod)
มี 2 แนวทางหลัก — Workspace หรือ Separate Directory
แนวทาง 1: Separate Directory (แนะนำสำหรับทีมใหญ่)
infrastructure/
├── modules/
│ ├── vpc/
│ ├── ec2-cluster/
│ └── rds/
└── environments/
├── dev/
│ ├── main.tf
│ ├── variables.tf
│ ├── terraform.tfvars
│ └── backend.tf
├── staging/
│ ├── main.tf
│ ├── ...
└── prod/
├── main.tf
├── ...
- ข้อดี — แยก state ชัดเจน, เปลี่ยน config prod โดยไม่กระทบ dev, control access ต่อ directory ได้
- ข้อเสีย — ต้องระวัง drift ถ้าแก้ config ซ้ำกันหลาย env (แก้ dev ลืมแก้ prod)
แนวทาง 2: Workspace (เหมาะกับ project เล็ก)
terraform workspace new dev
terraform workspace new staging
terraform workspace new prod
# ใน code:
resource "aws_instance" "web" {
instance_type = terraform.workspace == "prod" ? "t3.large" : "t3.micro"
}
- ข้อดี — ใช้ code เดียวจัดการหลาย env, ง่ายสำหรับ project เล็ก
- ข้อเสีย — เสี่ยง apply ผิด workspace, ไม่เหมาะกับ config ที่ต่างกันมากระหว่าง env
Module Organization
โครงสร้าง module ที่แนะนำ:
modules/
├── vpc/
│ ├── main.tf # resource ของ module
│ ├── variables.tf # input
│ ├── outputs.tf # output
│ ├── versions.tf # required_providers
│ └── README.md # วิธีใช้ + ตัวอย่าง
├── ec2-cluster/
│ ├── ...
└── rds/
├── ...
- แต่ละ module ควรทำหน้าที่เดียวที่ชัดเจน (Single Responsibility)
- ถ้า module ใหญ่เกินไป ให้แยกเป็น module ย่อย (submodules) แทน
- versions.tf ใน module ควรระบุเฉพาะ required_providers — ไม่กำหนด required_version เฉพาะเจาะจง (ให้ root module ควบคุม)
Variable และ Output ที่ดี
- ทุก variable ต้องมี
description— บอกว่าใช้ทำอะไร - ใช้
typeทุกครั้ง (string, number, list, map, object) — ไม่ใช่ปล่อยเป็น any - มี
defaultสำหรับค่าที่ optional — required variable ต้องไม่มี default - ใช้
validationblock สำหรับ input ที่มี rule เฉพาะ (เช่น ต้องเริ่มด้วย “prod-“) - กำหนด
sensitive = trueสำหรับ value ที่ไม่ควร print ลง log
variable "environment" {
type = string
description = "Deployment environment (dev, staging, prod)"
validation {
condition = contains(["dev", "staging", "prod"], var.environment)
error_message = "environment must be one of: dev, staging, prod"
}
}
variable "db_password" {
type = string
description = "Database admin password"
sensitive = true
}
Naming Convention
- Resource name: ใช้ snake_case เสมอ (เช่น
web_serverไม่ใช่WebServer) - ชื่อ resource ควรบอกบทบาท ไม่ต้องซ้ำ type — ผิด:
resource "aws_instance" "aws_instance_web"→ ถูก:resource "aws_instance" "web" - ใช้ prefix/suffix แสดง environment ใน tag (ไม่ใช่ในชื่อ resource) — เช่น
Name = "${var.environment}-web-server" - ชื่อ variable ใช้ snake_case, อธิบายหน้าที่ชัดเจน:
db_instance_classไม่ใช่แค่type
ใช้ Tag Consistent
กำหนด common tag ที่ locals แล้ว merge กับ tag เฉพาะของแต่ละ resource
locals {
common_tags = {
Project = var.project_name
Environment = var.environment
ManagedBy = "Terraform"
Owner = var.team_email
}
}
resource "aws_instance" "web" {
# ...
tags = merge(local.common_tags, {
Name = "${var.environment}-web-server"
Role = "frontend"
})
}
ควบคุม Provider Version
pin version ของ HCL CLI และ provider ไว้เสมอ เพื่อให้ทีม deploy ได้ผลลัพธ์เหมือนกัน
# versions.tf
terraform {
required_version = "~> 1.6.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.30"
}
random = {
source = "hashicorp/random"
version = "~> 3.5"
}
}
}
.gitignore ที่จำเป็น
# Terraform
.terraform/
.terraform.lock.hcl.bak
*.tfstate
*.tfstate.*
*.tfvars.local
crash.log
crash.*.log
override.tf
override.tf.json
*_override.tf
*_override.tf.json
# IDE
.idea/
.vscode/
ยกเว้น — .terraform.lock.hcl ต้อง commit (ไม่ใส่ใน .gitignore) เพื่อให้ provider version lock ตรงกันทั้งทีม
README.md ที่ดี
ควรมีอย่างน้อย:
- อธิบาย project ทำอะไร deploy อะไรบ้าง
- Prerequisite (HCL version, provider credentials, AWS account)
- วิธี init / plan / apply สำหรับแต่ละ environment
- รายการ variable หลัก (หรือลิงก์ไป
terraform-docsที่ generate อัตโนมัติ) - Troubleshooting พื้นฐาน
Best Practices สรุป
- ใช้โครงสร้างไฟล์มาตรฐาน (main/variables/outputs/versions) ทุก project
- แยก environment ด้วย directory สำหรับทีมใหญ่ ใช้ workspace เฉพาะ project เล็ก
- Module ต้องมี single responsibility + README อธิบายการใช้งาน
- Variable ต้องมี description, type, validation (ถ้ามี rule)
- Naming: snake_case, บอกบทบาท, ไม่ซ้ำ type
- ใช้ common tag ผ่าน locals + merge
- Pin version ของ HCL CLI และ provider
- commit
.terraform.lock.hclแต่ ignore state file และ.terraform/ - เขียน README ให้คนใหม่เริ่มทำงานได้เอง
สรุป
การจัดระเบียบโครงสร้าง project ให้ดีเป็นรากฐานของการใช้งาน HCL ในระดับทีม — โครงสร้างไฟล์มาตรฐาน, การแยก environment ที่ชัดเจน, module ที่มี single responsibility และ naming convention ที่เป็นระเบียบช่วยให้ทีม onboard คนใหม่ได้เร็วและลดความเสี่ยง config drift ในบทความถัดไปจะพูดถึง code style และการ format code ด้วย terraform fmt เพื่อให้ code อ่านง่ายและสม่ำเสมอทั้งทีม

