AWSリソースをTerraformで構築
ストーリー
「Terraformの基礎は固まった。ここからは実践だ」
木村先輩がアーキテクチャ図を広げた。
「先月AWS上に手動で構築した3層アーキテクチャを覚えているか? あれをTerraformでコード化する。同じ構成を何度でも 再現可能にするんだ」
「手動だと1時間以上かかりましたよね」
「Terraformなら
terraform apply一発で15分だ。 しかも100%同じ構成が保証される」
プロジェクト構成
infrastructure/
├── environments/
│ ├── staging/
│ │ ├── main.tf
│ │ ├── variables.tf
│ │ ├── outputs.tf
│ │ ├── backend.tf
│ │ └── terraform.tfvars
│ └── production/
│ ├── main.tf
│ ├── variables.tf
│ ├── outputs.tf
│ ├── backend.tf
│ └── terraform.tfvars
├── modules/
│ ├── vpc/
│ ├── ec2/
│ ├── rds/
│ └── s3/
└── .gitignore
.gitignore
# Terraform
.terraform/
*.tfstate
*.tfstate.backup
*.tfplan
*.tfvars # 機密情報を含む場合
!terraform.tfvars.example
S3バケットの構築
基本的なS3バケット
hcl
# modules/s3/main.tf
resource "aws_s3_bucket" "this" {
bucket = var.bucket_name
tags = var.tags
}
resource "aws_s3_bucket_versioning" "this" {
bucket = aws_s3_bucket.this.id
versioning_configuration {
status = var.enable_versioning ? "Enabled" : "Suspended"
}
}
resource "aws_s3_bucket_server_side_encryption_configuration" "this" {
bucket = aws_s3_bucket.this.id
rule {
apply_server_side_encryption_by_default {
sse_algorithm = "AES256"
}
}
}
resource "aws_s3_bucket_public_access_block" "this" {
bucket = aws_s3_bucket.this.id
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}hcl
# modules/s3/variables.tf
variable "bucket_name" {
description = "S3バケット名"
type = string
}
variable "enable_versioning" {
description = "バージョニングを有効にするか"
type = bool
default = true
}
variable "tags" {
description = "タグ"
type = map(string)
default = {}
}hcl
# modules/s3/outputs.tf
output "bucket_id" {
value = aws_s3_bucket.this.id
}
output "bucket_arn" {
value = aws_s3_bucket.this.arn
}EC2インスタンスの構築
hcl
# modules/ec2/main.tf
data "aws_ami" "amazon_linux" {
most_recent = true
owners = ["amazon"]
filter {
name = "name"
values = ["al2023-ami-*-x86_64"]
}
filter {
name = "virtualization-type"
values = ["hvm"]
}
}
resource "aws_instance" "this" {
count = var.instance_count
ami = data.aws_ami.amazon_linux.id
instance_type = var.instance_type
subnet_id = var.subnet_ids[count.index % length(var.subnet_ids)]
vpc_security_group_ids = var.security_group_ids
key_name = var.key_name
root_block_device {
volume_size = var.root_volume_size
volume_type = "gp3"
encrypted = true
}
user_data = var.user_data
tags = merge(var.tags, {
Name = "${var.name_prefix}-${count.index + 1}"
})
}hcl
# modules/ec2/variables.tf
variable "name_prefix" {
description = "インスタンス名のプレフィックス"
type = string
}
variable "instance_type" {
description = "インスタンスタイプ"
type = string
default = "t3.micro"
}
variable "instance_count" {
description = "インスタンス数"
type = number
default = 1
}
variable "subnet_ids" {
description = "サブネットID"
type = list(string)
}
variable "security_group_ids" {
description = "セキュリティグループID"
type = list(string)
}
variable "key_name" {
description = "SSHキーペア名"
type = string
default = null
}
variable "root_volume_size" {
description = "ルートボリュームサイズ(GB)"
type = number
default = 20
}
variable "user_data" {
description = "ユーザーデータスクリプト"
type = string
default = null
}
variable "tags" {
description = "タグ"
type = map(string)
default = {}
}RDSの構築
hcl
# modules/rds/main.tf
resource "aws_db_subnet_group" "this" {
name = "${var.name_prefix}-db-subnet"
subnet_ids = var.subnet_ids
tags = var.tags
}
resource "aws_db_instance" "this" {
identifier = "${var.name_prefix}-db"
engine = var.engine
engine_version = var.engine_version
instance_class = var.instance_class
allocated_storage = var.allocated_storage
max_allocated_storage = var.max_allocated_storage
storage_type = "gp3"
storage_encrypted = true
db_name = var.database_name
username = var.master_username
password = var.master_password
multi_az = var.multi_az
db_subnet_group_name = aws_db_subnet_group.this.name
vpc_security_group_ids = var.security_group_ids
backup_retention_period = var.backup_retention_period
backup_window = "03:00-04:00"
maintenance_window = "mon:04:00-mon:05:00"
deletion_protection = var.deletion_protection
skip_final_snapshot = var.environment != "production"
final_snapshot_identifier = var.environment == "production" ? "${var.name_prefix}-final-snapshot" : null
tags = var.tags
}モジュールの組み立て
hcl
# environments/staging/main.tf
terraform {
required_version = ">= 1.7.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
provider "aws" {
region = var.aws_region
default_tags {
tags = {
Environment = var.environment
ManagedBy = "terraform"
Project = var.project_name
}
}
}
# VPC モジュール
module "vpc" {
source = "../../modules/vpc"
name = "${var.project_name}-${var.environment}"
cidr_block = var.vpc_cidr
public_subnets = var.public_subnet_cidrs
private_subnets = var.private_subnet_cidrs
}
# EC2 モジュール
module "web" {
source = "../../modules/ec2"
name_prefix = "${var.project_name}-${var.environment}-web"
instance_type = var.web_instance_type
instance_count = var.web_instance_count
subnet_ids = module.vpc.public_subnet_ids
security_group_ids = [module.vpc.web_security_group_id]
user_data = <<-EOF
#!/bin/bash
yum update -y
yum install -y httpd
systemctl start httpd
systemctl enable httpd
EOF
}
# S3 モジュール
module "assets" {
source = "../../modules/s3"
bucket_name = "${var.project_name}-${var.environment}-assets"
enable_versioning = true
}
# RDS モジュール
module "database" {
source = "../../modules/rds"
name_prefix = "${var.project_name}-${var.environment}"
environment = var.environment
engine = "mysql"
engine_version = "8.0"
instance_class = var.db_instance_class
allocated_storage = 20
database_name = "app"
master_username = "admin"
master_password = var.db_password
multi_az = var.environment == "production"
subnet_ids = module.vpc.private_subnet_ids
security_group_ids = [module.vpc.db_security_group_id]
deletion_protection = var.environment == "production"
}まとめ
| ポイント | 内容 |
|---|---|
| S3 | バケット + 暗号化 + バージョニング + パブリックアクセスブロック |
| EC2 | AMIデータソース + count による複数台構築 |
| RDS | サブネットグループ + マルチAZ + バックアップ設定 |
| 組み立て | モジュールを組み合わせて環境を構築 |
チェックリスト
- S3バケットをTerraformで構築し、セキュリティ設定を適用できる
- EC2インスタンスをモジュール化して複数台構築できる
- RDSをTerraformで構築し、環境に応じた設定を適用できる
- モジュールを組み合わせたプロジェクト構成を理解した
次のステップへ
次のセクションでは、VPCネットワーク全体をTerraformでコード化する方法を学びます。
推定読了時間: 40分