💡

Terraform: count から for_each へ書き換える

2024/12/24に公開

課題

複数のリソースをリストで定義し、各リソースを count ループで作成した場合、各リソースがインデックス番号で紐付けられます。インデックス番号が変わると既存のリソースは一旦削除されてしまうため、途中の要素を削除したり挿入することができません。

例)

locals {
  repositories = [
    "test1",
    "test2",
    "test3",
  ]
}

resource "aws_ecr_repository" "ecr" {
  count = length(local.repositories)
  name  = local.repositories[count.index]
}

対策

count の代わりに for_each を使うことで、各リソースは文字列により紐付けられます。

resource "aws_ecr_repository" "ecr" {
  for_each = { for repo in local.repositories : repo => repo }
  name     = each.value
}

既存リソースの移行

すでに count を使ってデプロイしていた場合、リソースのキーがインデックス番号から文字列に変わるため、そのまま apply するとすべて作り直されてしまいます。これを避けるため、紐付けを修正します。

  1. リソースの一覧を確認

    $ terraform state list
    aws_ecr_repository.ecr[0]
    aws_ecr_repository.ecr[1]
    aws_ecr_repository.ecr[2]
    
  2. リソースのキーを変更してデプロイ

    $ terraform state mv 'aws_ecr_repository.ecr[0]' 'aws_ecr_repository.ecr["test1"]'
    Move "aws_ecr_repository.ecr[0]" to "aws_ecr_repository.ecr[\"test1\"]"
    Successfully moved 1 object(s).
    $ terraform state mv 'aws_ecr_repository.ecr[1]' 'aws_ecr_repository.ecr["test2"]'
    Move "aws_ecr_repository.ecr[1]" to "aws_ecr_repository.ecr[\"test2\"]"
    Successfully moved 1 object(s).
    $ terraform state mv 'aws_ecr_repository.ecr[2]' 'aws_ecr_repository.ecr["test3"]'
    Move "aws_ecr_repository.ecr[2]" to "aws_ecr_repository.ecr[\"test3\"]"
    Successfully moved 1 object(s).
    

for_each に書き換えた定義で、差分が発生していなければ成功です。

$ terraform plan
()

No changes. Your infrastructure matches the configuration.

Terraform has compared your real infrastructure against your configuration and found no differences, so no changes are needed.

Discussion