Terraform

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

2026-04-19
NicheeLab編集部

Terraform の設定ファイルは HCL で書かれ、最小単位は「ブロック」「属性(引数)」「式」。この3つの違いと関係をつかむと、どのリソースでも迷わず読めて書けます。

本稿は公式ドキュメントの用語と挙動に沿い、Associate 試験の出題傾向を踏まえて、具体例とチェックポイント中心に解説します。

HCL の基本構造:ファイルはブロックの集合、ブロックの中に属性と式

Terraform の .tf ファイルはブロックの集合体です。各ブロックは種類(type)、ラベル(任意、0個以上)、本体({ ... })からなり、本体の中に属性(key = value)や入れ子のブロックが並びます。value の部分に書けるのが式(expression)です。

コメントは # または //、複数行は /* */ が利用できます。識別子は英数字とアンダースコアを用い、先頭は数字不可。ファイルの先頭・末尾に特別な記号は不要で、複数ファイルはディレクトリ単位で結合されます(同一ディレクトリ内の .tf は論理的にマージ)。

  • ブロック例:resource、data、module、provider、variable、output、locals、terraform など
  • 属性はブロック本体の key = value。value は式(リテラル、参照、関数呼び出し、for 式、条件式など)
  • HCL2(Terraform 0.12+)では多くの場面で ${} の明示的な補間は不要

HCL 構文の関係(ファイル → ブロック → 属性/入れ子ブロック → 式)

.tf file複数のブロックの集合blocktype + labels + bodyresource "aws_s3_bucket" "this" { ... }attributekey = valuelength = 2nested block入れ子のブロックlifecycle { ... }attributekey = valuetags = { env = "dev" }expressionliteral / referencefunction / forconditional ...値を作るattribute(s)+ expression(s)block の body と同じ構造(再帰)さらに入れ子可expressionliteral / referencefunction / forconditional ...値を作るファイル → ブロック → 属性 / 入れ子ブロック → 式

最小構成の例:ブロック内に属性と入れ子ブロック

resource "random_pet" "name" {
  length    = 2                # 属性(value は number リテラル)
  prefix    = var.project      # 属性(value は式:変数参照)
  separator = "-"              # 属性(value は string リテラル)
}

output "pet_name" {
  value = random_pet.name.id   # 式:リソース属性参照
}

Block(ブロック):型・ラベル・本体で構成、入れ子も可能

ブロックは HCL の構文単位です。書式は block_type "label1" "label2" { ... }。label は 0 個以上で、resource のように type と name の 2 ラベルをとるものが典型です。入れ子のブロック(例:resource 内の lifecycle、provisioner、dynamic など)もあります。

Terraform の公式用語では、ブロック本体の key = value を argument(引数)と呼びますが、一般に attribute とも表現されます。試験やドキュメントでは argument の語が出ても同義と理解しておくと混乱しません。

  • 代表的なトップレベルブロック:terraform、provider、resource、data、module、variable、output、locals
  • ラベルの有無・数はブロック種別に依存(例:variable は 1 つ、terraform は 0)
  • 同一種別・同一ラベルのブロック重複は基本不可(設定の上書き・競合に注意)
概念役割代表例・書式記述パターン
Block(ブロック)構造を規定(型・ラベル・本体)resource "aws_s3_bucket" "this" { ... }入れ子ブロックや属性を内包
Attribute(引数)ブロックの性質・値を指定bucket = "example"key = value(value は式)
Expression(式)値を計算・合成var.name, aws_vpc.main.id, join(",", list)リテラル/参照/関数/for/条件

ブロックの型とラベル例

# ラベルなし
terraform {
  required_version = ">= 1.5.0"
}

# 1 ラベル
variable "project" {
  type        = string
  description = "Project name"
}

# 2 ラベル(type と name)
resource "random_pet" "this" {
  length = 2
}

# 入れ子ブロック(lifecycle)
resource "null_resource" "hook" {
  triggers = { t = timestamp() }
  lifecycle {
    prevent_destroy = true
  }
}

Attribute(属性/引数):= で値を与える。値は常に『式』

属性は key = value の形でブロック本体に記述します。value は常に式で、リテラル(string/number/bool)、コレクション(list/set/map)、構造体(object/tuple)、参照、関数呼び出し、for/条件式などが使えます。

文字列は二重引用符。複数行はヒアドキュメント(<<EOT ... EOT)を利用可能。true/false はブール値としてクォートしないのが基本です。型は暗黙変換が効く場面もありますが、variable では type を明示し、必要に応じて tonumber などで整えます。

  • 文字列補間:"${var.name}" ではなく "prefix-${var.name}" の形式で記述可能(0.12+)
  • map と object、list と tuple の違いを把握(キー/要素の型固定か、異種混在か)
  • nested block と属性の違い:構造を持つ繰り返し単位はブロック、単一値は属性が基本

属性の型と表現例

variable "env" { type = string }

locals {
  enabled   = true                # bool
  retries   = 3                   # number
  owners    = ["ops", "dev"]      # list(string)
  tags      = { env = var.env }   # map(string)
  config    = { retries = 3, debug = false }  # object
  message   = <<-EOT
Hello ${var.env}
EOT
}

