★ 初級

05. resource と data

Terraform の主役は resource。「これを作って」と命令するブロックです。一方 data は「これを読んで」と既存リソースを参照するブロック。さらにライフサイクル(作り直しや削除保護)の制御も学びます。

resource ブロック

# resource ブロックの基本形
# <TYPE> に provider が定義するリソース種類、<NAME> に自分で決める呼び名
resource "<TYPE>" "<NAME>" {
  # arguments...
}
# 具体例: S3 バケットを作る resource
# - "aws_s3_bucket" = リソース種類(provider が決めた名前)
# - "logs"          = HCL 内での識別子(後で aws_s3_bucket.logs と参照する)
# - bucket = AWS 側での実バケット名(全世界一意)
resource "aws_s3_bucket" "logs" {
  bucket = "my-app-logs-20260510"

  tags = {
    Name = "my-app-logs"
    Env  = "dev"
  }
}

他リソースの属性を参照する

形は <TYPE>.<NAME>.<ATTRIBUTE>。Terraform はこの参照を見て依存順を勝手に決めます。

# 3 つのリソースを作って、subnet と SG が VPC を「参照」している例
# 参照を書くだけで Terraform が依存順を自動判定(暗黙の依存)

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

resource "aws_subnet" "public" {
  vpc_id     = aws_vpc.main.id           # ← VPC の id を参照(VPC が先に作られる)
  cidr_block = "10.0.1.0/24"
}

resource "aws_security_group" "web" {
  vpc_id = aws_vpc.main.id               # ← 同じく VPC を参照
  name   = "web"
}

resource がどんな属性を返すかは、provider のドキュメントの「Attribute Reference」セクションを見ます(例: aws_vpc なら id, arn, default_security_group_id など)。

依存関係(暗黙と明示)

上の例のように、あるリソースが別のリソースの属性を参照していれば、暗黙の依存関係 が自動で作られます。Terraform は「VPC を先に作ってから subnet を作る」と判断します。

属性参照がないのに順序を強制したい時だけ depends_on:

resource "aws_iam_role_policy" "ssm" {
  role   = aws_iam_role.ec2.id
  policy = data.aws_iam_policy_document.ssm.json
}

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

  iam_instance_profile = aws_iam_instance_profile.ec2.name

  # SSM ポリシーが先に attach されてから起動して欲しい(暗黙の依存はないので明示)
  depends_on = [aws_iam_role_policy.ssm]
}
使いすぎ注意 depends_on本当に必要な時だけ。属性参照で済むなら参照のほうが意図が明確で、リファクタも壊れにくい。

data ソース

「すでに存在するもの」を Terraform に読ませるブロック。新しくは何も作りません。

# data ソース: 既存リソースの情報を「読むだけ」(新規作成はしない)

# AWS から「最新の Amazon Linux 2023 の AMI」を毎回検索して取得
data "aws_ami" "al2023" {
  most_recent = true                    # 複数候補から最新を選ぶ
  owners      = ["amazon"]              # Amazon 公式が所有する AMI に絞る

  filter {
    name   = "name"
    values = ["al2023-ami-*-x86_64"]    # 名前パターンで絞り込み
  }
}

# 現在の AWS アカウントの情報(account_id, arn, user_id)を取得
data "aws_caller_identity" "current" {}

# 現在のリージョン情報(name, description)を取得
data "aws_region" "current" {}

# data の参照は「data.<TYPE>.<NAME>.<ATTR>」形式
resource "aws_instance" "web" {
  ami           = data.aws_ami.al2023.id        # ← AMI ID をハードコードせず取得
  instance_type = "t3.micro"

  tags = {
    # ハードコードを排除(account ID / region をコードに書かない)
    Account = data.aws_caller_identity.current.account_id
    Region  = data.aws_region.current.name
  }
}

resource と data の使い分け

resourcedata
役割作る/変える/消す読むだけ
state に書くはい読み込んだ結果は持つが、消えても無害
典型例新しい VPC を作る会社共通 VPC の ID を引いて使う

lifecycle ─ 作り直しと削除保護

リソースの「変化のさせ方」を制御するメタブロック。

resource "aws_security_group" "web" {
  name_prefix = "web-"
  vpc_id      = aws_vpc.main.id

  lifecycle {
    create_before_destroy = true     # 新しい SG を先に作ってから古いのを消す
  }
}

resource "aws_db_instance" "main" {
  # ...
  lifecycle {
    prevent_destroy = true           # destroy しようとするとエラー(DB を守る)
    ignore_changes = [
      password,                       # この属性の差分は無視(外で変更)
      tags["LastBackup"],             # ネストした要素も指定可
    ]
  }
}

resource "aws_instance" "web" {
  # ...
  lifecycle {
    precondition {
      condition     = data.aws_ami.al2023.architecture == "x86_64"
      error_message = "x86_64 の AMI を使ってください。"
    }
    postcondition {
      condition     = self.public_dns != ""
      error_message = "Public DNS が割り当てられていません。"
    }
  }
}

主な lifecycle 引数

引数役割使う場面
create_before_destroy置換時に新を先に作るSG, ELB など名前一意で停止が許されないもの
prevent_destroydestroy をエラーに本番 DB、本番 S3 バケット
ignore_changes属性差分を無視外部ツールが書き換える tag、自動更新される password
replace_triggered_by別リソース変更で強制再作成ASG が起動テンプレ更新で再起動して欲しい
precondition / postcondition事前・事後検証入力 AMI が想定アーキテクチャか等

メタ引数の早見表

メタ引数 は、どの resource でも共通で使える特別な引数です(プロバイダ固有ではない)。

メタ引数役割
count同じものを N 個作る07
for_eachmap / set から複数作る07
provider使う provider のエイリアスを指定本章末
depends_on明示的依存本章上
lifecycle作り直し制御本章上