Terraformエラー修正:「count」と「for_each」メタ引数は相互排他的です

beginner🏗️ Terraform2026-05-11| Terraform 0.13以降、任意のOS(Linux、macOS、Windows)、任意のクラウドプロバイダー

Error Message

The "count" and "for_each" meta-arguments are mutually exclusive.
#terraform#count#for_each#メタ引数

エラーの内容

terraform plan または terraform apply を実行すると、すぐにこのエラーが発生します:

│ Error: Invalid combination of "count" and "for_each"
│
│   on main.tf line 3, in resource "aws_instance" "web":
│    3: resource "aws_instance" "web" {
│
│ The "count" and "for_each" meta-arguments are mutually exclusive.

リソースブロック(またはモジュールブロック)に countfor_each が同時に定義されています。Terraformはどちらのイテレーション戦略を使うか判断できないため、その場で処理を停止します。

なぜこのエラーが起きるのか

countfor_each はどちらもリソースの複数インスタンスを作成するよう Terraform に指示しますが、動作の仕組みが根本的に異なります:

  • count — N個のコピーを作成し、インデックスで参照する(resource[0]resource[1]、…)
  • for_each — マップのキーまたはセットの要素ごとに1つのコピーを作成し、キーで参照する(resource["web"]resource["db"]、…)

同じブロックに両方を指定するのは矛盾しています。シェフに「ステーキをミディアムレアかつウェルダンで焼いて」と頼むようなもので、どちらか一方しか選べません。

このエラーが発生しやすい3つのケース:

  • count ベースのコードを for_each に移行するとき、元の count 行を削除し忘れる
  • count を使っている別プロジェクトからリソースブロックをコピーして、クリーンアップせずに for_each を追加してしまう
  • 異なるソースから両方の引数を受け取るモジュール呼び出し

修正手順

ステップ1:問題のあるブロックを見つける

Terraformのエラーには正確なファイルと行番号が含まれています。そのファイルを開き、両方の引数が並んでいるブロックを探します:

resource "aws_instance" "web" {
  count    = 3                          # ← どちらか一方を削除する
  for_each = toset(["a", "b", "c"])    # ← どちらか一方だけ残す

  ami           = "ami-0c55b159cbfafe1f0"
  instance_type = "t3.micro"
}

ステップ2:適切なメタ引数を選択する

状況に合った方を選びます。

**count を使うケース:**N個の同一(またはほぼ同一)のコピーが必要で、キーが重要でない場合:

resource "aws_instance" "web" {
  count = 3

  ami           = "ami-0c55b159cbfafe1f0"
  instance_type = "t3.micro"

  tags = {
    Name = "web-${count.index}"
  }
}

**for_each を使うケース:**各インスタンスが web、api、db のように個別の役割やアイデンティティを持つ場合、または後から名前で参照する必要がある場合:

locals {
  instances = {
    web = "t3.micro"
    api = "t3.small"
    db  = "t3.medium"
  }
}

resource "aws_instance" "servers" {
  for_each = local.instances

  ami           = "ami-0c55b159cbfafe1f0"
  instance_type = each.value

  tags = {
    Name = each.key
  }
}

特定のインスタンスを後から参照する際もシンプルで読みやすいです:

output "web_ip" {
  value = aws_instance.servers["web"].public_ip
}

ステップ3:不要なメタ引数を削除する

使わない方を削除します。ブロック全体を注意深くスキャンしてください — 末尾に隠れている余分な count = ...for_each = ... があるだけでエラーが発生します。各リソースにはどちらか一方のみ(単一インスタンスだけ必要な場合はどちらも不要)を指定してください。

モジュールブロックも同じルールに従う

モジュール呼び出しも同様の問題が発生します:

# 壊れている状態
module "vpc" {
  source   = "./modules/vpc"
  count    = 2
  for_each = toset(["us-east-1", "eu-west-1"])
}

# 修正済み — どちらか一方を選ぶ:
module "vpc" {
  source   = "./modules/vpc"
  for_each = toset(["us-east-1", "eu-west-1"])

  region = each.value
}

修正の確認

まず terraform validate を実行します — フルプランより高速で、構文の問題をすぐに検出できます:

terraform validate

次のように表示されれば成功です:

Success! The configuration is valid.

続いてプランが正しいことを確認します:

terraform plan

注意点として、既存のリソースを count から for_each に切り替える場合、Terraformはすべてのインスタンスを破棄して再作成するプランを立てます。ステートキーの形式が resource[0] から resource["key"] に変わり、Terraformはそれらを別々のオブジェクトとして扱うためです。再作成を避けたい場合は、terraform state mv を使って手動でステートを移行してください。

クイックヒント

  • 実際のインフラではほとんどの場合 for_each を優先してください。count ベースのリストの途中からアイテムを削除すると、Terraformはそれ以降のすべてのインデックスをシフトさせ、触れていないリソースまで破棄・再作成する可能性があります。for_each では削除したキーのみが消えます。
  • 空のマップやセットは問題ありませんfor_each = {} はエラーなしでリソースをゼロ個作成します。
  • プレーンなリストを for_each に渡すことはできませんtoset() でラップしてください:for_each = toset(var.names)。リストは重複を許可しますが、for_each は許可しません。
  • ダイナミックブロックは別の話です — すでに count を使っているリソース内の dynamic ブロックで for_each を使うことは完全に有効です。それはTerraformが問題にしている競合ではありません。

Related Error Notes