output "tags" {
  value = merge(local.tags, { app = "web" }) # 関数呼び出しも式
}

Expression(式):参照・関数・条件・for で値を組み立てる

式は値を作るための構文です。参照(var.x、local.y、resource.attr)、関数(merge, join, coalesce など)、条件演算子(cond ? x : y)、for 式(リスト/マップ内包表記)、スプラット演算子([*])などが代表です。

0.12+ では多くの場所で ${} を不要に記述できます。文字列中で式を埋め込むときは "name-${var.env}" のように補間を使います。計画時に未確定の値(unknown value)は「known after apply」となり、関数や比較に制約が出る点に注意します。

  • 参照の基本:resource.type.name.attribute、data.source.name.attribute、module.mod.output
  • for 式:list は [for v in xs : expr]、map は { for k, v in m : k => expr if cond }
  • 条件式:cond ? a : b(真偽で型が合うように揃える)

式の具体例(参照・関数・条件・for)

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

locals {
  upper_names = [for n in var.names : upper(n)]
  name_map    = { for idx, n in var.names : idx => n }
  pick        = length(var.names) > 0 ? var.names[0] : "default"
  tags        = merge({ env = "dev" }, { app = "api" })
}

output "first_or_default" {
  value = local.pick
}

実務で効く書き方:for_each / count、depends_on、入力の優先順位

同型リソースの複数作成は for_each または count。キーで管理したい場合や後続参照を安定させたい場合は for_each が有利です。index ベースで十分なら count を選べます。

明示的な依存は depends_on を使用。ただし大半は属性参照で自動的に依存が解決されます。入力値の優先順位は -var / -var-file > *.auto.tfvars > terraform.tfvars > 環境変数 TF_VAR_* > 変数の default(上位が優先)。

  • for_each は map/set を受ける。each.key / each.value で参照
  • count.index は 0 起点。後で順序が変わると差分が大きくなる点に注意
  • 書式は terraform fmt、構文は terraform validate で早期チェック

for_each と count、depends_on の例

variable "subnets" { type = map(string) } # name => cidr

resource "null_resource" "by_each" {
  for_each = var.subnets
  triggers = { name = each.key, cidr = each.value }
}

resource "null_resource" "by_count" {
  count    = 2
  triggers = { idx = count.index }
}

resource "null_resource" "needs_each" {
  depends_on = [null_resource.by_each]
  triggers   = { ready = true }
}

Associate 試験で狙われやすい落とし穴

true/false を文字列で書かない、数値をクォートしないなど、型の素直な表現が基本。式の中でのみ補間(${})を使い、不要な場所では用いないのが 0.12+ の流儀です。

resource と data の参照接頭辞、出力(output)や locals の評価タイミング、unknown value の扱い(plan 時未確定)を取り違えないように。ブロックと属性の区別(例:lifecycle はブロック、tags は属性の map)も頻出ポイントです。

  • 正:enabled = true/誤:enabled = "true"
  • 正:value = aws_vpc.main.id/誤:value = aws_vpc.main(属性まで参照する)
  • 正:for_each でキー安定化/誤:count で順序依存の参照を多用

ありがちな誤りと正しい書き方

# 誤:文字列化された bool
# enabled = "true"
# 正:
enabled = true

# 誤:リソース全体参照
# value = aws_subnet.app
# 正:
value = aws_subnet.app.id

# 誤:不要な補間(0.12+)
# name = "${var.project}"
# 正:
name = var.project

問題で確認

Associate

問題 1

次の設定行は HCL のどの要素を最も正しく表していますか? availability_zones = length(data.aws_availability_zones.current.names)

  1. 属性(引数)に式を割り当てている。式はデータソース参照と関数呼び出しで構成される
  2. 入れ子のブロックを定義している
  3. プロバイダ設定ブロックを宣言している
  4. ラベルを定義している

正解: A

key = value の形で属性(引数)を定義し、value 側は式です。この式は data ソース(data.aws_availability_zones.current.names)を参照し、その長さを length 関数で計算しています。

よくある質問

Terraform 公式が言う argument と attribute は同じ意味ですか?

実質同義です。Terraform の仕様・ドキュメントではブロック本体の key = value を argument(引数)と呼びますが、一般的な解説では attribute と表記されることもあります。試験では argument の語が出ても混乱しないようにしましょう。

${} は今も必要ですか?

0.12 以降、式は素で書けるため多くの場面で不要です。文字列中で変数や参照を埋め込みたいときは "name-${var.env}" のように補間を使います。属性の右辺を丸ごと式にする場合は ${} なしで var.env や local.tags のように書きます。

null と空文字("")の違いは?

null は「値なし」を表し、プロバイダやリソースによりデフォルト適用や未設定扱いになります。空文字は長さ 0 の文字列であり、明示的な値です。条件式や merge の結果で意図せず空文字が入らないよう、coalesce や try でガードすると安全です。

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

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

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

NicheeLab編集部

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


関連記事
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

terraform init 実務と試験に効く: backend・provider の初期化を正しく設計する

Terraform Associate 向けに、terraform init の役割と backend・provider...

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