はじめに
Terraformを学び始めた方が次にステップアップとして挑戦する、「VPC・EC2・RDS」を用いた3層アーキテクチャの構築方法を解説します。
単にリソースを作るだけでなく、実務で必須となる以下の要素を盛り込んだ「そのまま使える」構成を目指します。
- モジュール化: 再利用性を高めるディレクトリ構造
- 脱踏み台サーバー: AWS Systems Manager (SSM) によるセキュアなログイン
- 秘匿情報の管理: AWS Secrets Manager を使ったRDSパスワードの注入
ディレクトリ構成
環境ごとに設定を分離し、共通の処理はmodulesに切り出す構成です。
terraform/
├── envs/
│ └── dev/ # 開発環境用設定
│ ├── main.tf # モジュールの呼び出し
│ ├── variables.tf
│ ├── terraform.tfvars # 具体的なパラメータ
│ └── backend.tf # S3バックエンド設定
└── modules/
├── vpc/ # ネットワーク基盤(SG含む)
├── ec2/ # SSM対応インスタンス
└── rds/ # PostgreSQLインスタンス
modules/vpc:セキュアなネットワーク基盤
システムの土台となるVPC、サブネット、ゲートウェイ、およびリソース間の通信を制御するセキュリティグループを定義します。
- パブリック/プライベートサブネットの分離による多層防御
- セキュリティグループによる「Web層からDB層への特定ポート許可」の定義
resource "aws_vpc" "this" {
cidr_block = var.vpc_cidr
enable_dns_support = true
enable_dns_hostnames = true
tags = { Name = "${var.project}-${var.env}-vpc" }
}
# EC2用セキュリティグループ
resource "aws_security_group" "web" {
name = "${var.project}-${var.env}-web-sg"
vpc_id = aws_vpc.this.id
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"]
}
}
# RDS用セキュリティグループ (Web-SGからの接続のみ許可)
resource "aws_security_group" "db" {
name = "${var.project}-${var.env}-db-sg"
vpc_id = aws_vpc.this.id
ingress {
from_port = 5432
to_port = 5432
protocol = "tcp"
security_groups = [aws_security_group.web.id]
}
}
modules/ec2:SSM管理による「踏み台レス」サーバー
ec2にSSM権限を付与します
resource "aws_instance" "this" {
ami = var.ami_id
instance_type = var.instance_type
subnet_id = var.subnet_id
vpc_security_group_ids = var.security_group_ids
iam_instance_profile = aws_iam_instance_profile.ssm_profile.name
metadata_options {
http_endpoint = "enabled"
http_tokens = "required"
}
tags = { Name = "${var.project}-${var.env}-ec2" }
}
# SSM用のIAMロール定義
resource "aws_iam_role" "ssm_role" {
name = "${var.project}-${var.env}-ssm-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [{ Action = "sts:AssumeRole", Effect = "Allow", Principal = { Service = "ec2.amazonaws.com" } }]
})
}
resource "aws_iam_role_policy_attachment" "ssm_managed" {
role = aws_iam_role.ssm_role.name
policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
}
modules/rds:データベース
PostgreSQLを使用したデータベースを構築します。
外部からの直接通信を遮断する非公開設定。
パスワードを変数化し、上位環境から動的に注入。
- 外部からの直接通信を遮断する非公開設定
- パスワードを変数化し、上位環境から動的に注入
resource "aws_db_subnet_group" "this" {
name = "${var.project}-${var.env}-rds-subnet-group"
subnet_ids = var.subnet_ids
}
resource "aws_db_instance" "this" {
identifier = var.identifier
engine = var.engine
engine_version = var.engine_version
instance_class = var.instance_class
allocated_storage = 20
db_name = var.database_name
username = var.master_username
password = var.master_password # 変数から注入
db_subnet_group_name = aws_db_subnet_group.this.name
vpc_security_group_ids = var.security_group_ids
publicly_accessible = false
skip_final_snapshot = true
}
envs/dev:環境固有の設定とシークレット管理
各モジュールを組み合わせて「開発環境」として統合し、パスワードなどの機密情報を安全に渡します。
# Secrets Managerからパスワードを読み取る
data "aws_secretsmanager_secret" "rds_password" {
name = "${var.project}/${var.env}/rds-password"
}
data "aws_secretsmanager_secret_version" "rds_password" {
secret_id = data.aws_secretsmanager_secret.rds_password.id
}
module "vpc" {
source = "../../modules/vpc"
vpc_cidr = var.vpc_cidr
# ... subnetの設定など
}
module "rds" {
source = "../../modules/rds"
master_password = data.aws_secretsmanager_secret_version.rds_password.secret_string
vpc_id = module.vpc.vpc_id
subnet_ids = module.vpc.private_subnet_ids
security_group_ids = [module.vpc.db_sg_id]
}
module "ec2" {
source = "../../modules/ec2"
subnet_id = module.vpc.private_subnet_ids[0]
security_group_ids = [module.vpc.web_sg_id]
}
まとめ:実務に耐えうるTerraform構成を目指して
今回の構成では、単にリソースをコード化するだけでなく、実務現場で求められる「保守性」「セキュリティ」「再利用性」に焦点を当てました。
今回の構成のメリット
-
環境のポータビリティ: envs/prod/ を新しく作るだけで、同じ構成の本番環境を数分で作成可能です
-
踏み台サーバーからの脱却: SSMを活用することで、SSHポートを公開するリスクを排除しました
-
機密情報の適切な管理: Secrets Manager を使い、Git管理からパスワードを完全に切り離しました
TerraformによるInfrastructure as Code(IaC)は、エンジニアの工数を大幅に削減し、ヒューマンエラーを防ぐ強力な武器になります。ぜひ、ご自身のプロジェクトに合わせてこのテンプレートをカスタマイズしてみてください。