この記事では、aws_ami・aws_vpc・aws_subnets・aws_caller_identity を使い、IDのハードコードをゼロにするポータブルなIaC設計を実践解説します。filterブロックの書き方、よくある「No results」エラーの対処まで、実際のterraform plan出力例を交えて解説します。
動作確認環境: Terraform 1.8.5 / AWS Provider 5.50.0(ap-northeast-1リージョン)
この記事のポイント
・data sourceはリソースを作らず既存の属性値をTerraformコードへ動的に取り込む仕組み
・aws_amiでmost_recent=trueとfilterを組み合わせると常に最新AMIを自動参照できる
・aws_vpc・aws_subnetsで既存VPCのサブネットIDを一覧取得しEC2やALBへ渡せる
・「No results」エラーはfilterのName値とAWSのタグ名の不一致が主な原因
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
なぜdata sourceを使うのか?ハードコードの3つの問題点
Terraformのresourceブロックは「リソースを作成・管理する」定義です。一方、dataブロック(data source)は「すでに存在するリソースの情報を読み取るだけ」の仕組みです。terraform apply を実行しても、data sourceが参照するリソースの作成・変更・削除は一切行いません。ハードコードの問題を具体的に見てみましょう。
# AMI IDとサブネットIDをベタ書き(問題のある書き方) resource "aws_instance" "web" { ami = "ami-0abcdef1234567890" # 廃止されてもすぐ気づかない instance_type = "t3.micro" subnet_id = "subnet-xxxxxxxx" # 環境ごとにコードを複製する羽目になる }
・AMI廃止リスク:AWSはOSのセキュリティパッチを適用した新しいAMIを定期的にリリースし、古いものを廃止します。廃止後にterraform applyすると「AMI not found」エラーになります
・リージョン非互換:AMI IDはリージョンごとに異なります。東京(ap-northeast-1)のコードを大阪(ap-northeast-3)で使うと別の値が必要になります
・環境ごとのコード複製:dev・stg・prodで別々のVPCとサブネットを使う場合、コードをコピーして値を書き換える手作業が発生します
data sourceを使えば、これらを動的に解決できます。
data sourceブロックの基本構文
1. resourceとdataの書き方の違い
# resource: Terraformが作成・管理する(tfstateに記録される) resource "aws_vpc" "new_vpc" { cidr_block = "10.0.0.0/16" } # data: すでにあるリソースを読み取るだけ(作成・変更・削除しない) data "aws_vpc" "existing" { filter { name = "tag:Name" values = ["production-vpc"] } } # data sourceへの参照は data.タイプ.名前.属性名 の形式 output "existing_vpc_cidr" { value = data.aws_vpc.existing.cidr_block }
2. filterブロックの書き方
filterブロックは、AWS APIのフィールド名(name)と絞り込む値(values)でリソースを特定します。data "aws_ami" "example" { most_recent = true owners = ["amazon"] # filterは複数書けばAND条件で絞り込まれる filter { name = "name" # AMIの名前フィールド(ワイルドカード可) values = ["al2023-ami-*"] } filter { name = "architecture" values = ["x86_64"] # valuesの中はOR条件 } }
aws_amiで最新AMIを動的取得する
1. Amazon Linux 2023の最新AMIを自動参照する
# main.tf data "aws_ami" "amzn2023" { most_recent = true owners = ["amazon"] filter { name = "name" values = ["al2023-ami-2023*-x86_64"] } filter { name = "virtualization-type" values = ["hvm"] } } resource "aws_instance" "web" { ami = data.aws_ami.amzn2023.id # data sourceで動的参照 instance_type = "t3.micro" tags = { Name = "web-server" } }
# terraform plan の実行例(ap-northeast-1リージョン) $ terraform plan data.aws_ami.amzn2023: Reading... data.aws_ami.amzn2023: Read complete after 1s [id=ami-0b5c74e235407b044] Terraform will perform the following actions: # aws_instance.web will be created + resource "aws_instance" "web" { + ami = "ami-0b5c74e235407b044" + instance_type = "t3.micro" + tags = { + "Name" = "web-server" } ... } Plan: 1 to add, 0 to change, 0 to destroy.
2. Ubuntu 24.04 LTSの最新AMIを取得する
Ubuntuの場合はownersにCanonicalのAWSアカウントIDを指定します。data "aws_ami" "ubuntu_2404" { most_recent = true owners = ["099720109477"] # CanonicalのAWSアカウントID(固定値) filter { name = "name" values = ["ubuntu/images/hvm-ssd-gp3/ubuntu-noble-24.04-amd64-server-*"] } filter { name = "virtualization-type" values = ["hvm"] } }
既存VPCとサブネットをdata sourceで参照する
1. aws_vpcで既存VPCをタグ名で参照する
Terraform管理外の既存VPC(マネジメントコンソールや別のTerraformプロジェクトで作成したVPC)を参照する場合はaws_vpcを使います。# Nameタグで既存VPCを参照 data "aws_vpc" "production" { filter { name = "tag:Name" values = ["production-vpc"] } } # 取得した属性値の参照例 output "vpc_id" { value = data.aws_vpc.production.id } output "vpc_cidr" { value = data.aws_vpc.production.cidr_block }
2. aws_subnetsで複数サブネットIDを一括取得する
ECSやALBのマルチAZ構成では複数のサブネットIDが必要です。aws_subnets(複数形)を使うと、条件に一致するすべてのサブネットIDをリストで取得できます。# VPC内のプライベートサブネットIDをすべて取得 data "aws_subnets" "private" { filter { name = "vpc-id" values = [data.aws_vpc.production.id] } filter { name = "tag:Type" values = ["private"] } } # ALBにサブネットIDリストをそのまま渡す resource "aws_lb" "app" { name = "app-alb" internal = true load_balancer_type = "application" subnets = data.aws_subnets.private.ids # IDリストを直接渡せる }
3. aws_vpc・aws_subnets・aws_amiを組み合わせてIDハードコードゼロにする
3つを組み合わせると、すべてのIDをdynamic参照にできます。# 既存VPCをタグで参照 data "aws_vpc" "main" { filter { name = "tag:Name" values = ["main-vpc"] } } # 最新Amazon Linux 2023 AMIを動的取得 data "aws_ami" "amzn2023" { most_recent = true owners = ["amazon"] filter { name = "name" values = ["al2023-ami-2023*-x86_64"] } } # パブリックサブネットを取得 data "aws_subnets" "public" { filter { name = "vpc-id" values = [data.aws_vpc.main.id] } filter { name = "tag:Type" values = ["public"] } } # EC2インスタンス(IDハードコードなし) resource "aws_instance" "web" { ami = data.aws_ami.amzn2023.id instance_type = "t3.micro" subnet_id = data.aws_subnets.public.ids[0] tags = { Name = "web-server" } }
>> Terraform実践セミナーの詳細はこちら
便利なdata source一覧(aws_caller_identity・aws_region・aws_availability_zones)
AMIやVPC以外にも頻繁に使う便利なdata sourceがあります。1. aws_caller_identityでAWSアカウントIDを取得する
IAMポリシーやS3バケットポリシーで自分のAWSアカウントIDが必要な場合に使います。data "aws_caller_identity" "current" {} # S3バケットポリシーでアカウントIDをハードコードしない resource "aws_s3_bucket_policy" "example" { bucket = aws_s3_bucket.example.id policy = jsonencode({ Statement = [{ Effect = "Allow" Principal = { AWS = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:root" } Action = "s3:GetObject" Resource = "${aws_s3_bucket.example.arn}/*" }] }) }
2. aws_regionとaws_availability_zonesでリージョン・AZ情報を取得する
# 現在のリージョン名を取得(マルチリージョン展開で使う) data "aws_region" "current" {} # 利用可能なAZを全取得(stateでフィルタリング可能) data "aws_availability_zones" "available" { state = "available" } # 先頭2つのAZにサブネットを作成(AZ名のハードコードなし) resource "aws_subnet" "public_a" { vpc_id = aws_vpc.main.id cidr_block = "10.0.1.0/24" availability_zone = data.aws_availability_zones.available.names[0] } resource "aws_subnet" "public_c" { vpc_id = aws_vpc.main.id cidr_block = "10.0.2.0/24" availability_zone = data.aws_availability_zones.available.names[1] }
トラブルシュート・エラー対処
「Your query returned no results」が出た時の対処法
filterの条件に一致するリソースが1件もない場合に発生します。$ terraform plan # エラー出力例 Error: Your query returned no results. Please change your search criteria and try again. with data.aws_ami.amzn2023, on main.tf line 1, in data "aws_ami" "amzn2023": 1: data "aws_ami" "amzn2023" {
・AWS CLIで同じ条件を検証する:
aws ec2 describe-images --owners amazon --filters "Name=name,Values=al2023-ami-2023*-x86_64" を実行して実際に返ってくる値を確認する・filter.name値を見直す:タグフィルターは大文字小文字を区別する。`tag:Environment` のEnvironmentは実際のタグキーと完全一致が必要
・ワイルドカードの確認:values内の `*` はAWS APIのパターンマッチング構文で正規表現ではない
注意: filterのname(小文字)とvaluesの大文字小文字を混同するミスが本番環境でも頻繁に起きます。AWSコンソールで実際のタグ名を確認してから書くのが確実です。
「Your query returned more than one result」が出た時の対処法
複数のリソースが条件に一致し、1件に絞れない場合に発生します。aws_ami以外のdata sourceで多いエラーです。$ terraform plan # エラー出力例 Error: Your query returned more than one result. Please try a more specific search criteria. with data.aws_vpc.main, on main.tf line 10, in data "aws_vpc" "main": 10: data "aws_vpc" "main" {
・filterを追加して候補を1件に絞る(Nameタグに加えてEnvironmentタグも絞り込む等)
・VPC IDが固定であれば id = "vpc-xxxxx" で直接指定する方が確実
・複数件が想定される場合はaws_vpcs(複数形のdata source)を使いids属性でIDリストを取得する
data sourceが古いキャッシュを返している疑いがある時
Terraformはplan実行時にdata sourceを再読み込みしますが、AWSリソースを外部で変更した直後に反映されないケースがあります。その場合は `-refresh-only` で強制的に最新状態に同期します。# stateを最新のAWS状態に同期する(リソースの変更はしない) $ terraform plan -refresh-only $ terraform apply -refresh-only
本記事のまとめ
Terraformのdata sourceを使ったハードコード排除の設計ポイントをまとめます。| やりたいこと | 使うdata source |
|---|---|
| 最新AMIを動的取得する | data "aws_ami" { most_recent = true } |
| 既存VPCをタグで参照する | data "aws_vpc" { filter { name = "tag:Name" } } |
| 複数サブネットIDを一括取得する | data "aws_subnets" { filter { name = "vpc-id" } } |
| AWSアカウントIDを取得する | data "aws_caller_identity" "current" {} |
| 現在のリージョン名を取得する | data "aws_region" "current" {} |
| 利用可能なAZを全取得する | data "aws_availability_zones" { state = "available" } |
・TerraformのHCL変数設計|variable・locals・output・data sourceで構成を整理する方法
・Terraformのmoduleとfor_each・countで構成を再利用する設計パターン
>> Terraform実践セミナーの詳細はこちら
3,100名以上が実践した「型」を無料で公開中
プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。
姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら

無料メルマガで学習を続ける
Linuxの実践スキルをメールで毎週お届け。
登録は30秒、解除もいつでも可。
登録無料・いつでも解除できます