Terraform

Terraform for 式でリスト/マップを生成する: 実務とAssociate対策

2026-04-19
NicheeLab編集部

Terraform の for 式は、コレクション型(list/map/set/tuple/object)を別の形に変換する表現手段です。HCL の式として評価され、リソースそのものを増やすのではなく、入力・中間データを整形します。

Associate 試験では、構文の差異(list 生成と map 生成)、if でのフィルタ、インデックスの扱い、重複キー時の挙動、for_each との役割の違いが頻出です。ここでは、安定した公式挙動に基づき、実務でもすぐ使える形で整理します。

for 式の基本構文と評価モデル

for 式は「コレクションを走査して、新しいコレクションを作る」ための式です。list を作る場合は角括弧、map を作る場合は波括弧を使います。if 句を末尾に置くと要素をフィルタできます。評価は式として行われ、リソース作成数には影響しません。

代表的構文は以下です。list 生成: [for v in xs : EXPR if COND]、map 生成: { for k, v in m : NEW_K => NEW_V if COND }。list 走査時も [for i, v in xs : ...] のようにインデックスを併記可能です。出力の順序は list では安定、map ではキー順は未定義ですが values() で取り出すと list として順序が決まります。

  • list 生成は [ ... ]、map 生成は { ... }。この括弧の違いを試験で取り違えない
  • if は末尾(コロンの右側)に置く。条件に合わない要素は出力に含まれない
  • list 走査でのインデックス: for i, v in var.list
  • map 生成ではキー重複があるとエラー(Duplicate key)
  • for 式は値の整形専用。複数リソース作成は resource の for_each で行う
機能主な用途出力/効果フィルタ可否
for 式入力データの整形(list/map の生成・変換)新しいコレクションを返す(リソース数は増えない)可能(if 条件)
スプラット演算子 (x.*.attr)同種属性の抽出を簡潔に書くlist を返す不可(条件は別途扱う)
for_each(メタ引数)複数リソース/モジュールの作成複数リソースを実体化間接的(事前に map/list を整形)

for 式の流れ

入力コレクションlist / map / setfor 要素[,インデックス]変換式 (expr)(任意) if 条件出力list または map

基本構文の例(list 変換・map 生成・フィルタ)

variable "names" { type = list(string) }

locals {
  upper_list = [for n in var.names : upper(n)]
  name_len   = { for n in var.names : n => length(n) }
  only_long  = [for n in var.names : n if length(n) > 3]
}

output "upper_list" { value = local.upper_list }
output "name_len"   { value = local.name_len }
output "only_long"  { value = local.only_long }

リスト生成と変換の実践

リストから別のリストを作る場合は [for v in list : EXPR] を基本に、必要に応じて [for i, v in list : ...] でインデックスも利用します。空文字などを除外したいときは if を併用します。

入れ子のリストを平坦化したいときは for 式で内側も展開し、最後に flatten() を適用します。順序を保持したまま変換できる点が for 式の強みです。

  • インデックスが必要なら for i, v in var.list
  • 空文字の除外は if length(v) > 0、null の除外は if v != null
  • 入れ子は二重の for と flatten() で整理する

リストの整形(インデックス利用・平坦化)

variable "roles" { type = list(string) default = ["web", "db", "", "cache"] }

locals {
  upper_roles = [for i, r in var.roles : "${i}-${upper(r)}" if length(r) > 0]

  ports_nested = [[80, 443], [8080], []]
  ports_flat   = flatten([for ps in local.ports_nested : [for p in ps : p]])
}

output "upper_roles" { value = local.upper_roles }
output "ports_flat"  { value = local.ports_flat }

マップ生成とキー/値の加工

map 生成は { for k, v in m : NEW_K => NEW_V } で記述します。キーや値の正規化(trimspace、lower/upper、置換)や、条件に合わないペアの除外が定石です。キー重複は計画時にエラーとなるため、distinct() などで事前にユニーク化するか、キー設計を見直します。

