1. 概要
dynamicブロックの基本構文とcontentブロックの書き方for_eachとの組み合わせ方- セキュリティグループルールへの適用(最頻出パターン)
- ネストした
dynamicブロック dynamicを使うべき場面・使わないべき場面
dynamicブロックは、リソース内の繰り返しブロックを動的に生成する機能です。セキュリティグループのインバウンドルールや、ECSコンテナのポートマッピングなど、同じ構造のブロックを複数記述するときに使います。
2. 基本構文
dynamic "<ブロック名>" {
for_each = <コレクション>
content {
# ブロックの中身(<ブロック名>.valueを使って各要素を参照)
# デフォルトのイテレータ名 = dynamicブロックのラベル名
<属性> = <ブロック名>.value.<フィールド>
# ...
}
}
for_eachにlist/set/mapを渡すと、要素の数だけブロックが生成されるcontentブロックに繰り返すべき設定を書く- イテレータ変数のデフォルト名はdynamicブロックのラベル名(例:
dynamic "ingress"ならingress.key/ingress.value) iterator引数で任意の別名に変更できる(ネスト時に区別しやすくなる)
3. セキュリティグループルールへの適用
最も多い使用例です。複数のポート・CIDRをリストで定義し、dynamicで展開します。
terraform {
required_version = ">= 1.9"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
provider "aws" {
region = "ap-northeast-1"
}
variable "environment" {
description = "環境名"
type = string
default = "dev"
}
locals {
# インバウンドルールをリストで定義
ingress_rules = [
{ port = 443, protocol = "tcp", cidr = "0.0.0.0/0", description = "HTTPS" },
{ port = 80, protocol = "tcp", cidr = "0.0.0.0/0", description = "HTTP redirect" },
{ port = 8080, protocol = "tcp", cidr = "10.0.0.0/8", description = "Internal API" },
]
}
resource "aws_security_group" "web" {
name = "${var.environment}-web-sg"
description = "Web server security group"
# ✅ dynamicブロックでルールを動的に生成
dynamic "ingress" {
for_each = local.ingress_rules
content {
from_port = ingress.value.port
to_port = ingress.value.port
protocol = ingress.value.protocol
cidr_blocks = [ingress.value.cidr]
description = ingress.value.description
}
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
description = "All outbound"
}
tags = {
Name = "${var.environment}-web-sg"
Environment = var.environment
ManagedBy = "terraform"
}
}
dynamicなしで書いた場合(繰り返しが発生):
# ❌ dynamicなしだと同じ構造を3回書く必要がある
resource "aws_security_group" "web" {
ingress {
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
from_port = 8080
to_port = 8080
protocol = "tcp"
cidr_blocks = ["10.0.0.0/8"]
}
}
4. mapを使ったパターン
mapで渡すと<ブロック名>.keyでキーを参照できます。
locals {
db_parameters = {
"max_connections" = "100"
"shared_buffers" = "256MB"
"effective_cache_size" = "768MB"
"log_min_duration_statement" = "1000"
}
}
resource "aws_db_parameter_group" "postgres" {
family = "postgres15"
name = "${var.environment}-postgres15"
dynamic "parameter" {
for_each = local.db_parameters
content {
name = parameter.key # mapのキーがパラメータ名
value = parameter.value # mapの値がパラメータ値
}
}
tags = {
Name = "${var.environment}-postgres15"
Environment = var.environment
ManagedBy = "terraform"
}
}
5. 条件付きブロック(0/1制御)
for_eachに空コレクションを渡すと、そのブロックは生成されません。条件によってブロックを有効/無効にできます。
variable "enable_monitoring" {
description = "CloudWatchモニタリングを有効にするか"
type = bool
default = false
}
resource "aws_instance" "app" {
ami = data.aws_ami.amazon_linux_2023.id
instance_type = "t3.micro"
# enable_monitoringがtrueのときだけmetadata_optionsブロックを生成
dynamic "metadata_options" {
for_each = var.enable_monitoring ? [1] : []
content {
http_endpoint = "enabled"
http_tokens = "required"
instance_metadata_tags = "enabled"
}
}
tags = {
Name = "app"
Environment = var.environment
ManagedBy = "terraform"
}
}
6. dynamicブロックのiterator(別名)
デフォルトのイテレータ名はdynamicブロックのラベル名(例:ingress.value)ですが、iterator引数で任意の別名に変更できます。ネストしたdynamicで外側と内側のイテレータを区別するときに便利です。
dynamic "ingress" {
for_each = local.ingress_rules
iterator = rule # ingress.value の代わりに rule.value を使う
content {
from_port = rule.value.port
to_port = rule.value.port
protocol = rule.value.protocol
cidr_blocks = [rule.value.cidr]
}
}
7. dynamicを使わないべき場面・制約
ネストは1段のみ有効
dynamicブロックは1段のネストには対応していますが、contentブロック内でさらにdynamicをネストする場合はそれぞれ独立したブロックとして記述します。iteratorを使って外側と内側のeachを区別することで多重ネストに対応できます(セクション6参照)。ただし3段以上のネストは可読性が著しく低下するため、localsでデータを事前に整形することを強く推奨します。
# ❌ ブロックが1つだけのときはdynamicは不要
dynamic "egress" {
for_each = [1]
content {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
# ✅ 1つだけなら通常のブロックで書く
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
8. 関連記事
- for_each — map/setで動的にリソースを作成するメタ引数 — for_eachとの比較
- for式の使い方 — dynamicに渡すコレクションの加工
- count — 数で複数リソースを作成するメタ引数 — ブロック単位vs リソース単位の比較
- 条件式(三項演算子)の使い方 —
for_each = flag ? [1] : []パターン - resourceブロック — 基本構文と使い方
- variable(入力変数)— 定義・型・バリデーション
9. まとめ
dynamicブロックはリソース内の繰り返しブロックを動的に生成するfor_eachにlist/set/mapを渡し、contentブロックに設定を書く- セキュリティグループのルールやDBパラメータグループが最頻出の用途
for_each = var.flag ? [1] : []で条件付きブロック(有効/無効)を実現できる- ブロックが1つだけの場合は通常のブロックを使う(dynamicは不要)
dynamicのネストはiteratorで外側と内側を区別する。3段以上のネストは可読性が悪化するのでlocalsで事前整形する
動作確認バージョン: Terraform >= 1.9 / AWS Provider ~> 5.0 公式ドキュメント: https://developer.hashicorp.com/terraform/language/expressions/dynamic-blocks