🕌

Terraform × AWS で開発基盤を構築してみた(VPC / EC2 / RDS / SecretsManager)

2025/03/22に公開

Terraform を用いて、AWS 上に開発環境の基盤を構築した実録ブログです。
構成は最小限ながらも実用的で、VPC → EC2(Bastion/App)→ RDS(MySQL)→ SecretsManager連携までカバー。
また、途中で起きたトラブルの原因と対処法もふまえて、構造的かつ実践的に解説しています。


📘 Part 1: AWS基盤のTerraform構築編

✅ 構成概要

VPC(10.0.0.0/24)
├── Public Subnet(10.0.1.0/28)
│   ├── NAT Gateway
│   └── EC2(Bastion、SSH踏み台)
└── Private Subnet(10.0.2.0/28)
    ├── App EC2(アプリ用)
    └── RDS(MySQL)
  • リージョン:ap-northeast-1
  • OS:Ubuntu 20.04(AMI: ubuntu-focal-20.04
  • 認証:SSH Key / SecretsManager(後述)

✅ Terraformファイル構成

main.tf        → VPC, Subnet などのネットワーク系
ec2.tf         → EC2(Bastion / App)
rds.tf         → RDSインスタンスおよびDBサブネットグループ
secrets.tf     → Secrets Managerおよびrandom_password
variables.tf   → 各種変数定義
outputs.tf     → 作成したリソースの確認用出力(terraform output 用)

outputs.tf の活用ポイント

  • 疎通確認時に EC2のIP や RDSエンドポイント、生成されたパスワードなどを即確認できて便利
  • 例:
output "bastion_public_ip" {
  value = aws_instance.bastion.public_ip
}

output "rds_endpoint" {
  value = aws_db_instance.mysql.endpoint
}

✅ SSHキー作成とBastionへの接続

aws ec2 create-key-pair \
  --key-name sample-dev-ssh-key \
  --query 'KeyMaterial' \
  --output text > sample-dev-ssh-key.pem

chmod 400 sample-dev-ssh-key.pem

ssh -i sample-dev-ssh-key.pem ubuntu@<bastion_public_ip>

✅ App EC2へのSSH(Bastion内から)

scp -i sample-dev-ssh-key.pem sample-dev-ssh-key.pem ubuntu@<bastion_ip>:/home/ubuntu/
chmod 400 sample-dev-ssh-key.pem
ssh -i sample-dev-ssh-key.pem ubuntu@<app_private_ip>

📘 Part 2: RDS + SecretsManager接続トラブル解決編

✅ 構成の意図

Terraformで以下を自動作成:

  1. ランダムなパスワード(random_password)
  2. Secrets Manager に保存
  3. RDS にそのSecretsの username / password を使って接続設定

❌ 発生したエラー(よくある)

ERROR 1045 (28000): Access denied for user 'sample_admin'@'10.0.2.X'

🔍 原因分析

Terraformのリソースは基本的に並列に実行されるため、
random_password → secretsmanager → rds の流れに 明示的な依存関係(depends_on) がないと、

  • RDS作成時に SecretsManager の値が まだ確定していない
  • 結果として、 空パスワード or 初期値 で作成されてしまう
  • → 実際のSecretsManagerとは不一致になり、接続エラー になる

✅ 解決策:random_password を直接 RDS に渡す

resource "aws_db_instance" "mysql" {
  ...
  username = "sample_admin"
  password = random_password.rds_password.result
}

SecretsManagerとの連携は一旦切って、確実に接続できる形に修正。


🔁 RDSの再作成

terraform taint aws_db_instance.mysql
terraform apply

🔐 パスワード確認用のOutputを追加

output "rds_password" {
  value       = random_password.rds_password.result
  sensitive   = true
}

出力確認:

terraform output rds_password

✅ MySQL接続確認

mysql -h <rds_endpoint> -P 3306 -u sample_admin -p

📘 Part 3: 実践Tips集(学びポイント)

💡 terraform apply が途中で止まったときの注意点

  • 削除しきれてないリソースが残り、再 apply で失敗することがある
  • 例:VPCを削除しようとしたが、IGWがまだアタッチされていて削除失敗
DependencyViolation: The vpc 'vpc-xxxx' has dependencies and cannot be deleted.

terraform destroytaint を使って順番に削除・再作成


🛢️ RDSは2つ以上のAZにサブネットをまたがる必要あり

DBSubnetGroupDoesNotCoverEnoughAZs:
The DB subnet group doesn't meet Availability Zone (AZ) coverage requirement.
  • RDSは高可用性を前提としているため、最低2つの異なるAZにサブネットが必要
  • ルートテーブルは共有でOK

🔐 SecretsManagerが削除スケジュール中エラー

InvalidRequestException:
You can't create this secret because a secret with this name is already scheduled for deletion.
  • SecretsManagerの削除は猶予期間付き(30日)
  • 再作成できないので、-v2 など別名でSecretsを作るか、削除キャンセルする

👤 AMIによってSSHユーザー名が違う

AMIの種類 SSHユーザー名
Amazon Linux 2 ec2-user
Ubuntu ubuntu
Debian admin
RHEL / CentOS ec2-user / centos

🏁 まとめ

Terraform × AWSで構築するインフラ環境では、構成を自動化できる一方で、

  • SecretsManager → RDSの順序依存
  • 破棄されないリソースの依存関係
  • AMIとユーザー名のマッピング

など細かな罠も多くあります。
それでも、一つずつ原因を突き止めて改善していくことで、信頼性の高い構成を安定して再現できるようになります。


🎯 次にやるなら…

  • ECS × ALB × Fargate でのコンテナ化
  • SSM Parameter Storeを用いた環境変数管理
  • CI/CD(GitHub Actions + Terraform Cloud)導入
  • CloudWatch によるモニタリング構成

以上、Terraform × AWS構築記録でした!

Discussion