Terraformのcreate/update/destroy適用順序を理解して複雑なIaCに備える

#terraform

はじめに

Terraform で IaC を進めると、複数のリソースが互いに依存する状況が生まれる。また運用期間が長くなるにつれ、リソースの create けでなく複数リソースの update や destroy も必要なケースが出てくる。

ここでありがちなのが「あるリソース A を削除する前にリソース B の内容を更新したい」といったケースだ。B が持っている配列に A の ID 等が含まれていて、先に配列から ID を消さないと destroy 時に API がエラーを返すというのがよくあるパターン。

これを解決するには apply の適用順序を理解しコントロールする必要があるが、あまり直感的でなく Web 上の情報も乏しいように感じたのでここにまとめる。

基本のおさらい

まずはシンプルな構成例でリソース間依存と適用順序の関係を整理する。

create や destroy の適用順序

これは依存関係から順序が決まるのでとても分かりやすい。以下の登場人物(resource)を例にする。

  • 会社の中のチームを表す team resource
  • 社員とその情報(社員番号、所属先チーム等)を表す team_member resource

リソースの定義は以下。

resource team "dev" { team_name = "dev" } resource team_membership "john_doe" { employee_id = 1234 # devチームに所属 team_id = team.dev.id }

依存関係を表すグラフと適用順序を図示すると以下のようになる。

依存グラフとcreate・destroyの関係
依存グラフとcreate・destroyの関係

適用のルールは極めてシンプルで、

  • resource の create は依存グラフのエッジから順に適用される
  • destroy は create の逆順

となる。

depends_on は基本的に書かなくていい

上記のサンプルコードのようにリソース内で他のリソースを参照している場合 Terraform は暗黙的に依存を認識し depends_on を書いているのと同じ作用が働く。そのため基本的に resource 定義で depends_on 明示的に書かないといけないケースは少ない。

Most resource dependencies are handled automatically. Terraform analyses any expressions within a resource block to find references to other objects, and treats those references as implicit ordering requirements when creating, updating, or destroying resources. https://developer.hashicorp.com/terraform/language/resources/behavior#resource-dependencies

たとえば destroy の適用順序でエラーが起きた場合、もし闇雲に depends_on を書いてしまっている箇所があれば一度依存グラフを図に起こして精査すれば分かりやすいはず。

多くはないはずだが以下のような例だと depends_on が効果的になる。

  • 前述のチームと所属メンバーを表す resource 群に、チームメンバーの稼働表 work_schedule があるとする
  • work_schedule にはメンバーの社員番号が含まれる
  • API の仕様上、稼働表に存在するメンバーをチームからは外せない (= team_membership を destroy できない)
resource work_schedule "schedule" { team_id = team.team_foo.id # team_membership との参照関係は無い employee_members = [1234, 12345, ...] }

もしチームが解散となった場合この仕様を満たすには work_schedule -> team_membership -> team の順で destroy しなければいけないが、 work_schedule と team_membership の間には暗黙的な依存関係がない。なので depends_on で以下のように依存グラフを組み替えて destroy の順序を変える必要がある。

depends_onの有無による依存グラフの変化
depends_onの有無による依存グラフの変化

destroy と create・update の順序を制御するには create_before_destroy

ここまでは resource 間依存と apply 順の関係性を整理するために create・destroy どちらかだけが走る例を挙げた。では冒頭に書いたような update や destroy が両方走る apply ではどう考えればいいか。

チーム・所属メンバー・勤怠表 3 つのリソースを例に、チームメンバーが 1 人抜ける場合を考えると次の順序で apply する必要がある。

  • work_schedule から抜けるメンバーの参照を消して update する
  • 該当メンバーの team_membership を destroy する

しかし Terraform では仕様上 create -> update -> destroy の順番で apply が適用されるという挙動になっている (参考: HashiCorp Discuss)。 depends_on はあくまで依存関係の制御、つまり create・destroy それぞれの順序制御とも言えるので、update と destroy 間の順序には関係ない。このような時には create_before_destroy lifecycle の出番になる。

Currently, the only user-controllable method for changing the ordering of create and destroy operations is with the create_before_destroy resource lifecycle attribute. https://github.com/hashicorp/terraform/blob/main/docs/destroying.md#create-before-destroy

依存グラフと create_before_destroy 無し・有りでの apply 順序は以下になる。名前からは create の為のものに思えるが update も同様で、destroy より前に適用させたい resource に lifecycle を指定すればいい。

create_before_destroyの有無による適用順の変化
create_before_destroyの有無による適用順の変化

作成済み resource の lifecycle 変更は state refresh が必要

やや余談だが、lifecycle に指定した値は resource を最初に作成した時だけ state へ書き込まれる。つまり作成済みの resource で適用順序起因の問題を見つけて create_before_destroy を追記し apply しても効果がない。

lifecycle も含めた既存 resource の変更を反映したい場合は terraform apply -refresh-only を実行する。このオプションを指定すると apply 時の差分がなくとも state を最新の状態にさせられるので、適用順序に起因してエラーが起きる修正を入れる前にこれを実行しておくといい。

まとめ

管理するリソースが増えた際に重要となる依存関係や適用順序のルールをこの記事で整理した。

この適用順序のルールについては一部 HashiCorp のドキュメントサイトに載ってないものがあったので、参考にした情報源も最後に貼っておく。 下記 1 つ目の GitHub repo 上の doc がまさにそれで、create_before_destroy についても詳しく書かれている。

Tweet