1. 概要
この記事では、以下の内容を解説します。
for式の基本構文(リスト出力・map出力)ifフィルタとの組み合わせfor_eachに渡すmapをfor式で生成するパターンlocalsブロックと組み合わせたデータ変換for式でよく使う組み込み関数との連携- よくある間違いと対処法
for式は、リスト・mapなどのコレクションを変換・フィルタリングするためのTerraform専用の式です。for_eachと名前が似ていますが別物で、for式は値を生成する式であり、リソースを作成するものではありません。localsと組み合わせてデータを加工し、for_eachに渡すのが典型的な使い方です。
2. for式とは
for式はコレクション(リスト・map・set)の各要素を変換して、新しいリスト([...])またはmap({...})を生成する式です。
# リストを別のリストに変換
[for 変数 in コレクション : 変換後の値]
# mapを別のmapに変換
{for キー変数, 値変数 in コレクション : 新キー => 新値}
Pythonのリスト内包表記に相当する機能です。
3. 基本構文
リストを出力する([ ])
# 基本形: [for <変数> in <コレクション> : <式>]
locals {
# 文字列リストを大文字に変換
upper_names = [for name in ["web", "app", "db"] : upper(name)]
# → ["WEB", "APP", "DB"]
# 数値リストを2倍にする
doubled = [for n in [1, 2, 3] : n * 2]
# → [2, 4, 6]
# 文字列を整形する
az_labels = [for az in ["ap-northeast-1a", "ap-northeast-1c"] : substr(az, -1, 1)]
# → ["a", "c"]
}
mapを出力する({ })
mapを出力するにはキー => 値の形式で記述し、波括弧{ }で囲みます。
locals {
# リストからmapを生成(値をキーとして使う)
name_map = { for name in ["web", "app", "db"] : name => "${name}-server" }
# → { "web" = "web-server", "app" = "app-server", "db" = "db-server" }
# mapのキーと値を入れ替える
inverted = { for k, v in { a = 1, b = 2, c = 3 } : v => k }
# → { 1 = "a", 2 = "b", 3 = "c" }
}
4. mapを反復する
コレクションがmapやobjectの場合、for式でキーと値の両方を受け取れます。
variable "instance_types" {
type = map(string)
default = {
dev = "t3.micro"
stg = "t3.small"
prd = "t3.medium"
}
}
locals {
# mapのキーと値を両方使う
instance_labels = [
for env, type in var.instance_types : "${env}: ${type}"
]
# → ["dev: t3.micro", "prd: t3.medium", "stg: t3.small"]
# ※ mapの反復順はキーのアルファベット順になります
# mapのキーだけ取得(keys()関数と同等)
env_names = [for env, _ in var.instance_types : env]
# → ["dev", "prd", "stg"]
}
5. ifでフィルタリングする
for式の末尾にif <条件>を追加すると、条件を満たす要素だけを残せます。
variable "users" {
type = list(object({
name = string
enabled = bool
role = string
}))
default = [
{ name = "alice", enabled = true, role = "admin" },
{ name = "bob", enabled = false, role = "viewer" },
{ name = "carol", enabled = true, role = "viewer" },
]
}
locals {
# enabledがtrueのユーザー名だけを抽出
active_users = [for u in var.users : u.name if u.enabled]
# → ["alice", "carol"]
# adminロールのユーザーのみ抽出してmapに変換
admin_map = { for u in var.users : u.name => u.role if u.role == "admin" }
# → { "alice" = "admin" }
}
6. for_eachに渡すmapをfor式で生成する
for_eachにはリストを直接渡せません。for式でリスト→mapに変換してから渡すのが実務で最も多いパターンです。
オブジェクトのリストからmapを生成する
terraform {
required_version = ">= 1.9"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
provider "aws" {
region = "ap-northeast-1"
}
variable "subnet_configs" {
description = "サブネット設定のリスト"
type = list(object({
name = string
cidr = string
az = string
tier = string # "public" or "private"
}))
default = [
{ name = "public-1a", cidr = "10.0.1.0/24", az = "ap-northeast-1a", tier = "public" },
{ name = "public-1c", cidr = "10.0.2.0/24", az = "ap-northeast-1c", tier = "public" },
{ name = "private-1a", cidr = "10.0.11.0/24", az = "ap-northeast-1a", tier = "private" },
{ name = "private-1c", cidr = "10.0.12.0/24", az = "ap-northeast-1c", tier = "private" },
]
}
locals {
# for式でリスト → map(name => object) に変換
subnets_map = { for s in var.subnet_configs : s.name => s }
# → {
# "public-1a" = { name = "public-1a", cidr = "10.0.1.0/24", ... }
# "public-1c" = { name = "public-1c", cidr = "10.0.2.0/24", ... }
# "private-1a" = { name = "private-1a", cidr = "10.0.11.0/24", ... }
# "private-1c" = { name = "private-1c", cidr = "10.0.12.0/24", ... }
# }
# フィルタ込み: パブリックサブネットのみのmap
public_subnets_map = { for s in var.subnet_configs : s.name => s if s.tier == "public" }
}
resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
tags = {
Name = "main-vpc"
ManagedBy = "terraform"
}
}
resource "aws_subnet" "main" {
for_each = local.subnets_map # for式で変換したmapを渡す
vpc_id = aws_vpc.main.id
cidr_block = each.value.cidr
availability_zone = each.value.az
tags = {
Name = each.key
Tier = each.value.tier
ManagedBy = "terraform"
}
}
7. localsと組み合わせたデータ変換パターン
複数のfor式を段階的に組み合わせる
locals {
# ステップ1: 生のリストをフィルタしてactiveなもののみ
active_configs = [
for c in var.server_configs : c if c.enabled
]
# ステップ2: フィルタ後のリストをfor_each用のmapに変換
active_configs_map = {
for c in local.active_configs : c.name => c
}
}
resource "aws_instance" "servers" {
for_each = local.active_configs_map
ami = data.aws_ami.amazon_linux_2023.id
instance_type = each.value.instance_type
root_block_device {
volume_size = each.value.volume_size
volume_type = "gp3"
encrypted = true
}
tags = {
Name = each.key
Environment = each.value.env
ManagedBy = "terraform"
}
}
data "aws_ami" "amazon_linux_2023" {
most_recent = true
owners = ["amazon"]
filter {
name = "name"
values = ["al2023-ami-*-x86_64"]
}
}
タグのmapを動的に生成する
variable "extra_tags" {
description = "追加するタグのリスト(key=valueの文字列)"
type = list(string)
default = ["Team=infra", "CostCenter=platform"]
}
locals {
# "key=value" の文字列リストをmapに変換
parsed_extra_tags = {
for tag in var.extra_tags :
split("=", tag)[0] => split("=", tag)[1]
}
# → { "Team" = "infra", "CostCenter" = "platform" }
# 共通タグと結合
all_tags = merge(
{
ManagedBy = "terraform"
Environment = var.environment
},
local.parsed_extra_tags
)
}
8. よくある間違い
mapを出力するのに[ ]を使ってしまう
# ❌ [ ] はリストを生成する。mapを生成するには { } を使う
locals {
wrong = [for k, v in var.my_map : k => v] # SyntaxError
# → Error: Invalid argument
correct = { for k, v in var.my_map : k => v } # { } で囲む
}
キーの重複(groupingモードが必要)
# ❌ 同じキーが複数存在するとエラー
locals {
# 複数のユーザーが同じroleを持つ場合、キーが重複してエラーになる
role_map = { for u in var.users : u.role => u.name }
# Error: Two different items produced the key "viewer"
}
# ✅ groupingモード(...)で同じキーの値をリストにまとめる
locals {
role_map = { for u in var.users : u.role => u.name... }
# → { "admin" = ["alice"], "viewer" = ["bob", "carol"] }
}
for式とfor_each(メタ引数)の混同
# ❌ for式(値を生成する式)とfor_each(リソースを複数作成するメタ引数)は別物
# for式: localsや変数で値を計算するために使う
locals {
names = [for s in var.servers : s.name] # ← for式
}
# for_each: resourceブロックでリソースを複数作成するために使う
resource "aws_instance" "servers" {
for_each = local.servers_map # ← for_eachメタ引数
}
9. for式 vs splat式
リスト内の特定属性を一括取得する場合、for式よりsplat式([*])の方が短く書けます。
# for式: 汎用的、フィルタや変換が可能
[for s in aws_instance.web : s.private_ip]
# splat式: 属性取得だけなら短く書ける
aws_instance.web[*].private_ip
splat式はフィルタや変換が不要な単純な属性取得に向いています。
→ [splat式([*])— リソースリストから属性を一括取得](/docs/expressions/splat/)
10. 関連記事
- for_each — map/setで動的にリソースを作成するメタ引数 — for式で生成したmapをfor_eachに渡す
- locals(ローカル値)の使い方 — for式をlocalsと組み合わせるパターン
- variable(入力変数)の使い方 — for式で変換する元データ
- toset / tolist / tomap — コレクション型変換関数 — for式と組み合わせる型変換
- 条件式(三項演算子)— 使い方
- lookup — mapから安全に値を取り出す
- [splat式([*])— リソースリストから属性を一括取得](/docs/expressions/splat/) — for式の代替パターン
11. まとめ
for式は値を生成する式。リスト出力は[...]、map出力は{...}で囲む- 基本形:
[for <変数> in <コレクション> : <式>] - mapの反復:
for k, v in <map> : ...でキーと値の両方を参照できる ifフィルタ: 末尾にif <条件>を追加すると条件を満たす要素だけを抽出- 最重要パターン:
{ for s in <list> : s.name => s }でリスト→mapに変換してfor_eachに渡す - キーが重複する場合は
...(groupingモード)でリストにまとめる localsと組み合わせて段階的にデータを変換するのが実務の定番
動作確認バージョン: Terraform >= 1.9 / AWS Provider ~> 5.0 対象リージョン: ap-northeast-1(東京) 公式ドキュメント: https://developer.hashicorp.com/terraform/language/expressions/for