★ 初級

11. データソース集

AWS Provider のうち、「読むだけ」 のデータソース。実物を作らずに既存リソースの情報を取得する仕組み。これらを覚えると、コードがハードコードから解放されて環境を移しやすくなります。

アカウント・リージョン情報

# data ソース 3 種(引数なし/空 {} で済む)
# - aws_caller_identity = 今 Terraform を実行している主体の情報
# - aws_region          = 現在の region 情報
# - aws_partition       = "aws"/"aws-cn"/"aws-us-gov" のどれか
data "aws_caller_identity" "current" {}
data "aws_region" "current" {}
data "aws_partition" "current" {}

# よく使うパターン: 上の data から取った値で ARN を組み立てる
# ハードコードしない → 別環境にコピペしても動く
locals {
  account_id = data.aws_caller_identity.current.account_id
  region     = data.aws_region.current.name
  partition  = data.aws_partition.current.partition   # 通常 "aws"

  # 例: arn:aws:logs:ap-northeast-1:123456789012:log-group:/aws/lambda/hello
  log_group_arn = "arn:${local.partition}:logs:${local.region}:${local.account_id}:log-group:/aws/lambda/hello"
}

aws_partition を使うと、中国リージョン(aws-cn)や GovCloud(aws-us-gov)を含むコードが書けます。普通は aws 固定で OK ですが、ARN を組む時は癖にしておくと安全。

アベイラビリティゾーン

# 現在のリージョンで利用可能な AZ 名の list を取得
# state = "available" → 一時的にダウンしている AZ を除外
data "aws_availability_zones" "available" {
  state = "available"

  # ローカルゾーンや Wavelength(特殊な opt-in 必要 AZ)を除外
  filter {
    name   = "opt-in-status"
    values = ["opt-in-not-required"]
  }
}

# 使い方: 取得した AZ list を使って subnet を 2 個(AZ ごとに 1 つ)作成
# count.index = 0, 1 → AZ 0, 1 にそれぞれ subnet を配置
resource "aws_subnet" "public" {
  count             = 2
  vpc_id            = aws_vpc.main.id
  cidr_block        = "10.0.${count.index + 1}.0/24"   # 10.0.1.0/24, 10.0.2.0/24
  availability_zone = data.aws_availability_zones.available.names[count.index]
}

AMI(最新・特定)

# Amazon Linux 2023 の最新 AMI を毎回検索
# most_recent + owners + filter で「Amazon 公式 + 名前パターン」を絞る
data "aws_ami" "al2023" {
  most_recent = true                    # 候補から最新作成日のものを選ぶ
  owners      = ["amazon"]              # 所有者を Amazon に限定

  filter {
    name   = "name"
    values = ["al2023-ami-*-x86_64"]    # 名前のワイルドカードマッチ
  }
}

# Ubuntu 24.04 の最新(owners に Canonical 社のアカウント ID を指定)
data "aws_ami" "ubuntu" {
  most_recent = true
  owners      = ["099720109477"]   # ← Canonical(Ubuntu 配布元)の公式 AWS アカウント

  filter {
    name   = "name"
    values = ["ubuntu/images/hvm-ssd-gp3/ubuntu-noble-24.04-amd64-server-*"]
  }
}

# 自社で焼いた AMI を引く(owners に "self" を指定すると自アカウントの AMI のみ)
data "aws_ami" "internal" {
  most_recent = true
  owners      = ["self"]

  filter {
    name   = "name"
    values = ["myapp-*"]   # 自社命名規則のプレフィックス
  }
}

IAM ポリシー組み立て

# IAM ポリシー JSON を HCL で組み立てる(jsonencode より読みやすい)
# .json 属性で IAM-policy 形式の JSON 文字列が取れる
data "aws_iam_policy_document" "lambda_assume" {
  statement {
    effect  = "Allow"
    actions = ["sts:AssumeRole"]
    principals {
      type        = "Service"
      identifiers = ["lambda.amazonaws.com"]
    }
  }
}

# 複数のポリシー文書を 1 つに合成(merge する)
# source_policy_documents に並べると、statements がすべて結合された JSON が返る
data "aws_iam_policy_document" "merged" {
  source_policy_documents = [
    data.aws_iam_policy_document.s3_read.json,
    data.aws_iam_policy_document.dynamodb_rw.json,
  ]
}

