TT Lab
はじめる
学ぶ 学習パス コース

Terraform/OpenTofu基礎

作るブロックと読むブロック

TT Labで続きを見る

一言でいうと

resourceは対象を所有し、dataは対象を読み取るだけです。この1文字の違いが、状態の形・プランの時点・破壊の範囲をすべて分けます。

なぜこの区別が必要だったのか

初期の設定言語には、resourceしかありませんでした。ところが、現実のインフラは、1つのチームがすべてを作るわけではありません。ネットワークはインフラチームがすでに作ってあり、証明書はセキュリティチームが発行し、アカウント番号は会社に1つしかありません。こうしたものを使うには値が必要ですが、値を得ようとしてresourceとして書くと、ツールはそれを自分の所有物と誤解します。所有していると信じた瞬間、destroyは他人のネットワークを削除しようとし、ドリフト検知は、他のチームが変更したものを元に戻そうとします。

そこで、「読み取りだけを行うブロック」が別にできました。dataブロックは、対象を作りも、変更も、削除もしません。状態に項目はできますが、それは所有の証書ではなく、最後に読んだ値のコピーです。そのため、destroyを回しても元のものは残り、プランには、createやdestroyではなく、readだけが出ます。

どう動くのか

状態ファイルを開くと、項目ごとにmodeがあります。値はmanagedかdataの2つです。tofu state listがアドレスを出力するときに、data.という接頭辞が付くかどうかも、ここで分かれます。

tofu state list
# data.local_file.seed      ← mode = "data"
# local_file.copy           ← mode = "managed"

読み取りがいつ起きるかが、2つ目の分かれ道です。デフォルトは、プランの段階です。引数がすべて既知の値なら、ツールは、プランを立てながら読み取り、読み取った値を、プランにそのまま埋め込みます。そのため、レビュアーは、プランを見るだけで、何が入るかがわかります。

読み取りが適用時点に先送りされる場合は、2つあります。引数が、まだ作られていないリソースの属性に依存するときと、データソースにdepends_onが付いているときです。後者は、元のものがすでにディスクにあっても先送りされます。依存が終わってから読むという宣言だからです。このとき、プランには次のように出ます。

  # data.local_file.back will be read during apply
      + content = (known after apply)

値が不明になると、その値を使う下流のリソースの属性も、すべて不明になります。プランのレビューの価値が、その分だけ下がるので、データソースにdepends_onを付けるときは、本当に必要かどうかを、もう一度考える必要があります。

3つ目の分かれ道は、もう一度読むという点です。managedリソースは、状態に書かれた値と実物を比べて、ドリフトを捕まえますが、データソースは、比べるものがありません。プランのたびに新しく読んで、その値を下流に流します。元のものが変われば、「状態が食い違った」ではなく「入力が変わった」として捕捉され、その値が再作成を呼ぶ属性なら、下流が丸ごと置き換えられます。

読み取れないときの失敗も、時点が違います。ファイルがなければ、適用まで進めず、プランがその場で止まります。

Error: Read local file data source error
+Original Error: open ./absent.txt: no such file or directory

名前が紛らわしい兄弟が1つあります。terraform_dataは、dataブロックではなく、組み込みプロバイダーが提供するmanagedリソースです。状態のmodeはmanagedで、値を変えるとプランにupdateやreplaceが出て、destroyすると消えます。名前のせいで、データソースとして読む人が多いのですが、このブロックの用途は、値を読むことではなく、「変わったら何かをやり直させる目印」を、状態に残すことです。

現場での姿

最もよくある事故は、他人が作ったものをresourceとして書くことです。すでにある対象をresourceとして宣言すると、ツールはそれを新しく作ろうとし、名前が重なれば適用が失敗し、名前が重ならなければ、まったく同じものがもう1つできます。後者のほうが悪いです。誰も失敗を見ないまま、重複がたまります。読み取るだけでよいものは、dataとして書き、すでにあるものを本当に所有する必要があるなら、importで取り込みます。

2つ目によくあるのは、プランが不明だらけになることです。レビュー文化が根付いているチームほど、「プランに値がすべて見えなければ承認しない」というルールを置きますが、データソース1つにdepends_onを付けたせいで、承認できないプランが出ます。このようなときは、依存を、データソースではなく、その値を使うリソースの側に移すほうがよいです。

3つ目は、逆方向です。元のものが静かに変わって、下流が置き換えられる場合です。設定ファイルは1文字も変わっていないのに、プランに置き換えが出ます。原因をコードで探すと、永遠に見つかりません。データソースが読む元が何なのか、それを誰が変更できるのかを、先に見ます。

次のラボですること

PodのOpenTofuと、local・random・nullプロバイダーで、8つのステップを進めます。ファイル1つをdataとして読んでコピーし、状態でmodeで2つの項目を分け、destroyのあとに元のものが残ることを確認します。続けて、名前が適用の時点で決まるファイルを読んで、プランがどう変わるか、すでにあるファイルにdepends_onを付けると何が先送りされるか、元のものがないときにプランがどこで止まるかを見ます。最後の2つのステップでは、terraform_dataのmodeを、データソースと並べて比較し、元のものを変えて下流が置き換えられるところまで確認します。