Lab: สร้าง EC2 + Security Group

📖 text • 25 นาที

Lab: สร้าง EC2 + Security Group

สิ่งที่ต้องมี: Terraform + AWS Credentials ตั้งค่าแล้ว (ดูบท Install Terraform)

เป้าหมาย

สร้าง EC2 Instance พร้อม Security Group ที่เปิด port 80 (HTTP) — ฝึกการเขียน resource หลายตัวที่เชื่อมกัน

Step 1: สร้างโปรเจค

mkdir terraform-ec2-lab && cd terraform-ec2-lab

Step 2: Provider Configuration

สร้างไฟล์ main.tf:

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }

  required_version = ">= 1.0"
}

provider "aws" {
  region = "ap-southeast-1"
}

Step 3: Data Source — หา AMI และ VPC

เพิ่มต่อใน main.tf:

# หา Amazon Linux 2023 AMI ล่าสุด
data "aws_ami" "amazon_linux" {
  most_recent = true
  owners      = ["amazon"]

  filter {
    name   = "name"
    values = ["al2023-ami-2023.*-x86_64"]
  }

  filter {
    name   = "virtualization-type"
    values = ["hvm"]
  }
}

# ใช้ Default VPC
data "aws_vpc" "default" {
  default = true
}

Step 4: Security Group

เพิ่มต่อ:

resource "aws_security_group" "web" {
  name        = "web-sg"
  description = "Allow HTTP and SSH"
  vpc_id      = data.aws_vpc.default.id

  # อนุญาต HTTP จากทุกที่
  ingress {
    description = "HTTP"
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  # อนุญาต SSH จากทุกที่ (lab เท่านั้น — production ควรจำกัด IP)
  ingress {
    description = "SSH"
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  # อนุญาต outbound ทั้งหมด
  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }

  tags = {
    Name = "web-sg"
  }
}

อธิบาย Security Group

Rule ความหมาย
ingress port 80 อนุญาตคนเข้าเว็บ (HTTP)
ingress port 22 อนุญาต SSH เข้า server
egress all อนุญาต server ออกไปข้างนอกได้ทุก port
cidr_blocks = ["0.0.0.0/0"] จากทุก IP address

Step 5: EC2 Instance

เพิ่มต่อ:

resource "aws_instance" "web" {
  ami                    = data.aws_ami.amazon_linux.id
  instance_type          = "t2.micro"
  vpc_security_group_ids = [aws_security_group.web.id]

  user_data = <<-EOF
    #!/bin/bash
    yum update -y
    yum install -y httpd
    systemctl start httpd
    systemctl enable httpd
    echo "<h1>Hello from Terraform!</h1><p>Instance: $(hostname)</p>" > /var/www/html/index.html
  EOF

  tags = {
    Name        = "web-server"
    Environment = "lab"
    ManagedBy   = "terraform"
  }
}

อธิบาย

  • ami = ใช้ AMI ที่หาจาก data source (ไม่ต้อง hardcode)
  • vpc_security_group_ids = ผูก Security Group ที่สร้างไว้
  • user_data = script ที่รันตอน instance เปิดครั้งแรก — ติดตั้ง Apache แล้วสร้างหน้าเว็บ

Step 6: Output

เพิ่มต่อ:

output "instance_id" {
  description = "ID ของ EC2 instance"
  value       = aws_instance.web.id
}

output "public_ip" {
  description = "Public IP ของ EC2 instance"
  value       = aws_instance.web.public_ip
}

output "web_url" {
  description = "URL สำหรับเปิดเว็บ"
  value       = "http://${aws_instance.web.public_ip}"
}

output "ami_used" {
  description = "AMI ที่ใช้"
  value       = data.aws_ami.amazon_linux.name
}

Step 7: Init & Plan

terraform init
terraform plan

ดู plan ให้ดี — ควรเห็น:

Plan: 2 to add, 0 to change, 0 to destroy.

2 resource = Security Group + EC2 Instance

Step 8: Apply

terraform apply

พิมพ์ yes แล้วรอ — EC2 ใช้เวลาสร้างประมาณ 30 วินาที

Apply complete! Resources: 2 added, 0 changed, 0 destroyed.

Outputs:

ami_used    = "al2023-ami-2023.x.x-x86_64"
instance_id = "i-0abc123def456789"
public_ip   = "54.xx.xx.xx"
web_url     = "http://54.xx.xx.xx"

Step 9: ทดสอบ

เปิด URL จาก output web_url ในเบราว์เซอร์:

curl http://<PUBLIC_IP>

ควรเห็น:

<h1>Hello from Terraform!</h1><p>Instance: ip-xxx-xxx-xxx-xxx</p>

หมายเหตุ: อาจต้องรอ 1-2 นาทีให้ user_data ทำงานเสร็จ

Step 10: ดูข้อมูลใน State

# ดู resource ทั้งหมด
terraform state list
data.aws_ami.amazon_linux
data.aws_vpc.default
aws_instance.web
aws_security_group.web
# ดูรายละเอียด EC2
terraform state show aws_instance.web

Step 11: ลอง Modify

เพิ่ม tag ให้ EC2:

  tags = {
    Name        = "web-server"
    Environment = "lab"
    ManagedBy   = "terraform"
    Course      = "terraform-fundamentals"  # เพิ่มบรรทัดนี้
  }
terraform plan
  ~ resource "aws_instance" "web" {
      ~ tags = {
          + "Course" = "terraform-fundamentals"
        }
    }

Plan: 0 to add, 1 to change, 0 to destroy.

~ = update in-place (ไม่ต้องสร้างใหม่ แค่เพิ่ม tag)

terraform apply -auto-approve

Step 12: Destroy

สำคัญ: ลบ resource ทิ้งเมื่อเสร็จ lab เพื่อไม่ให้เสียค่าใช้จ่าย

terraform destroy
Plan: 0 to add, 0 to change, 2 to destroy.

Do you really want to destroy all resources?
  Enter a value: yes

Destroy complete! Resources: 2 destroyed.

Cleanup

cd .. && rm -rf terraform-ec2-lab

สิ่งที่ได้เรียนรู้

Concept สิ่งที่ฝึก
Data Source หา AMI และ VPC ที่มีอยู่
Security Group สร้าง firewall rules (ingress/egress)
EC2 Instance สร้าง server พร้อม user_data
Resource Reference เชื่อม SG กับ EC2 ด้วย aws_security_group.web.id
Output แสดงข้อมูลหลัง apply (IP, URL)
Modify เพิ่ม tag โดยไม่ต้องสร้าง resource ใหม่

ไฟล์ main.tf เต็ม

คลิกเพื่อดูโค้ดทั้งหมด
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }

  required_version = ">= 1.0"
}

