Terraform の組み込み関数は、宣言をシンプルに保ちつつ可変性を扱う要となります。特に length、lookup、templatefile は Associate レベルでも頻出です。
本稿では公式仕様に沿って挙動を押さえ、試験で問われやすいポイントと実務のベストプラクティスを一気に整理します。
Terraform の関数は式として評価され、計画時に可能な限り具体化されます。未知の値が混じる(例: 外部データソース)場合は、評価を遅延できない関数はエラーになることがあります。関数は副作用を持たない純粋関数として扱われます。
型は重要です。length は文字列・リスト・セット・マップ(オブジェクト含む)に適用でき、数値やブールには適用できません。lookup はマップから値を取り出し、キーが無いときのみデフォルトを返します(キーが存在し値が null の場合は null を返す点が試験でも狙われます)。templatefile はファイルパスと変数マップを受け取り、テンプレートを展開します。
関数が関わる評価の流れ(概念図)
Inputs (variables, data) ---> Expressions (functions)
| \
| +--> locals
v
Resource arguments ----> Plan ----> Apply
Files: templatefile(path.module/...) --reads--> Template --renders--> String for arguments関数の基本: 型と評価の最小例
variable "names" { type = list(string) default = ["app", "db"] }
variable "tags" { type = map(string) default = {} }
locals {
name_count = length(var.names) # 2
maybe_owner = try(var.tags["owner"], null) # 存在しないなら null
# path.module は現在のモジュールフォルダ
}
length は最頻出の基礎関数です。文字列の長さ、コレクション(list/set/map/tuple/object)の要素数を返します。Associate では count/for_each と組み合わせる場面が典型です。
注意点は null と未知値の扱いです。length(null) は無効な引数エラーになります。未知値が混ざる可能性がある場合は、coalesce と同型の空値でガードする、または try/can を併用します。
length の実践パターン
variable "subnets" { type = list(string) default = [] }
variable "tags" { type = map(string) default = null }
locals {
subnet_count = length(var.subnets) # 0 以上
safe_tag_count = length(coalesce(var.tags, tomap({}))) # null を空マップに置換
non_empty_name = length(trimspace(var.name)) > 0 # 文字列の空判定
}
resource "null_resource" "per_subnet" {
count = length(var.subnets)
}
# 未知の可能性があるとき
locals {
# try で安全に代入(型を合わせるため tomap({}) を第二引数に)
maybe_tags = try(var.tags, tomap({}))
tag_count = length(maybe_tags)
}
マップから安全に値を取り出す標準手は lookup(map, key, default) です。キーが存在しないときのみ default を返し、キーが存在して値が null の場合は null を返します。この挙動は試験で問われやすいポイントです。
直接添字 map["key"] は存在しないキーでエラーになります。未知や欠損の可能性があるなら try(map["key"], default) または lookup を使います。値が空文字や空コレクションの場合にフォールバックしたいときは、trimspace・length などと条件式を組み合わせます。
| 手法 | 存在しないキー | 値が null のとき | 主な使いどころ |
|---|---|---|---|
| lookup(m, "k", "def") | def を返す | null を返す(def にはならない) | キー欠損時のみ既定値を使いたい |
| m["k"] | エラー(Invalid index) | null | キーが必ず存在する前提のとき |
| try(m["k"], "def") | def を返す | def を返す | 欠損や null でも強制的に既定値に寄せたい |
lookup / 添字 / try の安全な取り出し例
variable "meta" { type = map(string) default = {} }
locals {
# 欠損時のみ "dev" を使い、null は null のまま保持
env_lookup = lookup(var.meta, "environment", "dev")
# 欠損や null でも最終的に "dev" にしたい
env_force_default = try(var.meta["environment"], "dev")
# キーの存在チェック
has_env = contains(keys(var.meta), "environment")
# 空文字・空白ならフォールバック
owner_raw = try(var.meta["owner"], null)
owner = (owner_raw != null && trimspace(owner_raw) != "") ? trimspace(owner_raw) : "unknown"
}
templatefile(path, vars) はファイルを読み込み、vars マップで差し込みます。path は path.module を基準に組み立てると、モジュール配布時も安全です。テンプレート内では ${var} 形式で参照し、%{ if } ディレクティブなどの制御構文も使用できます。
テンプレートへ複雑な構造を渡す場合は jsonencode や yamlencode を使ってから渡すと、シェルスクリプトやクラウドイニットとの相性が良くなります。
templatefile の実装例(ユーザーデータを外部化)
# main.tf
variable "cluster_name" { type = string }
variable "tags" { type = map(string) default = {} }
locals {
user_data = templatefile("${path.module}/user_data.sh.tmpl", {
cluster_name = var.cluster_name,
tags_json = jsonencode(var.tags)
})
}
# user_data.sh.tmpl(テンプレートファイルの中身の例)
#cloud-config
runcmd:
- echo "Cluster: ${cluster_name}" > /etc/motd
- echo 'Tags: ${tags_json}' >> /etc/motd
%{ if length(trimspace(cluster_name)) == 0 }
- echo "WARN: cluster name is empty" >> /var/log/setup.log
%{ endif }
実務では関数を組み合わせてロバストな式にします。merge はマップのマージ、coalesce は最初の非 null 値、try は最初に成功する式の値を返します。join/split、compact、distinct、toset などはコレクション整形で定番です。
未知・欠損・空を区別して扱うのがコツです。null と空文字や空コレクションは目的に応じて使い分け、必要に応じて明示的に変換します。
補助関数の組み合わせ例
variable "base_tags" { type = map(string) default = { app = "web" } }
variable "extra_tags" { type = map(string) default = null }
variable "raw_subnets" { type = list(string) default = ["subnet-a", "", "subnet-b", "subnet-a"] }
locals {
tags = merge(var.base_tags, coalesce(var.extra_tags, tomap({})))
# 空文字除去 → 重複排除 → セット化
subnets_clean = toset(distinct(compact(var.raw_subnets)))
# CSV を構築
subnet_csv = join(",", sort(tolist(subnets_clean)))
# 失敗しうる取り出しに try
env = try(var.extra_tags["environment"], "dev")
}
試験では関数の境界条件(null、欠損、空)、評価タイミング(計画時/適用時)、path 変数の使い分けが狙われます。落とし穴を押さえておくと取りこぼしが減ります。
実務では unknown を前提に安全な式にする、テンプレートは templatefile に寄せる、タグやラベルは merge と lookup/try で堅牢に構築するのが定石です。
よくある落とし穴の是正例
# NG: 欠損時にエラー
# local.env = var.tags["environment"]
# OK: 欠損・null に強い
locals {
env = try(var.tags["environment"], "dev")
}
# NG: null をそのまま length に渡す
# local.c = length(var.tags)
# OK: 同型でガード
locals {
c = length(coalesce(var.tags, tomap({})))
}
Associate
問題 1
マップ var.tags からキー "team" を取り出し、キーが存在しない場合にのみ "platform" を返し、キーが存在して値が null の場合は null のままにしたい。最も適切な式はどれか。
正解: A
lookup(map, key, default) はキーが存在しないときのみ default を返し、キーが存在して値が null の場合は null を返す。B はキー欠損でエラー、C は値が null の場合に default へ切り替わるため要件と異なる。D は条件が逆で要件を満たさない。
templatefile のテンプレート内で Terraform の変数 var.x を直接参照できますか?
できません。templatefile に渡したマップのキーのみがテンプレート内で参照可能です。必要な値は templatefile の第2引数に明示的に渡してください。
length で文字列の長さを求める際、マルチバイト文字はどう扱われますか?
文字数としてカウントされます(バイト数ではありません)。したがって length("あ") は 1 になります。
lookup と try の使い分けは?
キー欠損時のみデフォルトに落としたい場合は lookup。キー欠損や null など例外的状況をまとめて既定値に寄せたい場合は try を使います。実務では要件に応じて選び、null と空の違いを明確にします。
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を用いた既存リソース参照の基本、選択基準、評価順序、...