★ 初級

03. 値(variable / local / output)

Terraform の「値」には 3 つの場所 があります。variable は外から受け取る値、local は中で計算する値、output は外に返す値。役割をちゃんと分けると、コードがぐっと読みやすくなります。

3 種類の使い分け

役割参照する書き方使う場面
variable外から受け取る入力var.NAME環境ごとに変えたい値(リージョン、サイズ)
local内部で計算する中間値local.NAME長い式の名前付け、共通タグ
output外に出す結果(モジュールから親へ)VPC ID を別モジュールに渡す等

variable(入力)

# 文字列の変数(必須項目ではない=default あり)
# 使う側は var.region でアクセス
variable "region" {
  type        = string
  description = "AWS リージョン"
  default     = "ap-northeast-1"
}

# 文字列の変数(最小限の指定)
variable "instance_type" {
  type    = string
  default = "t3.micro"
}

# list 型の変数(同じ型の要素を順序付きで複数持つ)
variable "subnet_cidrs" {
  type    = list(string)
  default = ["10.0.1.0/24", "10.0.2.0/24"]
}

# map 型の変数(キーと値のペア。値はすべて同じ型)
variable "tags" {
  type = map(string)
  default = {
    Project = "hcl-guide"
    Env     = "dev"
  }
}

variable の主要属性

属性意味
type受け付ける型(次章)。型を書くとミス値で弾かれる
default省略時の値。書かないと「必須入力」
description説明。terraform plan やドキュメント生成で使われる
sensitivetrue で plan/apply 出力にマスク表示
nullablefalsenull 不可(デフォルト true)
validation値の妥当性チェック(後述)

variable に値を渡す方法と優先順位

同じ変数に複数の経路から値が来ると、下に行くほど強い(後から上書き)

  1. variable の default(最弱)
  2. 環境変数 TF_VAR_NAME
  3. terraform.tfvars
  4. terraform.tfvars.json
  5. *.auto.tfvars / *.auto.tfvars.json(辞書順に)
  6. CLI の -var-file / -var(最強)

tfvars ファイルの書き方

# terraform.tfvars
region        = "us-east-1"
instance_type = "t3.small"
subnet_cidrs  = ["10.10.1.0/24", "10.10.2.0/24"]
# 環境ごとの tfvars を CLI で指定
terraform apply -var-file=envs/prd/terraform.tfvars

# ピンポイントで上書き
terraform apply -var="instance_type=t3.large"

# 環境変数で渡す(自動化で便利)
export TF_VAR_region=ap-northeast-1
terraform apply

validation で値を検証する

受け取った値が「想定外」で apply が走ってしまう前に、validation で止められます。

# validation ブロック: 値を受け取った直後にチェック
# condition が false なら error_message を表示して plan を中断
variable "environment" {
  type        = string
  description = "環境名"

  validation {
    # contains(LIST, VALUE) で「LIST に VALUE が含まれているか」を判定
    condition     = contains(["dev", "stg", "prd"], var.environment)
    error_message = "environment は dev / stg / prd のいずれかにしてください。"
  }
}

# 複雑な検証は can() と組み合わせる
variable "vpc_cidr" {
  type = string

  validation {
    # cidrnetmask() は無効な CIDR でエラーになる
    # can() で「エラーにならず評価できるか」を真偽値に変換
    condition     = can(cidrnetmask(var.vpc_cidr))
    error_message = "vpc_cidr は有効な CIDR でなければなりません(例: 10.0.0.0/16)。"
  }
}

locals(内部計算)

locals は「式の結果に名前を付ける」場所。同じ式を 3 か所で使うなら local にまとめると変更が 1 か所で済みます。

# locals ブロック: 「式の結果に名前を付ける」場所
# 参照側は local.name_prefix のように local.XXX で書く(locals ではなく local)
locals {
  # 文字列を組み合わせて命名プレフィックス。例: "myapp-prd"
  name_prefix = "${var.project}-${var.environment}"

  # 比較式の結果(true / false)を保存。後で if 的に使う
  is_prod     = var.environment == "prd"

  # 共通タグを 1 か所で定義 → 複数リソースで使い回し
  common_tags = {
    Project   = var.project
    Env       = var.environment
    ManagedBy = "Terraform"
  }
}

# local.name_prefix で参照 → "myapp-prd-logs" というバケット名になる
resource "aws_s3_bucket" "logs" {
  bucket = "${local.name_prefix}-logs"
  tags   = local.common_tags   # ← 同じ tags をそのまま使える
}

# data バケットも同じ local を使う → 名前付けとタグが揃う
resource "aws_s3_bucket" "data" {
  bucket = "${local.name_prefix}-data"
  tags   = local.common_tags
}
注意 ブロック名は locals(複数形)ですが、参照は local.NAME(単数形)です。間違えやすいポイント。

output(出力)

「root モジュール」(プロジェクト直下)の output は terraform apply の最後に CLI に表示されます。「子モジュール」の output は親から module.NAME.OUTPUT で参照できます。

# シンプルな output: 単一の値を返す
# 参照: root module なら CLI 表示、子 module なら module.NAME.vpc_id で取れる
output "vpc_id" {
  description = "作成した VPC の ID"
  value       = aws_vpc.main.id
}

# for 式で list を作って返す
# aws_subnet.public は for_each で作られた map 想定 → 各 s の id を集めた list
output "public_subnet_ids" {
  value = [for s in aws_subnet.public : s.id]
}

# 機密値は sensitive = true でマスク
# → terraform output しても  と表示される(state には平文で残るので注意)
output "db_password" {
  value     = random_password.db.result
  sensitive = true
}

子モジュールの出力を親で使う

# 親 (root)
module "network" {
  source = "./modules/network"
  cidr   = "10.0.0.0/16"
}

resource "aws_instance" "web" {
  subnet_id = module.network.public_subnet_ids[0]   # ← 子の output を参照
  # ...
}