dataブロック — 既存リソース・外部データを参照する

1. 概要

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

  • dataブロックの役割とresourceブロックとの違い
  • dataブロックの基本構文と値の参照方法(data.<TYPE>.<NAME>.<ATTR>
  • よく使うdataソースのパターン(AMI・SSM Parameter Store・IAMポリシー)
  • depends_onが必要なケース
  • よくある間違いと対処法

dataブロック(データソース)は、Terraformが管理していない既存リソースや外部データを参照するための仕組みです。resourceがリソースを「作る」のに対し、dataはリソースを「参照する」だけで、何も作成・変更しません。


2. resourceとの違い

比較項目resourcedata
AWSへの影響作成・変更・削除する読み取るだけ(変更しない)
Stateへの記録✅ 記録される❌ 記録されない
参照方法<TYPE>.<NAME>.<ATTR>data.<TYPE>.<NAME>.<ATTR>
用途インフラを定義する既存リソースの情報を取得する
# resourceブロック: 新しいS3バケットを作成する
resource "aws_s3_bucket" "my_bucket" {
  bucket = "my-new-bucket"
}

# dataブロック: 既存のS3バケットを参照する(作成しない)
data "aws_s3_bucket" "existing" {
  bucket = "already-existing-bucket"
}

3. 基本構文

data "<データソースタイプ>" "<名前>" {
  # フィルター条件(タイプによって異なる)
  <引数> = <値>
}

取得した値の参照方法:

data.<TYPE>.<NAME>.<ATTRIBUTE>

4. よく使うdataソースのパターン

AMI IDを動的に取得する(最重要パターン)

AMI IDは頻繁に更新されるため、ハードコードせずにdataブロックで動的に取得するのがベストプラクティスです。

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

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

# Amazon Linux 2023の最新AMIを動的に取得
data "aws_ami" "amazon_linux_2023" {
  most_recent = true
  owners      = ["amazon"]  # Amazonが公開したAMIのみ

  filter {
    name   = "name"
    values = ["al2023-ami-*-x86_64"]
  }

  filter {
    name   = "virtualization-type"
    values = ["hvm"]
  }
}

resource "aws_instance" "web" {
  ami           = data.aws_ami.amazon_linux_2023.id  # ← 動的に取得したID
  instance_type = "t3.micro"

  root_block_device {
    volume_size = 20
    volume_type = "gp3"
    encrypted   = true
  }

  tags = {
    Name        = "web"
    Environment = "dev"
    ManagedBy   = "terraform"
  }
}

SSM Parameter Storeから値を取得する

機密情報や共有設定をSSMパラメータストアに保存し、dataブロックで取得するパターンです。

# SSM Parameter Store からデータベースのエンドポイントを取得
data "aws_ssm_parameter" "db_endpoint" {
  name = "/myapp/prod/db/endpoint"
}

data "aws_ssm_parameter" "db_password" {
  name            = "/myapp/prod/db/password"
  with_decryption = true  # SecureStringタイプのパラメータを復号して取得
}

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

  user_data = <<-EOF
    #!/bin/bash
    echo "DB_ENDPOINT=${data.aws_ssm_parameter.db_endpoint.value}" >> /etc/app.conf
  EOF

  tags = {
    Name      = "app"
    ManagedBy = "terraform"
  }
}

IAMポリシードキュメントを生成する

aws_iam_policy_documentは、JSONを直接書かずにHCLでIAMポリシーを定義できるデータソースです。

# IAMポリシードキュメントをHCLで定義
data "aws_iam_policy_document" "s3_read" {
  statement {
    effect    = "Allow"
    actions   = ["s3:GetObject", "s3:ListBucket"]
    resources = [
      "arn:aws:s3:::myapp-bucket",
      "arn:aws:s3:::myapp-bucket/*"
    ]
  }

  statement {
    effect    = "Deny"
    actions   = ["s3:DeleteObject"]
    resources = ["arn:aws:s3:::myapp-bucket/*"]
  }
}

