Skip to content

Thực hành: Basic Infrastructure

🎯 Mục tiêu

🎯 Sau bài thực hành này, bạn sẽ:

  • Viết Terraform HCL cơ bản với provider và resources
  • Sử dụng variables và outputs
  • Thực hiện workflow init → plan → apply → destroy

Mô tả bài tập

Bạn cần tạo một môi trường development đơn giản trên AWS: VPC, Subnet, Security Group và 1 EC2 instance. Tất cả sử dụng Terraform HCL thay vì console thủ công.

Yêu cầu

Bài 1: Provider & Variables

Tạo cấu trúc project:

lab-infra/
├── main.tf
├── variables.tf
├── outputs.tf
└── terraform.tfvars

variables.tf — Khai báo input variables:

hcl
variable "aws_region" {
  description = "AWS region to deploy"
  type        = string
  default     = "ap-southeast-1"
}

variable "project_name" {
  description = "Project name for tagging"
  type        = string
  # TODO: Thêm validation rule: chỉ cho phép lowercase và dấu gạch ngang
}

variable "environment" {
  description = "Environment name"
  type        = string
  default     = "dev"

  validation {
    condition     = contains(["dev", "staging", "prod"], var.environment)
    error_message = "Environment must be dev, staging, or prod."
  }
}

main.tf — Cấu hình provider:

hcl
terraform {
  required_version = ">= 1.5.0"

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

provider "aws" {
  region = var.aws_region

  default_tags {
    tags = {
      Project     = var.project_name
      Environment = var.environment
      ManagedBy   = "terraform"
    }
  }
}

Bài 2: VPC & Networking

Thêm vào main.tf — tạo VPC và Subnet:

hcl
# TODO: Tạo VPC
# - CIDR: 10.0.0.0/16
# - enable_dns_hostnames = true
# - Tag: Name = "${var.project_name}-vpc"

# TODO: Tạo Public Subnet
# - CIDR: 10.0.1.0/24
# - map_public_ip_on_launch = true
# - availability_zone = "${var.aws_region}a"

# TODO: Tạo Internet Gateway và Route Table
# - Route 0.0.0.0/0 → Internet Gateway
# - Associate Route Table với Subnet

Bài 3: Security Group & EC2

Tạo Security Group và EC2 instance:

hcl
# TODO: Security Group
# - Ingress: SSH (22) từ your IP only
# - Ingress: HTTP (80) từ anywhere
# - Egress: All traffic

# TODO: EC2 Instance
# - AMI: Amazon Linux 2023 (dùng data source)
# - Instance type: t3.micro
# - Subnet: public subnet ở trên
# - Security Group: SG ở trên
# - User data: install nginx

outputs.tf — Export thông tin cần thiết:

hcl
# TODO: Output instance public IP, VPC ID, Subnet ID

Gợi ý

💡 Xem gợi ý
  • Bài 1: Variable validation dùng can(regex(...)) — ví dụ: can(regex("^[a-z][a-z0-9-]*$", var.project_name))
  • Bài 2: Dùng aws_vpc, aws_subnet, aws_internet_gateway, aws_route_table, aws_route_table_association
  • Bài 3: Dùng data "aws_ami" để tìm AMI mới nhất. User data dùng <<-EOF ... EOF heredoc
  • Luôn dùng default_tags trên provider để tag tất cả resources tự động
  • terraform plan trước khi apply — review changes!

Lời giải

✅ Xem lời giải

variables.tf hoàn chỉnh:

hcl
variable "aws_region" {
  description = "AWS region"
  type        = string
  default     = "ap-southeast-1"
}

variable "project_name" {
  description = "Project name for tagging"
  type        = string

  validation {
    condition     = can(regex("^[a-z][a-z0-9-]*$", var.project_name))
    error_message = "Project name must start with lowercase letter, only contain lowercase letters, numbers, and hyphens."
  }
}

variable "environment" {
  description = "Environment name"
  type        = string
  default     = "dev"

  validation {
    condition     = contains(["dev", "staging", "prod"], var.environment)
    error_message = "Environment must be dev, staging, or prod."
  }
}

main.tf hoàn chỉnh:

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

provider "aws" {
  region = var.aws_region
  default_tags {
    tags = {
      Project     = var.project_name
      Environment = var.environment
      ManagedBy   = "terraform"
    }
  }
}

resource "aws_vpc" "main" {
  cidr_block           = "10.0.0.0/16"
  enable_dns_hostnames = true
  tags = { Name = "${var.project_name}-vpc" }
}

resource "aws_subnet" "public" {
  vpc_id                  = aws_vpc.main.id
  cidr_block              = "10.0.1.0/24"
  map_public_ip_on_launch = true
  availability_zone       = "${var.aws_region}a"
  tags = { Name = "${var.project_name}-public-subnet" }
}

resource "aws_internet_gateway" "main" {
  vpc_id = aws_vpc.main.id
  tags   = { Name = "${var.project_name}-igw" }
}

resource "aws_route_table" "public" {
  vpc_id = aws_vpc.main.id
  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.main.id
  }
  tags = { Name = "${var.project_name}-public-rt" }
}

resource "aws_route_table_association" "public" {
  subnet_id      = aws_subnet.public.id
  route_table_id = aws_route_table.public.id
}

data "aws_ami" "amazon_linux" {
  most_recent = true
  owners      = ["amazon"]
  filter {
    name   = "name"
    values = ["al2023-ami-*-x86_64"]
  }
}

resource "aws_security_group" "web" {
  name_prefix = "${var.project_name}-web-"
  vpc_id      = aws_vpc.main.id

  ingress {
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"] # Production: restrict to your IP
  }

  ingress {
    from_port   = 80
    to_port     = 80
    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 = "${var.project_name}-web-sg" }
}

resource "aws_instance" "web" {
  ami                    = data.aws_ami.amazon_linux.id
  instance_type          = "t3.micro"
  subnet_id              = aws_subnet.public.id
  vpc_security_group_ids = [aws_security_group.web.id]

  user_data = <<-EOF
    #!/bin/bash
    yum install -y nginx
    systemctl start nginx
    systemctl enable nginx
  EOF

  tags = { Name = "${var.project_name}-web" }
}

outputs.tf:

hcl
output "instance_public_ip" {
  value = aws_instance.web.public_ip
}

output "vpc_id" {
  value = aws_vpc.main.id
}

output "subnet_id" {
  value = aws_subnet.public.id
}

Liên kết liên quan