Lab: สร้าง Reusable Web Server Module
📖 text • 30 นาทีLab: สร้าง Reusable Web Server Module
สิ่งที่ต้องมี: Terraform + AWS Credentials ตั้งค่าแล้ว
เป้าหมาย
- สร้าง module สำหรับ web server (EC2 + Security Group)
- ใช้ module สร้าง server 2 ตัวด้วย config ต่างกัน
- ใช้ module จาก Registry ร่วมด้วย
Step 1: สร้างโครงสร้างโปรเจค
mkdir terraform-module-lab && cd terraform-module-lab
mkdir -p modules/web-server
terraform-module-lab/
├── main.tf # Root module
├── variables.tf
├── outputs.tf
└── modules/
└── web-server/ # Child module
├── main.tf
├── variables.tf
└── outputs.tf
Step 2: เขียน Web Server Module
modules/web-server/variables.tf
variable "name" {
description = "Name prefix for resources"
type = string
}
variable "instance_type" {
description = "EC2 instance type"
type = string
default = "t2.micro"
}
variable "ami_id" {
description = "AMI ID"
type = string
}
variable "vpc_id" {
description = "VPC ID"
type = string
}
variable "http_port" {
description = "Port for HTTP traffic"
type = number
default = 80
}
variable "user_data" {
description = "User data script"
type = string
default = ""
}
variable "tags" {
description = "Additional tags"
type = map(string)
default = {}
}
modules/web-server/main.tf
resource "aws_security_group" "this" {
name = "${var.name}-sg"
description = "Security group for ${var.name}"
vpc_id = var.vpc_id
ingress {
description = "HTTP"
from_port = var.http_port
to_port = var.http_port
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
description = "SSH"
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = merge(var.tags, {
Name = "${var.name}-sg"
})
}
resource "aws_instance" "this" {
ami = var.ami_id
instance_type = var.instance_type
vpc_security_group_ids = [aws_security_group.this.id]
user_data = var.user_data
tags = merge(var.tags, {
Name = var.name
})
}
modules/web-server/outputs.tf
output "instance_id" {
description = "EC2 Instance ID"
value = aws_instance.this.id
}
output "public_ip" {
description = "Public IP address"
value = aws_instance.this.public_ip
}
output "security_group_id" {
description = "Security Group ID"
value = aws_security_group.this.id
}
output "url" {
description = "Web URL"
value = "http://${aws_instance.this.public_ip}:${var.http_port}"
}
Step 3: เขียน Root Module
variables.tf
variable "project" {
description = "Project name"
type = string
default = "module-lab"
}
main.tf
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
required_version = ">= 1.0"
}
provider "aws" {
region = "ap-southeast-1"
}
# Data Sources
data "aws_ami" "amazon_linux" {
most_recent = true
owners = ["amazon"]
filter {
name = "name"
values = ["al2023-ami-2023.*-x86_64"]
}
}
data "aws_vpc" "default" {
default = true
}
locals {
common_tags = {
Project = var.project
ManagedBy = "terraform"
}
}
# ========== Web Server 1: Main App (port 80) ==========
module "app_server" {
source = "./modules/web-server"
name = "${var.project}-app"
instance_type = "t2.micro"
ami_id = data.aws_ami.amazon_linux.id
vpc_id = data.aws_vpc.default.id
http_port = 80
user_data = <<-EOF
#!/bin/bash
yum update -y
yum install -y httpd
systemctl start httpd
systemctl enable httpd
cat > /var/www/html/index.html <<HTML
<h1>Main App Server</h1>
<p>Port: 80</p>
<p>Managed by Terraform Module</p>
HTML
EOF
tags = merge(local.common_tags, {
Role = "app"
})
}
# ========== Web Server 2: API Server (port 8080) ==========
module "api_server" {
source = "./modules/web-server"
name = "${var.project}-api"
instance_type = "t2.micro"
ami_id = data.aws_ami.amazon_linux.id
vpc_id = data.aws_vpc.default.id
http_port = 8080
user_data = <<-EOF
#!/bin/bash
yum update -y
yum install -y python3
cat > /home/ec2-user/app.py <<PYTHON
from http.server import HTTPServer, SimpleHTTPRequestHandler
import json
class Handler(SimpleHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
self.send_header('Content-type', 'application/json')
self.end_headers()
response = {"status": "ok", "service": "api", "port": 8080}
self.wfile.write(json.dumps(response).encode())
HTTPServer(('', 8080), Handler).serve_forever()
PYTHON
python3 /home/ec2-user/app.py &
EOF
tags = merge(local.common_tags, {
Role = "api"
})
}
outputs.tf
output "app_url" {
description = "Main App URL"
value = module.app_server.url
}
output "api_url" {
description = "API Server URL"
value = module.api_server.url
}
output "app_ip" {
description = "App Server Public IP"
value = module.app_server.public_ip
}
output "api_ip" {
description = "API Server Public IP"
value = module.api_server.public_ip
}
Step 4: Init & Plan
terraform init
Initializing modules...
- api_server in modules/web-server
- app_server in modules/web-server
Initializing provider plugins...
- Finding hashicorp/aws versions matching "~> 5.0"...
Terraform has been successfully initialized!
Terraform เห็น 2 modules — ทั้งคู่ใช้ source เดียวกัน
terraform plan
Plan: 4 to add, 0 to change, 0 to destroy.
4 resources = 2 Security Groups + 2 EC2 Instances
Step 5: Apply
terraform apply
พิมพ์ yes
Apply complete! Resources: 4 added, 0 changed, 0 destroyed.
Outputs:
api_ip = "54.xx.xx.xx"
api_url = "http://54.xx.xx.xx:8080"
app_ip = "54.yy.yy.yy"
app_url = "http://54.yy.yy.yy:80"
Step 6: ทดสอบ
# ทดสอบ App Server
curl http://<APP_IP>
<h1>Main App Server</h1>
<p>Port: 80</p>
<p>Managed by Terraform Module</p>
# ทดสอบ API Server (รอ 1-2 นาทีให้ user_data ทำงาน)
curl http://<API_IP>:8080
{"status": "ok", "service": "api", "port": 8080}
Step 7: ดู Resource ใน State
terraform state list
data.aws_ami.amazon_linux
data.aws_vpc.default
module.api_server.aws_instance.this
module.api_server.aws_security_group.this
module.app_server.aws_instance.this
module.app_server.aws_security_group.this
สังเกต: resource อยู่ใต้ module.<name> — Terraform จัดกลุ่มให้อัตโนมัติ
Step 8: Cleanup
terraform destroy -auto-approve
cd .. && rm -rf terraform-module-lab
สิ่งที่ได้เรียนรู้
| Concept | สิ่งที่ฝึก |
|---|---|
| Module structure | variables.tf, main.tf, outputs.tf |
| Reusability | ใช้ module เดียวสร้าง server 2 ตัว |
| Module input | ส่ง variables ให้ module |
| Module output | ดึงค่าจาก module ด้วย module.<name>.<output> |
| merge() | รวม tags จาก root + module |
| State | resource ถูกจัดกลุ่มใต้ module.<name> |
สรุปคอร์ส
ยินดีด้วย! คุณเรียนจบ Terraform Fundamentals แล้ว
สิ่งที่ได้เรียนรู้ตลอดคอร์ส:
| Module | หัวข้อ |
|---|---|
| 1. Introduction | IaC concept, Terraform workflow, ติดตั้ง, Lab แรก |
| 2. HCL & Resource | Syntax, Providers, Resource/Data Source, Lab EC2+SG |
| 3. State Management | State, Remote Backend, State Commands, Lab S3 Backend |
| 4. Variables & Outputs | Variables, Outputs, Locals, tfvars, Lab Multi-env |
| 5. Modules | Module concept, เขียน Module, Registry, Lab Reusable Module |
Next Steps
- ฝึกสร้าง infrastructure ของตัวเองด้วย Terraform
- ลองใช้ module จาก Terraform Registry ในโปรเจคจริง
- ศึกษาเพิ่ม: Terraform Cloud, CI/CD pipeline, advanced patterns