Terraformの「Missing resource instance key」エラーをcountリソースをインデックスなしで参照した場合に修正する

beginner🏗️ Terraform2026-07-22| Terraform >= 0.12、任意のクラウドプロバイダー(AWS / GCP / Azure)、macOS / Linux / Windows

Error Message

Error: Missing resource instance key: Because aws_instance.web has "count" set, its attributes must be accessed on specific instances.
#terraform#count#resource-reference#hcl#index

TL;DR

count リソースを単一オブジェクトとして扱っています。これはリストです。インデックスを追加してください: 最初のインスタンスには aws_instance.web[0]、別のカウントリソース内では aws_instance.web[count.index]、すべてのIDを一度に収集するには aws_instance.web[*].id を使います。

このエラーが発生する原因

リソースブロックに count を追加した瞬間、Terraformはそれを順序付きリストに変換します — count = 1 の場合でも同様です。そのリソースへのすべての参照で、どのインスタンスを指すかを指定する必要があります。インデックスを省略すると次のエラーが発生します:

Error: Missing resource instance key: Because aws_instance.web has "count" set, its attributes must be accessed on specific instances.

このエラーを引き起こす設定例:

resource "aws_instance" "web" {
  count         = 3
  ami           = "ami-0c55b159cbfafe1f0"
  instance_type = "t3.micro"
}

output "web_ip" {
  value = aws_instance.web.private_ip  # ← エラー: インデックスが欠落
}

修正方法

1. 特定のインデックスを参照する

どのインスタンスが必要かわかっている場合は、0始まりのインデックスで指定します:

output "first_web_ip" {
  value = aws_instance.web[0].private_ip
}

インデックスは0から始まります — 2番目のインスタンスは [1]、3番目は [2] です。ほとんどのケースで最もシンプルな修正方法です。

2. スプラット式を使ってすべての値を取得する

すべてのインスタンスから同じ属性が必要な場合は、スプラット演算子 [*] を使うと一度にリストで返されます:

output "all_web_ips" {
  value = aws_instance.web[*].private_ip
}

アウトプット、ローカル変数、リストが有効な場所であればどこでも使えます。ただし注意点があります: 引数が単一の文字列を期待する場所では使わないでください — 型の不一致エラーが発生します。

3. 別リソース内での参照 — count.index を使う

両方のリソースが count を使っている場合は、count.index で1対1にマッピングします:

resource "aws_eip" "web" {
  count    = 3
  instance = aws_instance.web[count.index].id
}

各EIPは同じ位置のwebインスタンスとペアになります — EIP 0 → web[0]、EIP 1 → web[1]、という具合です。

4. count から for_each に切り替える(複雑な場合に推奨)

インデックスの管理が煩雑になってきた場合は、for_each に切り替えるサインです。数値インデックスの代わりに文字列キーを使います — 読みやすく、リストの途中でアイテムが追加・削除されても安全です:

variable "web_names" {
  default = ["web-a", "web-b", "web-c"]
}

resource "aws_instance" "web" {
  for_each      = toset(var.web_names)
  ami           = "ami-0c55b159cbfafe1f0"
  instance_type = "t3.micro"

  tags = {
    Name = each.key
  }
}

output "web_ips" {
  value = { for k, v in aws_instance.web : k => v.private_ip }
}

通常の count では、"web-b" を削除するとそれ以降のインデックスがすべてずれます — Terraformは "web-c" が変更されたと判断し、削除と再作成を計画します。for_each では各インスタンスが安定した文字列キーを持つため、"web-b" を削除しても "web-b" だけに影響します。

エッジケース: count = 1 でもインデックスが必要

これは多くの人が気づかないポイントです。count = 1 でもリソースはリストになります:

resource "aws_instance" "bastion" {
  count         = 1
  ami           = "ami-0c55b159cbfafe1f0"
  instance_type = "t3.micro"
}

# これはエラーになります:
resource "aws_eip" "bastion" {
  instance = aws_instance.bastion.id   # ERROR
}

# 正しい書き方:
resource "aws_eip" "bastion" {
  instance = aws_instance.bastion[0].id
}

よく発生する箇所

  • アウトプット — インデックスなしで resource.name.attribute を直接参照している場合
  • 依存リソースの引数 — カウントリソースに依存するリソースにIDやARNを渡す場合
  • ローカル変数 — カウントリソースの属性から派生値を計算する場合
  • モジュールの入力 — リストではなく文字列を期待する変数に単一の値を渡す場合

修正の確認方法

すべてが正しく設定されているか確認するための2つのコマンドです。まず構文を検証します:

terraform validate
Success! The configuration is valid.

次にプランを実行します。アウトプットブロックや依存リソースには、エラーなしで実際の値 — IPアドレス、ID、ARN — が表示されるはずです:

terraform plan

予期せず置き換えとしてマークされているものがあれば、参照しているインデックスを再確認してください。

クイックリファレンスチートシート

  • resource.name[0] — 最初のインスタンス
  • resource.name[count.index] — 現在のインデックス(別のカウントリソース内で使用)
  • resource.name[*].attr — すべてのインスタンス(リストを返す)
  • resource.name[length(resource.name) - 1] — 最後のインスタンス
  • { for k, v in resource.name : k => v.attr }for_each リソースをマップ処理する

Related Error Notes