list から map を作るときは { for o in list : o.name => o.id } のように特定フィールドをキーにします。values(map) で値だけの list を得られる点も覚えておくと、後段の list 変換に繋げられます。

  • キー重複は Duplicate key で失敗。必ず一意になるように設計する
  • 条件付きで除外する場合は if を末尾に付ける(式中に書かない)
  • list から map を作る時はキー候補(主キー相当)を明確に

map の正規化と list→map 変換

variable "tags" { type = map(string) }
variable "servers" {
  type = list(object({ name = string, id = string }))
}

locals {
  normalized_tags = {
    for k, v in var.tags : lower(trimspace(k)) => trimspace(v)
    if v != null && trimspace(v) != ""
  }

  server_map = { for s in var.servers : s.name => s.id }

  server_ids_doubled = [for id in values(local.server_map) : "srv-${id}"]
}

output "normalized_tags"     { value = local.normalized_tags }
output "server_map"          { value = local.server_map }
output "server_ids_doubled"  { value = local.server_ids_doubled }

フィルタ条件と null/unknown の扱い

for 式の if は、その要素を出力に含めるかどうかの判定です。条件が false の要素は「生成されない」ため、list では要素が詰められ、map ではキーごと欠落します。null は自動では落ちないため、必要なら明示的に条件に含めます。

プラン時に未知(unknown)の値が含まれる場合、if の評価結果も unknown になり得ます。その場合は適用時に最終的なフィルタが決まり、計画に「(known after apply)」が現れます。Associate レベルでは、この評価タイミングの概念を押さえておけば十分です。

  • if は要素の採否。list の穴埋めは自動で行われる(スパースにはならない)
  • null を落としたいなら if v != null を付ける
  • unknown が絡むと計画時に完全な出力は確定しない

null と unknown を考慮したフィルタ

variable "users" {
  type = list(object({ name = string, active = bool }))
}

locals {
  active_names = [for u in var.users : u.name if try(u.active, false)]
  non_empty    = [for s in ["a", "", "b", null] : s if s != null && s != ""]
  kept_nulls   = [for s in ["a", "", "b", null] : s] # 条件がなければ null は残る
}

output "active_names" { value = local.active_names }
output "non_empty"    { value = local.non_empty }
output "kept_nulls"   { value = local.kept_nulls }

dynamic ブロックやモジュール入力での活用

for 式は「入力データの整形」に集中させ、実リソースの増減は resource/module の for_each に任せるのが基本です。dynamic ブロックでは for_each に与える list/map を、事前に for 式で組み立てておくと安全です。

実務では、セキュリティグループのルール、タグ、ラベル、IAM ポリシー文など、構造化データを for 式で正規化→dynamic/for_each で展開、が再現性の高いパターンです。

  • for 式=整形、for_each=展開 と役割分担を明確に
  • dynamic の for_each には list/map を渡す。map のキーはアドレス指定子に使える
  • フィルタを if で完了させてから dynamic に渡すとロジックが明確になる

for 式で整形 → dynamic で展開(例: SG ルール)

variable "ingress_rules" {
  type = list(object({ port = number, cidr = string }))
}

locals {
  ingress_norm = [for r in var.ingress_rules : {
    port = r.port
    cidr = trimspace(r.cidr)
  } if r.cidr != null && trimspace(r.cidr) != ""]
}

resource "aws_security_group" "example" {
  name        = "example"
  description = "example"
  vpc_id      = "vpc-xxxxxxxx"

  dynamic "ingress" {
    for_each = local.ingress_norm
    content {
      from_port   = ingress.value.port
      to_port     = ingress.value.port
      protocol    = "tcp"
      cidr_blocks = [ingress.value.cidr]
    }
  }
}

Associate 試験対策の落とし穴とチェックポイント

構文差異([ ... ] と { ... })、if の位置、インデックスの取得、Duplicate key、values()/keys() の併用、flatten と二重 for の組み合わせは頻出です。特に map 生成でのキー重複エラーは定番のひっかけです。

スプラット演算子は簡潔ですが、条件付き加工やキー生成はできません。条件や複雑な加工が必要なときは for 式を選ぶ、という切り分けを覚えておきましょう。

  • [ ... ] は list、{ ... } は map。誤用しない
  • list のインデックスは for i, v in var.list で取得
  • map 生成時はキーが一意かを確認。必要なら distinct() 等で前処理
  • list の二重構造 → [for x in xs : [for y in x : ...]] + flatten()
  • values(map) で値の list、keys(map) でキーの list を取得