ネットワーク既存参照

「会社共通の VPC があるので、それを使う」「既存の subnet を見つける」といった使い方。

# タグで既存 VPC を検索(Name タグが "shared-main" の VPC を引く)
data "aws_vpc" "main" {
  tags = { Name = "shared-main" }
}

# 上の VPC 内の subnet を、タグで絞り込んで一覧取得
# 返るのは ids 属性に subnet ID の list
data "aws_subnets" "private" {
  filter {
    name   = "vpc-id"
    values = [data.aws_vpc.main.id]
  }

  tags = { Tier = "private" }
}

# 使い方: 取得した list の 1 番目を subnet_id に渡す
resource "aws_instance" "worker" {
  subnet_id = data.aws_subnets.private.ids[0]
  # ...
}

Route 53 ホストゾーン

# 既存の Route 53 ホストゾーンをドメイン名で検索
# Terraform でゾーン自体を作らず、外で作られたゾーンに「レコードだけ追加」する用途
data "aws_route53_zone" "this" {
  name = "hcl-guide.com"
  # private_zone = false  # public/private 両方ある場合に明示
}

# 取得したゾーンに新しい A レコードを追加
resource "aws_route53_record" "blog" {
  zone_id = data.aws_route53_zone.this.zone_id   # data から zone_id を取得
  name    = "blog.hcl-guide.com"
  type    = "A"
  # ...
}

Secrets Manager / SSM Parameter Store

API キー・パスワードを .tfvars に書かず、Secrets Manager / SSM から取得します。

# Secrets Manager から最新版のシークレットを取得
# secret_string プロパティに「文字列としての中身」が入っている
data "aws_secretsmanager_secret_version" "db" {
  secret_id = "myapp/db/master"
}

# 慣例: シークレットの中身を JSON で入れておき、jsondecode で構造化
# 例: {"username":"admin","password":"..."} → { username=..., password=... }
locals {
  db = jsondecode(data.aws_secretsmanager_secret_version.db.secret_string)
}

# 取り出した値は local.db.username / local.db.password で使える
# SSM Parameter Store から値を取得(名前で指定)
# value プロパティに中身が入る。SecureString でも復号済みの値が取れる
data "aws_ssm_parameter" "api_key" {
  name = "/myapp/external-api-key"
}

# Lambda の環境変数として渡す例
resource "aws_lambda_function" "x" {
  environment {
    variables = {
      API_KEY = data.aws_ssm_parameter.api_key.value
    }
  }
}
秘密が state に入る data ソースで取得した秘密は、Terraform の state ファイルに平文で保存 されます。state を S3 に置く時は 必ず暗号化(バケットの SSE-KMS)と アクセス制限(IAM、bucket policy)をかける。

KMS キー

# AWS が用意した「EBS 用デフォルト KMS キー」のエイリアスを引く
# target_key_arn に実際の KMS キーの ARN が入る
data "aws_kms_alias" "ebs" {
  name = "alias/aws/ebs"
}

# 使い方: 取得した KMS キーの ARN を EBS ボリュームの暗号化に使う
resource "aws_ebs_volume" "data" {
  availability_zone = "ap-northeast-1a"
  size              = 100
  encrypted         = true
  kms_key_id        = data.aws_kms_alias.ebs.target_key_arn
}

ハードコード排除のコツ

Terraform 学習者がやりがちな「ハードコード」と、置き換える data:

ハードコード置き換え先
"123456789012"data.aws_caller_identity.current.account_id
"ap-northeast-1"data.aws_region.current.name
["ap-northeast-1a","1c"]data.aws_availability_zones.available.names
"ami-0c4..."data.aws_ami.al2023.id
"db_password = ..."data.aws_secretsmanager_secret_version
vpc_id = "vpc-xxx"data.aws_vpc.main.id

これだけで、コードは「環境を変えても動く」状態に近づきます。

AWS 編はここまで お疲れ様でした。HCL × AWS × GitHub の 3 領域で、Terraform の実務に必要な土台が揃いました。次は実際に ホーム から興味のあるトピックを行ったり来たりして、自分のプロジェクトで手を動かしてみてください。