dynamicブロックは、変数の内容に応じてリソース内のネストブロックを増減させるための言語機能です。
count/for_each(リソース単位)とは目的が異なり、nested blocks(例: ingress, lifecycle_rule など)を動的に生成するのが主用途です。
dynamicは、リソースやデータソースの内部で、可変個数のネストブロックを生成するための構文です。記述は dynamic "<ブロック名>" { for_each = ... iterator = ... content { ... } } の形を取り、for_eachで反復対象を与え、contentの中で各要素を展開します。
iteratorは省略可能で、省略時はeachというデフォルト名が使われます(each.valueで要素を参照)。mapをfor_eachに渡すとeach.key/each.valueが使え、list/setでは主にeach.valueを使います。
注意点として、dynamicは『プロバイダが定義するネストブロック』を生成するための仕組みであり、meta-argument(count, for_each, depends_on, provider, lifecycle など)の生成には使えません。
セキュリティグループのingressをdynamicで生成
variable "ingress_rules" {
description = "許可するingressルールの一覧"
type = list(object({
from_port = number
to_port = number
protocol = string
cidr_blocks = list(string)
description = optional(string)
ipv6_cidr_blocks = optional(list(string), [])
}))
}
resource "aws_security_group" "web" {
name = "web-sg"
description = "Web tier security group"
# 可変個数のingressブロックを生成
dynamic "ingress" {
for_each = var.ingress_rules
iterator = rule
content {
from_port = rule.value.from_port
to_port = rule.value.to_port
protocol = rule.value.protocol
cidr_blocks = rule.value.cidr_blocks
ipv6_cidr_blocks = try(rule.value.ipv6_cidr_blocks, null)
description = try(rule.value.description, null)
}
}
# 既定のegress(固定1ブロックのためdynamicは不要)
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
dynamicは単純な繰り返しだけでなく、条件付きの有無や、enabledフラグによるフィルタ、入れ子ブロックの展開にも使えます。0/1個のブロックを条件で切り替えたい場合は、for_eachに[]または[要素1つ]を渡すパターンが実用的です。
また、入力のリストから enabled = true の要素のみを抽出してからfor_eachに渡すと、条件ロジックがcontent内に散らからず読みやすくなります。入れ子構造は、dynamicの中でさらに固定のサブブロックを書く、または必要に応じてdynamicを入れ子にします(過度な入れ子は可読性低下に注意)。
dynamicブロック評価の流れ
0/1個のオプションブロックを動的生成
variable "access_logging" {
type = object({
bucket = string
prefix = optional(string)
})
default = null
}
resource "aws_s3_bucket" "b" {
bucket = "example-bucket-1234"
dynamic "logging" {
for_each = var.access_logging == null ? [] : [var.access_logging]
iterator = log
content {
target_bucket = log.value.bucket
target_prefix = try(log.value.prefix, null)
}
}
}
可変構成を作る手段は複数あります。dynamicはネストブロックを増減したいときの道具です。リソース自体の数を増減したいならresourceレベルのfor_each/countを使います。属性値の計算は内包表記やmergeで十分で、dynamicを使う必要はありません。
試験でも実務でも“何を可変にしたいのか(リソース数か、ネストブロック数か、単なる属性値か)”を起点に選ぶと誤りにくいです。
| 手段 | 主な使いどころ | 長所 | 注意点 |
|---|---|---|---|
| dynamicブロック | ネストブロックの可変生成(ingress, lifecycle_rule等) | スキーマ検証が効く/DRY化 | for_eachはPlan時に既知で必要、過度な入れ子は可読性低下 |
| resource for_each | リソース個数の可変化(同型リソースの複数作成) | 個別アドレス指定が容易(each.key) | キーの安定性が必須(setではなくmap推奨) |
| count | 単純なN個の反復(indexでアクセス) | シンプル・軽量 | 要素の追加削除でインデックスがズレやすい |
| 内包表記/merge | 属性値(map/list)の生成 | 可読性・型整合が取りやすい | ネストブロックは作れない(dynamicと役割が違う) |
やってはいけない例と代替
# NG: 属性(map)をdynamicで作ろうとするのは誤り
# dynamic "tags" { ... } は不可。tagsはブロックではなく属性。
# OK: 属性は式で組み立てる
locals {
base_tags = {
app = "web"
}
}
resource "aws_instance" "example" {
ami = "ami-12345678"
instance_type = "t3.micro"
tags = merge(local.base_tags, {
env = var.env
})
}
# リソース数を増やしたいならresourceのfor_each
resource "aws_iam_user" "u" {
for_each = var.user_names # map推奨(キー安定)
name = each.key
}
for_eachに渡す値がPlan時に未確定(unknown)だと、dynamicは展開できずエラーになります。データソースの結果に依存して数が決まる場合は、入力を見直すか、事前に値を確定させる設計に改めます。
list/setでfor_eachするとキーが不安定になりやすいため、map化して安定キーを使うのが安全です。また、optionalな属性はtry(..., null)で包み、プロバイダがnullを“未設定”として扱う前提に合わせると差分が出にくくなります。
型のガードとフィルタの定石
variable "rules" {
type = list(object({
name = string
enabled = optional(bool, true)
priority = number
}))
}
locals {
# enabledのみ抽出し、キー=論理名でmap化して安定キーを確保
active_rules = { for r in var.rules : r.name => r if try(r.enabled, true) }
}
resource "example_resource" "main" {
# ネストブロック rule を生成する(仮の例)
dynamic "rule" {
for_each = local.active_rules
iterator = r
content {
name = r.value.name
priority = r.value.priority
}
}
}
試験では、dynamicの役割(ネストブロックの生成)と、resourceのfor_each/count(リソース数の可変化)を取り違えないことが頻出ポイントです。iteratorやeach.valueの参照、条件付きで0/1個だけ生成するパターンも押さえておきましょう。
また、dynamicでmeta-argumentを作れない点、属性は式で構築する点、for_eachの値はPlan時既知が要件である点は定番のひっかけになります。
Iterator名と参照の確認
dynamic "ingress" {
for_each = var.ingress_rules
iterator = blk
content {
from_port = blk.value.from_port
to_port = blk.value.to_port
protocol = blk.value.protocol
}
}
ストレージバケットのライフサイクルルールは、条件やアクションの組み合わせで複数定義することが多く、dynamicと相性が良い題材です。enabledフラグでオンオフし、optional属性はtry(..., null)で包むと差分が安定します。
以下はGoogle Cloud Storageの例ですが、考え方は他プロバイダのlifecycle_rule相当でも同様です。
GCS バケットの lifecycle_rule を dynamic で生成
variable "lifecycle_rules" {
type = list(object({
enabled = optional(bool, true)
action = object({
type = string # e.g. "Delete" | "SetStorageClass"
storage_class = optional(string)
})
condition = object({
age = optional(number)
matches_prefix = optional(list(string))
matches_suffix = optional(list(string))
with_state = optional(string)
matches_storage_class = optional(list(string))
})
}))
}
locals {
active_lc = [for r in var.lifecycle_rules : r if try(r.enabled, true)]
}
resource "google_storage_bucket" "b" {
name = "example-bucket-xyz"
location = "US"
dynamic "lifecycle_rule" {
for_each = local.active_lc
iterator = lc
content {
action {
type = lc.value.action.type
storage_class = try(lc.value.action.storage_class, null)
}
condition {
age = try(lc.value.condition.age, null)
matches_prefix = try(lc.value.condition.matches_prefix, null)
matches_suffix = try(lc.value.condition.matches_suffix, null)
with_state = try(lc.value.condition.with_state, null)
matches_storage_class = try(lc.value.condition.matches_storage_class, null)
}
}
}
}
Associate / Pro
問題 1
Terraformで、変数の内容に応じてネストブロック(例: ingress)を0個以上生成したい。最も適切な実装はどれか。
正解: A
ネストブロックの可変生成にはdynamicが適切。countはリソース数の反復であり、ネストブロックは増減できない。templatefileでHCLを生成するのは型検証が効かず非推奨。lifecycleなどのメタ引数はdynamicで生成できない。
dynamicのfor_eachにlist/set/mapのどれを渡すべき?
map推奨です。キーが安定して計画・差分が安定します。list/setも使えますが、順序や暗黙キーが不安定になりやすく、後続の変更で不要な差分が発生しがちです。必要なら { for v in var.list : v.name => v } のようにmap化してから反復します。
for_eachにdataソースの結果(Plan時に未確定)を渡すとどうなる?
Plan時にunknownのままではdynamicを展開できずエラーになります。for_eachの値はPlan時既知である必要があります。設計としては、入力変数で制御する、もしくは事前プロセスで値を確定させてからTerraformに渡すのが安全です。
meta-argument(lifecycle, count, for_each等)をdynamicで作れますか?
できません。dynamicはプロバイダが定義する通常のネストブロック用です。meta-argumentはTerraform言語レベルの仕組みなので、通常の静的なブロック/引数として記述してください。
NicheeLab編集部
データエンジニアリング・クラウド資格の専門家。Databricks・Snowflake等の認定資格を保有し、実務経験に基づいた問題作成・解説を行っています。NicheeLab運営。
Terraform HCL 構文の基礎:Block / Attribute / Expression を正しく使い分ける
Terraform Associate で頻出の HCL 構文を、ブロック・属性・式の3視点で整理。実務で迷いがちな書き...
Terraform Authoring & Ops Pro: 上位資格の範囲と対策
上位レベルを想定したTerraformの設計・運用ドメインを整理し、実務で通用する対策を提示。モジュール設計、ステート運...
Terraform Providers の基本: プラグイン型アーキテクチャを正しく使いこなす
Associate レベルで押さえるべき Provider の基礎、インストール、バージョニング、認証、エイリアス運用を...
Terraform Resourceブロック徹底ガイド: 最小単位のリソース定義
Associateレベルで押さえるべきResourceブロックの構造、依存関係、メタ引数、ライフサイクル制御を実務目線で...
Terraform Data Source徹底理解:既存リソースの参照で壊さず足す
Terraform Associate向けに、Data Sourceを用いた既存リソース参照の基本、選択基準、評価順序、...