安全なパターン集(重複回避・キー/値抽出)

locals {
  # 重複キー回避(ユニーク化してから map 生成)
  uniq = distinct(["a", "b", "a"])            # => ["a", "b"]
  m    = { for v in local.uniq : v => upper(v) }  # キーは一意

  # map の値リストを加工
  doubled = [for v in values({ a = 1, b = 2 }) : v * 2]  # => [2, 4]

  # zipmap で list から map を合成
  keys_   = ["env", "app"]
  vals_   = ["prod", "api"]
  labels  = zipmap(keys_, vals_)  # => { env = "prod", app = "api" }
}

output "m"       { value = local.m }
output "doubled" { value = local.doubled }
output "labels"  { value = local.labels }

問題で確認

Associate

問題 1

次の for 式の評価結果として正しいものはどれですか。 variable "roles" { type = list(string) default = ["web", "db", "", "cache"] } locals { upper_roles = [for i, r in var.roles : "${i}-${upper(r)}" if length(r) > 0] } output "upper_roles" { value = local.upper_roles }

  1. A. ["WEB", "DB", "CACHE"]
  2. B. ["0-WEB", "1-DB", "3-CACHE"]
  3. C. ["0-WEB", "1-DB", "2-", "3-CACHE"]
  4. D. { 0 = "WEB", 1 = "DB", 3 = "CACHE" }

正解: B

list 生成の for 式で [for i, r in ... : ... if length(r) > 0] としているため、空文字は除外されます。出力は list で、各要素は "インデックス-大文字化" の形式となり、結果は ["0-WEB", "1-DB", "3-CACHE"] です。map にはならないため D は誤りです。

よくある質問

for 式と resource の for_each は何が違いますか?

for 式は値(list/map 等)を生成・変換する式で、リソース数は増えません。for_each はメタ引数で、与えた map/list に応じてリソース(またはモジュール)を複製します。前者はデータ整形、後者は展開という役割分担です。

リスト走査でインデックスを使うには?

list に対して [for i, v in var.list : EXPR] のように 2 つ目の識別子を置くと i に 0 始まりのインデックスが入ります。map の場合は [for k, v in var.map : ...] で k がキーです。

map 生成でキーが重複するとどうなりますか?

重複キーは計画時に Duplicate key エラーになります。distinct() で事前にユニーク化する、キー設計を見直す、あるいはキーの一部にインデックスや名前空間を付与するなどで回避します。

この記事で学んだ内容を問題で確認しましょう

16,000問以上の問題で実力チェック

無料で問題を解いてみる
この記事の著者

NicheeLab編集部

データエンジニアリング・クラウド資格の専門家。Databricks・Snowflake等の認定資格を保有し、実務経験に基づいた問題作成・解説を行っています。NicheeLab運営。


関連記事
Terraform

Terraform HCL 構文の基礎:Block / Attribute / Expression を正しく使い分ける

Terraform Associate で頻出の HCL 構文を、ブロック・属性・式の3視点で整理。実務で迷いがちな書き...

Terraform

Terraform Authoring & Ops Pro: 上位資格の範囲と対策

上位レベルを想定したTerraformの設計・運用ドメインを整理し、実務で通用する対策を提示。モジュール設計、ステート運...

Terraform

Terraform Providers の基本: プラグイン型アーキテクチャを正しく使いこなす

Associate レベルで押さえるべき Provider の基礎、インストール、バージョニング、認証、エイリアス運用を...

Terraform

Terraform Resourceブロック徹底ガイド: 最小単位のリソース定義

Associateレベルで押さえるべきResourceブロックの構造、依存関係、メタ引数、ライフサイクル制御を実務目線で...

Terraform

Terraform Data Source徹底理解:既存リソースの参照で壊さず足す

Terraform Associate向けに、Data Sourceを用いた既存リソース参照の基本、選択基準、評価順序、...

Terraformの記事一覧 (101件)
© 2026 NicheeLab All rights reserved.