vague memory

うろ覚えを無くしていこうともがき苦しむ人の備忘録

Terraform "count = 1" の追加/削除は暗黙的に move される

Terraformのコードをリファクタリングしている際に気付いた点です。 moved ブロックを記述していないのに move の動作となったので不思議に思いました。

あまり遭遇しないと思いますし、 便利なので利用しましょうというより、 仕様なので意図しない出力があっても落ち着きましょうという内容です。


公式ドキュメント

Refactoring | Terraform | HashiCorp Developer

Note: When you add count to an existing resource that didn't use it, Terraform automatically proposes to move the original object to instance zero, unless you write an moved block explicitly mentioning that resource. However, we recommend still writing out the corresponding moved block explicitly, to make the change clearer to future readers of the module.

"moved ブロックが明示的に指定されていない場合、count を追加すると インスタンス0に移動するよう提案する" とあります。

変更を明確にするため、movedブロックを明示的に記述することを推奨 ともありますので、基本的には明示しましょう。

moved ブロックについては公式ドキュメント参照。 Use Configuration to Move Resources | Terraform | HashiCorp Developer

どういうことか

Terraform で変数によりリソース作成有無を切り替える手法として以下の様に記述することがあります。

resource foo bar {
  count = condition ? 1 : 0
  etc
}

この count = の部分を追加・削除した際に、 リソース削除・追加(destroyed/created) ではなく、moved の動作になるという話です。

検証

  • 事前作成リソース
# 既存リソースに count を追加する例
resource "terraform_data" "this" {
  provisioner "local-exec" {
    command = "echo this."
  }
}

# 既存リソースから count を削除する例
variable "create" {
  type    = bool
  default = true
}
resource "terraform_data" "this_count" {
  count = var.create ? 1 : 0
  provisioner "local-exec" {
    command = "echo this count."
  }
}

apply します。

 % terraform apply
terraform_data.this: Refreshing state... [id=6e9d88ea-41ac-169e-2626-9cb4af26c2ea]
terraform_data.this_count[0]: Refreshing state... [id=bde4e6a0-d339-1a63-e41a-120ed59d30e9]

以下の変更を加えます。変更箇所は count の部分です。

# 既存リソースに count を追加する例
resource "terraform_data" "this" {
  count = 1  # count を追加する
  provisioner "local-exec" {
    command = "echo this."
  }
}

# 既存リソースから count を削除する例
variable "create" {
  type    = bool
  default = true
}
resource "terraform_data" "this_count" {
  #count = var.create ? 1 : 0  # count 削除する
  provisioner "local-exec" {
    command = "echo this count."
  }
}

この状態で plan を叩くと、 move の動作になります。

Terraform will perform the following actions:

  # terraform_data.this has moved to terraform_data.this[0]
    resource "terraform_data" "this" {
        id = "6e9d88ea-41ac-169e-2626-9cb4af26c2ea"
    }

  # terraform_data.this_count[0] has moved to terraform_data.this_count
    resource "terraform_data" "this_count" {
        id = "bde4e6a0-d339-1a63-e41a-120ed59d30e9"
    }

Plan: 0 to add, 0 to change, 0 to destroy.

moved を明示指定する場合でも同様の動作になります。

moved {
  from = terraform_data.this
  to   = terraform_data.this[0]
}

moved {
  from = terraform_data.this_count[0]
  to   = terraform_data.this_count
}

検証(2)

count に2以上の値が指定されている場合、 [0] のみ前述の動作 になります。
混乱の元なのでやはり moved を明示指定すべきだと思います。

Terraform will perform the following actions:

  # terraform_data.this2 has moved to terraform_data.this2[0]
    resource "terraform_data" "this2" {
        id = "fd03ec07-12cf-2347-fe0b-c1aa6b088f0c"
    }

  # terraform_data.this2[1] will be created
  + resource "terraform_data" "this2" {
      + id = (known after apply)
    }

  # terraform_data.this2_count[0] has moved to terraform_data.this2_count
    resource "terraform_data" "this2_count" {
        id = "d44f89cb-442c-501f-ca87-192b44dcc4a6"
    }

  # terraform_data.this2_count[1] will be destroyed
  # (because resource does not use count)
  - resource "terraform_data" "this2_count" {
      - id = "567b7f34-47e4-e164-2442-1a808845b0d7" -> null
    }

Plan: 1 to add, 0 to change, 1 to destroy.

検証(3)

module に対する count では効きません。

Terraform will perform the following actions:

  # module.example.terraform_data.this will be destroyed
  # (because module.example is not in configuration)
  - resource "terraform_data" "this" {
      - id = "8dada03a-1f12-93b1-84c9-f516e94335c4" -> null
    }

  # module.example[0].terraform_data.this will be created
  + resource "terraform_data" "this" {
      + id = (known after apply)
    }

  # module.example_count.terraform_data.this will be created
  + resource "terraform_data" "this" {
      + id = (known after apply)
    }

  # module.example_count[0].terraform_data.this will be destroyed
  # (because module.example_count[0] is not in configuration)
  - resource "terraform_data" "this" {
      - id = "8a6bdeb1-9c26-7b40-b578-869ea6da4a5e" -> null
    }

Plan: 2 to add, 0 to change, 2 to destroy.

Issue が挙がっているので、将来的に挙動が変わるかもしれません。

おまけ

(0,1 のために count を使用する用途とは違いますが念の為、 ) count を利用する際によく併用される count.index が定義に含まれている場合は既存通りの動作です。

count を削除するので count.index は Error になります。

Error: Reference to "count" in non-counted context