resource "aws_iam_policy" "s3_read" {
  name   = "s3-read-policy"
  policy = data.aws_iam_policy_document.s3_read.json  # JSON文字列として取得
}

既存のVPCとサブネットを参照する

Terraform管理外のVPCを使用するケースや、既存の共有VPCを参照するパターン。

# 既存のVPCをタグで検索
data "aws_vpc" "shared" {
  filter {
    name   = "tag:Name"
    values = ["shared-vpc"]
  }
}

# 共有VPC内の特定のサブネットを取得
data "aws_subnets" "private" {
  filter {
    name   = "vpc-id"
    values = [data.aws_vpc.shared.id]
  }

  filter {
    name   = "tag:Tier"
    values = ["private"]
  }
}

resource "aws_instance" "app" {
  ami           = data.aws_ami.amazon_linux_2023.id
  instance_type = "t3.micro"
  subnet_id     = data.aws_subnets.private.ids[0]  # 取得したサブネットを使用

  tags = {
    Name      = "app"
    ManagedBy = "terraform"
  }
}

5. depends_onが必要なケース

通常、dataブロックはplanフェーズで評価されます。しかし、同じapplyで作成したリソースに依存するdataブロックは、そのリソースが作成された後でないと正しく評価できません。

# ❌ apply直後のリソースをdataで参照するとplanで失敗する可能性がある
resource "aws_iam_role" "lambda" {
  name = "lambda-role"
  # ...
}

data "aws_iam_role" "lambda" {
  name = "lambda-role"
  # → planフェーズでlambda-roleがまだ存在しないとエラーになる
}

# ✅ 解決策1: 同じapplyで作ったリソースはresourceで直接参照する(dataは不要)
resource "aws_lambda_function" "main" {
  role = aws_iam_role.lambda.arn  # ← dataではなくresourceを参照
}

# ✅ 解決策2: 別applyで先にリソースを作成してからdataで参照する
# ✅ 解決策3: どうしてもdataが必要なら depends_on を設定する
data "aws_iam_role" "lambda" {
  name       = "lambda-role"
  depends_on = [aws_iam_role.lambda]  # リソース作成後に評価
}

6. よくある間違い

dataで取得した値の参照方法を間違える

# ❌ data. プレフィックスを忘れる
resource "aws_instance" "web" {
  ami = aws_ami.amazon_linux_2023.id  # Error: resourceとして探してしまう
}

# ✅ data. プレフィックスが必要
resource "aws_instance" "web" {
  ami = data.aws_ami.amazon_linux_2023.id
}

filterの条件が複数ヒットして失敗する

│ Error: Your query returned more than one result. Please try a more
│ specific search criteria, or use data.aws_amis to retrieve all AMIs.
# ❌ フィルターが広すぎて複数ヒット
data "aws_ami" "amazon_linux" {
  most_recent = true
  owners      = ["amazon"]
  # filterが足りない
}

# ✅ filterを絞り込む
data "aws_ami" "amazon_linux_2023" {
  most_recent = true
  owners      = ["amazon"]

  filter {
    name   = "name"
    values = ["al2023-ami-*-x86_64"]  # 具体的なパターンを指定
  }
}

7. 関連記事


8. まとめ

  • dataブロックは既存リソース・外部データを「参照するだけ」。Stateには記録されない
  • 参照方法はdata.<TYPE>.<NAME>.<ATTR>(resourceと違いdata.プレフィックスが必要)
  • 最重要パターン: AMI IDをmost_recent = trueで動的に取得する
  • SSM Parameter Storeからシークレットを取得、IAMポリシードキュメントをHCLで定義する用途に使う
  • 同じapplyで作成するリソースをdataで参照するのは避ける(直接resourceを参照する)
  • 「dataを使うべきかresourceを使うべきか」迷ったら → data vs resource — 参照と作成の使い分け

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