localsブロック — 繰り返しを減らすローカル値の定義

1. 概要

この記事では、以下の内容を解説します。

  • localsブロックの基本構文と使いどころ
  • variableとの違いと使い分けの判断基準
  • for式との組み合わせ(リスト変換・map生成)
  • タグのDRY化など実務でよく使うパターン
  • 初心者がつまずくポイント

locals(ローカル値)は、コード内で繰り返し使う式や計算結果に名前をつけるための仕組みです。variableが「外から値を受け取る」のに対し、localsは「コード内で計算した値を再利用する」ために使います。


2. localsブロックとは

locals {
  # 名前をつけて再利用したい式を定義
  app_name = "${var.project}-${var.environment}"
  is_production = var.environment == "prd"
}

# local.<名前> で参照する
resource "aws_instance" "web" {
  tags = {
    Name = local.app_name  # ← local. で参照(locals. ではない)
  }
}

💡 ブロック名 vs 参照名: ブロックはlocals(複数形)、参照するときはlocal(単数形)です。locals.app_nameは誤りで、正しくはlocal.app_nameです。


3. variableとの違い

比較項目variablelocals
外部から値を変えられるか✅ tfvarsや-varで変更可能❌ コード内で固定
Terraformの外部から渡すか✅ ユーザーやCIが渡す❌ コード内で計算
動的な式を定義できるか❌ 式は書けない(値のみ)✅ 任意の式・計算結果
主な用途環境・設定値の切り替え繰り返しの式に名前をつける

判断フローチャート

値を定義したい
       │
       ▼
「terraform apply -var で外から変えたい?」
       │
  Yes ─┤         No
       │           │
       ▼           ▼
   variable    「コード内で式や計算をしたい?」
                   │
              Yes ─┤       No
                   │        │
                   ▼        ▼
                locals   (定数ならvariableのdefaultで可)

詳細な比較は「locals vs variable — 違いと使い分けの完全ガイド」を参照してください。


4. 基本的な使いどころ

長い式に名前をつける

terraform {
  required_version = ">= 1.9"
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

provider "aws" {
  region = "ap-northeast-1"
}

variable "project" {
  type = string
}

variable "environment" {
  type = string
}

locals {
  # 繰り返し使うプレフィックスを1か所で定義
  name_prefix = "${var.project}-${var.environment}"

  # 条件式の結果に名前をつける(可読性向上)
  is_production = var.environment == "prd"

  # 複雑な計算結果に名前をつける
  retention_days = local.is_production ? 90 : 7
}

resource "aws_cloudwatch_log_group" "app" {
  name              = "/aws/${local.name_prefix}/app"  # 毎回書かなくて済む
  retention_in_days = local.retention_days

  tags = {
    Name        = "${local.name_prefix}-log-group"
    Environment = var.environment
    ManagedBy   = "terraform"
  }
}

共通のタグをまとめる(DRY化)

locals {
  # 全リソースに付けるタグを一元管理
  common_tags = {
    Project     = var.project
    Environment = var.environment
    ManagedBy   = "terraform"
    Owner       = "infra-team"
  }
}

resource "aws_vpc" "main" {
  cidr_block = "10.0.0.0/16"

  # merge()で共通タグ + 個別タグを結合
  tags = merge(local.common_tags, {
    Name = "${local.name_prefix}-vpc"
  })
}

resource "aws_instance" "web" {
  ami           = data.aws_ami.amazon_linux_2023.id
  instance_type = "t3.micro"

  tags = merge(local.common_tags, {
    Name = "${local.name_prefix}-web"
    Role = "web-server"
  })
}

環境名から派生する値を計算する

locals {
  # 環境ごとの設定をmapで定義
  env_config = {
    dev = { instance_type = "t3.micro",  min_capacity = 1, max_capacity = 2  }
    stg = { instance_type = "t3.small",  min_capacity = 1, max_capacity = 4  }
    prd = { instance_type = "t3.medium", min_capacity = 2, max_capacity = 10 }
  }

  # 現在の環境の設定を取得(lookupより安全)
  current_config = local.env_config[var.environment]
}

resource "aws_instance" "app" {
  ami           = data.aws_ami.amazon_linux_2023.id
  instance_type = local.current_config.instance_type  # 環境に応じて自動選択

  tags = merge(local.common_tags, {
    Name = "${local.name_prefix}-app"
  })
}

5. for式との組み合わせ

localsfor式を組み合わせると、データを柔軟に変換できます。

リストを変換してlocalsに格納する

variable "availability_zones" {
  type    = list(string)
  default = ["ap-northeast-1a", "ap-northeast-1c", "ap-northeast-1d"]
}

locals {
  # AZリストから末尾1文字(a/c/d)を抽出したリスト
  az_suffixes = [for az in var.availability_zones : substr(az, -1, 1)]

  # サブネット名のリストを生成
  private_subnet_names = [
    for az in var.availability_zones : "${local.name_prefix}-private-${az}"
  ]
}

mapを生成してfor_eachに渡す

variable "subnet_configs" {
  type = list(object({
    name = string
    cidr = string
    az   = string
  }))
  default = [
    { name = "public-1a",  cidr = "10.0.1.0/24",  az = "ap-northeast-1a" },
    { name = "public-1c",  cidr = "10.0.2.0/24",  az = "ap-northeast-1c" },
    { name = "private-1a", cidr = "10.0.11.0/24", az = "ap-northeast-1a" },
    { name = "private-1c", cidr = "10.0.12.0/24", az = "ap-northeast-1c" },
  ]
}

locals {
  # listをfor_eachに渡せるmap(name → object)に変換
  subnets_map = { for s in var.subnet_configs : s.name => s }
}

resource "aws_subnet" "main" {
  for_each = local.subnets_map  # localsを経由してfor_eachに渡す

  vpc_id            = aws_vpc.main.id
  cidr_block        = each.value.cidr
  availability_zone = each.value.az

  tags = merge(local.common_tags, {
    Name = each.key
  })
}

6. 初心者がつまずくポイント

localsは再代入できない

localsブロックで一度定義した値は変更できません。

locals {
  name = "web"
}

# ❌ エラー: localsは再代入不可
locals {
  name = "app"  # Duplicate local name
}

複数のlocalsブロックを書くこと自体は可能ですが、同じ名前は使えません。

循環参照は作れない

# ❌ エラー: 循環参照
locals {
  a = local.b + 1
  b = local.a + 1  # 互いに依存しているため評価できない
}

local は参照するとき単数形

locals {
  prefix = "web"  # 定義はlocals(複数形)
}

resource "aws_instance" "web" {
  # ❌ 間違い
  # tags = { Name = locals.prefix }

  # ✅ 正しい: 参照はlocal(単数形)
  tags = { Name = local.prefix }
}

7. 関連記事


8. まとめ

  • localsはコード内で繰り返し使う式や計算結果に名前をつける仕組み
  • 参照するときはlocal.<名前>(単数形)。ブロック定義はlocals(複数形)
  • variableとの違い: 外から変えられる→variable、コード内で計算→locals
  • タグの共通化(merge(local.common_tags, {...}))が最もよく使われるパターン
  • for式と組み合わせてlistをmapに変換し、for_eachに渡すのが実務の定番
  • localsは再代入不可・循環参照不可

動作確認バージョン: Terraform >= 1.9 / AWS Provider ~> 5.0 対象リージョン: ap-northeast-1(東京) 公式ドキュメント: https://developer.hashicorp.com/terraform/language/values/locals