シナリオ
terraform plan を実行すると、即座に失敗します:
Error: no matching EC2 instances found
With the criteria passed, no matching EC2 instances were found. Please change your search criteria and try again.
インスタンスはAWSコンソール上に存在しています。目で確認できます。しかしTerraformには見えていません。深夜2時、デプロイはブロックされたままです。
実際に何が起きているか
Terraformのdata "aws_instance"、およびaws_ami、aws_subnet、aws_security_groupなどの類似データソースは、filterブロックをAWS APIコールに変換します。すべての条件を同時に満たすリソースがゼロだった場合、Terraformは強制停止します。フォールバックなし。警告なし。ただ止まるだけです。
このエラーを引き起こすブロックの例:
data "aws_instance" "app_server" {
filter {
name = "tag:Name"
values = ["my-app-server"]
}
filter {
name = "instance-state-name"
values = ["running"]
}
}
filterブロックはそれぞれAND条件として機能します。タグの大文字小文字の違い、停止中のインスタンス、リージョンの不一致など、filterが1つでも間違っていれば結果はゼロになります。
ステップ1:正確な条件でリソースの存在を確認する
Terraformに触れる前に、AWS CLIで同じクエリを再現してみましょう。Terraformが壊れているのか、それともインフラが想定と一致していないのかを即座に判断できます:
aws ec2 describe-instances \
--filters "Name=tag:Name,Values=my-app-server" \
"Name=instance-state-name,Values=running" \
--query "Reservations[*].Instances[*].{ID:InstanceId,State:State.Name,Tags:Tags}" \
--output table
何も返ってこない場合、Terraformの判断は正しいです——現時点でAWS上にあなたの条件に一致するリソースが存在しないということです。インスタンスの状態を確認してください:
aws ec2 describe-instances \
--filters "Name=tag:Name,Values=my-app-server" \
--query "Reservations[*].Instances[*].{ID:InstanceId,State:State.Name}" \
--output table
stoppedまたはterminatedと表示されている場合、それが原因です——instance-state-nameフィルターによって除外されています。インスタンスを起動するか、そのフィルターを削除してください。
ステップ2:リージョンを確認する
リージョンの不一致は、思いのほか多くの人が引っかかる問題です。インスタンスはus-west-2にあるのに、プロバイダーまたはシェル環境がus-east-1を向いている、といったケースです。結果は空で、役に立つエラーメッセージも出ません。
aws ec2 describe-instances \
--region us-west-2 \
--filters "Name=tag:Name,Values=my-app-server" \
--output table
Terraformのプロバイダーリージョンがリソースのリージョンと一致しているか確認してください:
provider "aws" {
region = "us-west-2" # must match the resource's actual region
}
デフォルトプロバイダーとは異なるリージョンのリソースを参照する場合は、エイリアスプロバイダーが必要です:
provider "aws" {
alias = "us_west"
region = "us-west-2"
}
data "aws_instance" "app_server" {
provider = aws.us_west
filter {
name = "tag:Name"
values = ["my-app-server"]
}
}
よくあるフィルターのミス
タグ値は大文字・小文字を区別する
AWSのタグ値は大文字・小文字を区別します。APIの観点では、my-app-serverとMy-App-Serverは別々のタグです。確実に一致させるには、実際の値をアカウントから取得してください:
aws ec2 describe-instances \
--query "Reservations[*].Instances[*].Tags[?Key=='Name'].Value" \
--output text | sort
出力から値を正確にコピーしてください。手で打ち直してはいけません。
タグのフィルター名の構文が間違っている
タグフィルターにはtag:プレフィックスが必須です——省略できません:
# WRONG — "Name" is an EC2 filter for resource names, not the tag
filter {
name = "Name"
values = ["my-server"]
}
# CORRECT
filter {
name = "tag:Name"
values = ["my-server"]
}
複数の値はOR、複数のブロックはAND
1つのfilterブロック内の複数の値はORになります。複数のfilterブロックはANDになります。この2つを混同すると、意図とは異なる動作をするフィルターが静かに生成されます:
# OR: finds instances named "server-a" OR "server-b"
filter {
name = "tag:Name"
values = ["server-a", "server-b"]
}
# AND: finds instances that have the right name AND are in us-west-2a
filter {
name = "tag:Name"
values = ["my-app-server"]
}
filter {
name = "availability-zone"
values = ["us-west-2a"]
}
AMI:結果が多すぎてもエラーになる
aws_amiデータソースは逆の問題が起きます。複数のAMIにマッチするフィルターもエラーをスローします。most_recent = trueで絞り込んでください:
data "aws_ami" "ubuntu" {
most_recent = true
owners = ["099720109477"]
filter {
name = "name"
values = ["ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"]
}
}
クイックフィックス:問題のあるフィルターを特定する
データソースが解決するまで、フィルターを1つずつコメントアウトしていきます。最も絞り込みの強いフィルターから始めてください:
data "aws_instance" "app_server" {
filter {
name = "tag:Name"
values = ["my-app-server"]
}
# Temporarily disabled to isolate the problem
# filter {
# name = "instance-state-name"
# values = ["running"]
# }
}
terraform planが正常に動くようになったら、フィルターを1つずつ戻していきます。再び壊れるものが原因です。
恒久的な修正:複数形のデータソースを使う
条件付きインフラ、ブルー・グリーンデプロイ、定期的に停止されるインスタンスなど——そういったパターンでは、単数形のデータソースは何度もエラーを起こし続けます。代わりにaws_instances(複数形)に切り替えましょう。リストが空でもエラーにはなりません:
data "aws_instances" "app_servers" {
filter {
name = "tag:Name"
values = ["my-app-server"]
}
filter {
name = "instance-state-name"
values = ["running"]
}
}
locals {
app_server_id = length(data.aws_instances.app_servers.ids) > 0 ? data.aws_instances.app_servers.ids[0] : null
}
terraform planはクリーンに動作します。nullの場合の処理は後続で明示的に行ってください——少なくともパイプラインはブロックされません。
必須のデータソース——共有VPC、ベースAMI、制限されたセキュリティグループ——にはコメントを残しておくべきです。深夜に壊れたとき、そのコメントが5分で解決できるかどうか、45分の調査になるかどうかの分かれ目になります:
data "aws_instance" "app_server" {
# Requires: EC2 instance tagged Name=my-app-server in running state.
# Owned by the infra-base workspace — start it there if this fails.
filter {
name = "tag:Name"
values = ["my-app-server"]
}
filter {
name = "instance-state-name"
values = ["running"]
}
}
修正の確認
フルプランを実行する前に、データソースだけを対象にして解決するか確認してください:
terraform plan -target='data.aws_instance.app_server'
取得された内容を確認するためのデバッグ出力を追加します:
output "debug_instance_id" {
value = data.aws_instance.app_server.id
}
適用後にterraform output debug_instance_idを実行してください。i-0abc123def456のような有効なインスタンスIDが表示されれば、データソースが正しく解決されていることを確認できます。コミット前にこのoutputは削除してください——不必要にインフラのIDをstateファイルに漏洩させることになります。