provider "aws" {
  region = "ap-southeast-1"
}

data "aws_ami" "amazon_linux" {
  most_recent = true
  owners      = ["amazon"]

  filter {
    name   = "name"
    values = ["al2023-ami-2023.*-x86_64"]
  }

  filter {
    name   = "virtualization-type"
    values = ["hvm"]
  }
}

data "aws_vpc" "default" {
  default = true
}

resource "aws_security_group" "web" {
  name        = "web-sg"
  description = "Allow HTTP and SSH"
  vpc_id      = data.aws_vpc.default.id

  ingress {
    description = "HTTP"
    from_port   = 80
    to_port     = 80
    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 = {
    Name = "web-sg"
  }
}

resource "aws_instance" "web" {
  ami                    = data.aws_ami.amazon_linux.id
  instance_type          = "t2.micro"
  vpc_security_group_ids = [aws_security_group.web.id]

  user_data = <<-EOF
    #!/bin/bash
    yum update -y
    yum install -y httpd
    systemctl start httpd
    systemctl enable httpd
    echo "<h1>Hello from Terraform!</h1><p>Instance: $(hostname)</p>" > /var/www/html/index.html
  EOF

  tags = {
    Name        = "web-server"
    Environment = "lab"
    ManagedBy   = "terraform"
  }
}

output "instance_id" {
  description = "ID ของ EC2 instance"
  value       = aws_instance.web.id
}

output "public_ip" {
  description = "Public IP ของ EC2 instance"
  value       = aws_instance.web.public_ip
}

output "web_url" {
  description = "URL สำหรับเปิดเว็บ"
  value       = "http://${aws_instance.web.public_ip}"
}

output "ami_used" {
  description = "AMI ที่ใช้"
  value       = data.aws_ami.amazon_linux.name
}

สรุป

คุณเพิ่งสร้าง infrastructure หลายชิ้นที่เชื่อมกัน: Data Source → Security Group → EC2 Instance

นี่คือ pattern ที่ใช้ในงานจริง — สร้าง resource หลายตัวแล้วเชื่อมกันด้วย reference

บทถัดไปเราจะเรียนรู้เรื่อง State Management