modalsoul’s blog

これは“失敗”と呼べるかもしれないが、ぼくは“学習体験”と呼びたい

aws_cloudwatch_event_ruleのstateでデフォルト値が反映されないパターン

tl;dr

  • aws_cloudwatch_event_ruleのstate引数を"DISABLED"にした後、設定から削除してもルールは"DISABLED"のまま
  • 設定を削除した場合、管理が放棄されるのみでデフォルト値は適用されない

事象

aws_cloudwatch_event_ruleリソースでCloudWatch Event Ruleを管理する際、以下の状況に遭遇した。

  1. 初期設定で、ルールの状態を明示的に無効化するため、state = "DISABLED"と記述し、terraform applyを実行した。

    resource "aws_cloudwatch_event_rule" "example" {
      name        = "my-event-rule"
      description = "My example event rule"
      event_pattern = jsonencode({
        source = ["aws.s3"]
      })
      state = "DISABLED" # ここでDISABLEDに設定
    }
    
  2. state引数のデフォルト値は"ENABLED"であるため、ルールを有効化する意図でstate引数を削除した。

    resource "aws_cloudwatch_event_rule" "example" {
      name        = "my-event-rule"
      description = "My example event rule"
      event_pattern = jsonencode({
        source = ["aws.s3"]
      })
      // state引数を削除
    }
    
  3. この状態でterraform applyを実行したが、ルールは無効("DISABLED")のままだった。


原因

Terraformが一度管理した引数を設定ファイルから削除した場合の挙動にある。

  • デフォルト値の適用タイミング: Terraformの引数におけるデフォルト値は、その引数を設定ファイルに明示的に記述しなかった場合にのみ適用される。
  • 管理の放棄: 一度state = "DISABLED"と明示的に記述してterraform applyを実行すると、Terraformはaws_cloudwatch_event_rulestate属性を管理対象として認識する。その後、設定ファイルからstate引数を削除した場合、Terraformは「もうこのstate属性はTerraformの設定で管理しない」と判断し、その属性に対する変更操作を行わない。
  • 既存状態の維持: 結果として、AWS上に存在するCloudWatch Event Ruleのstateは、以前に"DISABLED"に設定された状態のまま維持される。Terraformは、設定ファイルに記述されていない属性については、AWS上の既存の状態を変更しようとしないため、デフォルト値に戻ることはない。

これは、Terraformが「設定ファイルに記述された状態」にAWSリソースを収束させようとする性質から生じる挙動だ。設定ファイルに存在しないものは、Terraformの管理外となるため、既存の状態が温存される。


解決方法

state引数のデフォルト値が"ENABLED"であっても、DISABLEDからENABLEDへ戻したい場合は、明示的にstate = "ENABLED"と記述し、terraform applyを実行する必要があった。

resource "aws_cloudwatch_event_rule" "example" {
  name        = "my-event-rule"
  description = "My example event rule"
  event_pattern = jsonencode({
    source = ["aws.s3"]
  })
  state = "ENABLED" # 明示的にENABLEDに設定
}

詳解

Terraformのでリソースを定義する際、Optionalな引数(記述しなくてもよい引数)やComputedな引数(Terraformの実行中にAWSから値が返される引数)の扱いに注意が必要。

aws_cloudwatch_event_ruleにおけるstate引数では、

  • 定義時: ドキュメントの記載上、stateOptionalであり、デフォルト値は"ENABLED"
    • 初回デプロイでstateを省略した場合 → ENABLED
    • 初回デプロイでstate = "DISABLED"と記述した場合 → DISABLED
  • 更新時: 一度Terraform Stateファイルにstate = "DISABLED"という情報が記録されると、Terraformは次回以降のterraform planterraform applyで、この値を管理対象として比較する。
    • 設定ファイルからstateを削除 Terraformは設定ファイルにstateの記述がないため、stateに関する変更は「計画」されない。Terraform Stateファイルに記録されているstateの値と、AWS上の実際のstateの値が合致しているため、「変更なし」と判断される。結果として、AWS上のルールはDISABLEDのまま。
    • 設定ファイルをstate = "ENABLED"に変更 Terraformは設定ファイル上のstate = "ENABLED"と、Terraform Stateファイルに記録されているstate = "DISABLED"を比較し、差分を検出する。この差分を解消するため、AWS上のルールをENABLEDに更新する「計画」を立て、apply時に実行する。

Terraformは「最終的にどういう状態になってほしいか」を宣言するツールであり、その宣言から外れたものは管理対象外となる。デフォルト値は「宣言を省略した場合の便宜的な値」であり、一度明示的に宣言したものを後で削除しても、Terraformは「以前の宣言状態に戻れ」とは指示しない。

「デフォルト値」と「管理対象からの離脱」の概念の理解が浅かった。