こうした声はよく聞きます。RDSはEC2やVPCと比べてリソース間の依存関係が多く、サブネットグループの設定漏れやセキュリティグループの許可ポートミスで apply 直後にエラーになるケースが後を絶ちません。
この記事では、aws_db_instance を中心に、aws_db_subnet_group・aws_db_parameter_group・aws_security_group を組み合わせたRDSのプロビジョニング手順を実践解説します。MySQLを題材に、削除保護・マルチAZ・自動バックアップといった本番環境の必須設定も含めてカバーします。
動作確認環境: Terraform 1.8.5 / AWS Provider 5.50.0(ap-northeast-1リージョン、MySQL 8.0.35)
この記事のポイント
・aws_db_instanceの前にサブネットグループとセキュリティグループの定義が必要
・aws_db_parameter_groupで文字コード・タイムゾーンをコードで管理できる
・本番環境ではdeletion_protection=trueとskip_final_snapshot=falseを必ず設定する
・terraform applyは完了まで最大20分かかるためCI/CDのタイムアウト設定に注意
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
なぜRDSをTerraformで管理するのか
AWSコンソールからRDSを手作業で構築すると、パラメータグループの設定値・サブネットグループのAZ構成・セキュリティグループのingressルールが別々の画面に散らばります。後からどの設定が本番と開発で異なるのかを把握しにくく、「なぜかstgで動いてprodで動かない」問題の温床になります。Terraformで管理することで次の3つの問題が解消されます。
・設定の再現性:terraform applyを実行すれば同じ構成のRDSを何度でも作れる
・差分の可視化:terraform planでパラメータ変更の影響範囲を事前に確認できる
・レビュー可能性:GitのPRでRDSの設定変更をチームでレビューできる
ただしRDSはリソース間の依存関係が明確なため、作成順序を間違えると apply 時にエラーになります。次のセクションで依存関係の全体像と具体的な設定手順を解説します。
aws_db_instanceの基本設定|依存リソースの作成順序
1. aws_db_subnet_groupでサブネットグループを作成する
RDSを作成するには必ず事前にサブネットグループが必要です。サブネットグループは「RDSを配置できるサブネットの集合」で、マルチAZ構成ではサブネットグループに2つ以上のAZのサブネットを含める必要があります。# subnet_group.tf resource "aws_db_subnet_group" "main" { name = "main-db-subnet-group" subnet_ids = [ aws_subnet.private_a.id, # ap-northeast-1a のプライベートサブネット aws_subnet.private_c.id, # ap-northeast-1c のプライベートサブネット ] tags = { Name = "main-db-subnet-group" Environment = "production" } }
2. セキュリティグループを定義する
RDS用のセキュリティグループでは、アクセス元(アプリケーションサーバーのセキュリティグループ)からのみ3306番ポートを許可します。0.0.0.0/0 の全開放は絶対に避けてください。# security_group.tf resource "aws_security_group" "rds" { name = "rds-sg" description = "Security group for RDS MySQL" vpc_id = aws_vpc.main.id ingress { from_port = 3306 to_port = 3306 protocol = "tcp" # アプリケーションサーバーのSGからのみ許可 security_groups = [aws_security_group.app.id] description = "MySQL from app servers" } egress { from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] } tags = { Name = "rds-sg" } }
3. aws_db_instanceでMySQLを起動する
サブネットグループとセキュリティグループが揃ったら aws_db_instance を定義します。# rds.tf resource "aws_db_instance" "main" { identifier = "main-mysql" engine = "mysql" engine_version = "8.0.35" instance_class = "db.t3.micro" allocated_storage = 20 storage_type = "gp3" db_name = "appdb" username = "admin" password = var.db_password # variables.tfで定義したsensitive変数 db_subnet_group_name = aws_db_subnet_group.main.name vpc_security_group_ids = [aws_security_group.rds.id] parameter_group_name = aws_db_parameter_group.main.name # 本番必須設定 deletion_protection = true skip_final_snapshot = false final_snapshot_identifier = "main-mysql-final-snapshot" backup_retention_period = 7 backup_window = "02:00-03:00" maintenance_window = "mon:03:00-mon:04:00" tags = { Name = "main-mysql" Environment = "production" } }
# variables.tf variable "db_password" { description = "RDS admin password" type = string sensitive = true # terraform planとログに値が表示されなくなる }
$ terraform apply -var="db_password=MySecretPass123!" aws_db_subnet_group.main: Creating... aws_db_subnet_group.main: Creation complete after 4s [id=main-db-subnet-group] aws_security_group.rds: Creating... aws_security_group.rds: Creation complete after 3s [id=sg-0a1b2c3d4e5f67890] aws_db_instance.main: Creating... aws_db_instance.main: Still creating... [10s elapsed] aws_db_instance.main: Still creating... [1m0s elapsed] # ... (RDSの作成は通常5分~20分かかる) aws_db_instance.main: Creation complete after 8m42s [id=main-mysql] Apply complete! Resources: 3 added, 0 changed, 0 destroyed.
>> Terraform実践セミナーの詳細はこちら
パラメータグループで文字コードとタイムゾーンをコード化する
1. aws_db_parameter_groupを定義する
AWSが提供するデフォルトのパラメータグループは変更不可です。カスタム設定(文字コードやタイムゾーン)が必要な場合は aws_db_parameter_group を作成してから aws_db_instance に紐づけます。# parameter_group.tf resource "aws_db_parameter_group" "main" { name = "main-mysql80" family = "mysql8.0" # engineとengine_versionに対応したfamilyを指定 description = "Custom parameter group for MySQL 8.0" parameter { name = "character_set_server" value = "utf8mb4" } parameter { name = "character_set_client" value = "utf8mb4" } parameter { name = "time_zone" value = "Asia/Tokyo" apply_method = "immediate" # 再起動なしで即時反映 } parameter { name = "slow_query_log" value = "1" } parameter { name = "long_query_time" value = "1" # 1秒以上のクエリをスロークエリとして記録 } tags = { Name = "main-mysql80" } }
2. apply_methodの違い(immediateとpending-reboot)
パラメータの apply_method には2種類あります。・immediate:apply直後に反映。time_zoneやslow_query_logなど動的パラメータに使用できる
・pending-reboot:次のメンテナンスウィンドウまたは手動再起動後に反映。innodb_buffer_pool_sizeなどの静的パラメータに必要
静的パラメータを immediate にしてもエラーにはなりませんが反映されません。AWSドキュメントのパラメータ一覧で「Dynamic/Static」を確認してから設定してください。
実務Tips|削除保護・自動バックアップ・マルチAZ
1. 本番環境の削除保護設定
terraform destroy を実行しても本番DBが消えないよう、deletion_protection = true は必須です。削除保護が有効な状態で destroy しようとすると次のエラーになります。$ terraform destroy Error: RDS DB Instance is protected from deletion. with aws_db_instance.main, on rds.tf line 1, in resource "aws_db_instance" "main": Please disable deletion protection before deleting.
2. skip_final_snapshotとfinal_snapshot_identifierの設定
skip_final_snapshot = false(デフォルト)の場合、terraform destroy を実行するとRDS削除前に最終スナップショットが自動作成されます。final_snapshot_identifier には一意の名前を設定してください(省略するとエラーになります)。開発環境では毎回スナップショットが作成されるとコストがかかるため、skip_final_snapshot = true にするケースもあります。環境ごとに var-file で制御する設計が一般的です。
3. マルチAZ設定でフェイルオーバーを実現する
本番環境では multi_az = true を設定します。1行追加するだけですが、コスト(インスタンス料金が約2倍)と引き換えに単一AZ障害時の自動フェイルオーバーが有効になります。resource "aws_db_instance" "main" { # ... 既存の設定 ... multi_az = true # スタンバイレプリカを別AZに自動配置 } output "rds_endpoint" { value = aws_db_instance.main.endpoint sensitive = false }
「InvalidParameterCombination」などのapplyエラーと対処法
1. サブネットグループのAZが1つしかない場合
マルチAZを有効にしようとしたとき、または一部のインスタンスタイプで複数AZのサブネットが要求されるときに発生します。$ terraform apply Error: InvalidParameterCombination: Cannot specify a DB subnet group for DB instances in VPC. # またはサブネットグループのAZが不足している場合: Error: DB subnet group "main-db-subnet-group" is invalid because it must have at least 2 availability zones.
2. パラメータグループのfamilyが一致しない場合
$ terraform apply Error: InvalidParameterValue: Could not find parameter group family "mysql8.0" for parameter group "main-mysql80" # familyの名前は厳密に確認する(例: mysql8.0, mysql8.4, aurora-mysql8.0 は別物)
3. applyが途中でタイムアウトする場合
RDSの作成は最大20分かかります。CI/CDパイプラインのデフォルトタイムアウトが短いとapplyが中断されます。Terraform側では timeouts ブロックで延長できます。resource "aws_db_instance" "main" { # ... timeouts { create = "40m" # デフォルトは40m。CI側も合わせて延長する update = "80m" # マルチAZ変更などは時間がかかる delete = "40m" } }
本記事のまとめ
TerraformでRDSをプロビジョニングする設計のポイントをまとめます。| やりたいこと | 使うリソース・設定 |
|---|---|
| RDSのサブネットグループを定義する | resource "aws_db_subnet_group" { subnet_ids = [...] } |
| RDS専用セキュリティグループを作る | resource "aws_security_group" { ingress { from_port = 3306 } } |
| MySQLインスタンスを作成する | resource "aws_db_instance" { engine = "mysql" } |
| 文字コード・タイムゾーンを設定する | resource "aws_db_parameter_group" { parameter { name = "time_zone" } } |
| 誤削除を防止する | aws_db_instance { deletion_protection = true } |
| マルチAZフェイルオーバーを有効にする | aws_db_instance { multi_az = true } |
>> Terraform実践セミナーの詳細はこちら
3,100名以上が実践した「型」を無料で公開中
プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。
姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら
- 前のページへ:terraform consoleコマンドでHCL式を対話検証する方法|変数・関数・条件式のデバッグ実践手順
- この記事の属するカテゴリ:Terraformへ戻る

無料メルマガで学習を続ける
Linuxの実践スキルをメールで毎週お届け。
登録は30秒、解除もいつでも可。
登録無料・いつでも解除できます