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

Terraform実戦

状態を移し、消し、取り込む

TT Labで続きを見る

目標

すでに適用されたリソースのアドレスを安全に移し、管理の外にあったリソースを状態に取り込み、その結果、コードと状態が食い違っていないことを終了コードで証明します。

なぜ重要なのか

状態ファイルは、ツールの記憶です。コードは「何があるべきか」を、実体は「何があるか」を語り、状態は「最後に把握したもの」を語ります。リファクタリングは、この3つのうちコードだけを変える作業なので、状態を一緒に移さないと、ツールは、古いリソースが消え、新しいリソースができたと判断して、破棄と作成をプランします。そのため、名前の変更のような些細な作業が、本番では危険な作業になります。逆に、state rmは、名前とは違って実体を削除しません。帳簿からだけ消す、管理放棄の宣言であり、コードに宣言が残っていると、次のプランが、そのリソースを再び作ろうとします。この非対称を正確に知っていれば、状態手術は怖くありません。そして、すべての手術の前には、コピーを取ってください。元に戻せる作業と戻せない作業の違いは、そのファイル1つです。

ステップ

  1. /root/tf/surgery/main.tfに3つのリソースを宣言し、tofu initのあとに適用してください。local_file.legacy(ファイル/root/tf/surgery/files/legacy.txt)、local_file.notes(ファイル/root/tf/surgery/files/notes.txt)、random_pet.orphanです。そのあと、状態ファイルを/root/tf/surgery/backup/terraform.tfstate.bakにコピーしてください。コピーは正しいJSONで、lineageが現在の状態と同じで、リソースが3つ以上ある必要があります。
  2. tofu state listの結果を/root/tf/surgery/out/state-list.txtに(3行以上)、tofu state show local_file.legacyの結果を/root/tf/surgery/out/show-legacy.txtに保存してください。2つ目のファイルには、id属性の行が表示されている必要があります。
  3. tofu state mv local_file.legacy local_file.renamedで状態のアドレスを変更し、同じ作業の中で、コードのリソース名もrenamedに直してください。古いlocal_file.legacyブロックは、コメントアウトではなく完全に削除する必要があり、作業ディレクトリのどの.tfファイルにも残っていてはいけません。状態だけを移してコードをそのままにすると、次のプランが、古い名前のリソースを再び作ろうとします。tofu planの出力を/root/tf/surgery/out/plan-after-rename.txtに保存すると、No changesである必要があります。
  4. /root/tf/surgery/modules/archive/ディレクトリにモジュールを作成し、同じlocal_fileリソースをrenamedという名前で宣言してください(ファイルパス・内容などの引数の値が、移す前と完全に同一である必要があります)。ルートにmodule "archive"ブロックを追加してtofu initを実行したあと、tofu state mv local_file.renamed module.archive.local_file.renamedを実行し、ルートのmain.tfからは、そのリソースブロックを削除してください(ここでもコメントアウトではなく削除です。リソースの宣言は、今後はモジュールのファイルにだけあるべきです)。tofu planの出力を/root/tf/surgery/out/plan-after-module.txtに保存すると、No changesである必要があります。
  5. tofu state rm random_pet.orphanを実行したあと、tofu planの出力を/root/tf/surgery/out/plan-after-rm.txtに保存してください。プランには、random_pet.orphanを再び作成するという内容(1 to add)がある必要があります。確認できたら、main.tfからも、そのリソースブロックを削除し、/root/tf/surgery/out/rm-note.txtに、state rmとdestroyの違いを1行で書いてください。
  6. main.tfにresource "terraform_data" "adopted"を空の殻として宣言し、その上にimportブロックを置いて、to = terraform_data.adopted、id = "labhub-adopted"を指定したあと、適用してください。状態のterraform_data.adoptedのid属性が、labhub-adoptedである必要があります。
  7. main.tfにresource "terraform_data" "legacy_job"を宣言し、今回は、コマンドラインでtofu import terraform_data.legacy_job labhub-legacyを実行してください。出力を/root/tf/surgery/out/import.txtに保存すると、Import successfulという文言が表示されている必要があり、状態のterraform_data.legacy_jobのidは、labhub-legacyである必要があります。
  8. tofu plan -detailed-exitcodeを実行し、終了コードだけを/root/tf/surgery/out/final-exit.txtに書いてください(0である必要があります)。そして、/root/tf/surgery/out/inventory.jsonを作成して、addressesに現在の状態のすべてのアドレスを配列として入れ、backup_takenにtrueを入れてください。アドレスの数は、状態のインスタンス数とちょうど同じである必要があり、一覧には、module.archive.で始まるアドレスと、取り込んだadoptedのアドレスが含まれている必要があります。

参考

手術前の状態コピーを確保する

/root/tf/surgery/main.tfに3つのリソースを宣言し、tofu initのあとに適用してください。local_file.legacy(ファイル/root/tf/surgery/files/legacy.txt)、local_file.notes(ファイル/root/tf/surgery/files/notes.txt)、random_pet.orphanです。そのあと、状態ファイルを/root/tf/surgery/backup/terraform.tfstate.bakにコピーしてください。コピーは正しいJSONで、lineageが現在の状態と同じで、リソースが3つ以上ある必要があります。

状態に触れるすべての作業の前に、ファイルをコピーします。コピーが同じ状態のものかを確認するには、リネージの値を比べてください。

状態の一覧と個別リソースを覗く

tofu state listの結果を/root/tf/surgery/out/state-list.txtに(3行以上)、tofu state show local_file.legacyの結果を/root/tf/surgery/out/show-legacy.txtに保存してください。2つ目のファイルには、id属性の行が表示されている必要があります。

一覧のコマンドと、特定のアドレスの属性をまるごと表示するコマンドは、違います。後者には、id属性が表示されている必要があります。

state mvで名前だけ変える

tofu state mv local_file.legacy local_file.renamedで状態のアドレスを変更し、同じ作業の中で、コードのリソース名もrenamedに直してください。古いlocal_file.legacyブロックは、コメントアウトではなく完全に削除する必要があり、作業ディレクトリのどの.tfファイルにも残っていてはいけません。状態だけを移してコードをそのままにすると、次のプランが、古い名前のリソースを再び作ろうとします。tofu planの出力を/root/tf/surgery/out/plan-after-rename.txtに保存すると、No changesである必要があります。

コードと状態が同じ名前を指していてこそ、プランが空になります。状態だけを移すと、次のプランが古い名前で再び作ろうとし、コードだけを直すと、破棄と作成が並んで表示されます。古い名前のブロックは、コメントではなく削除です。

リソースをモジュールの中に移す

/root/tf/surgery/modules/archive/ディレクトリにモジュールを作成し、同じlocal_fileリソースをrenamedという名前で宣言してください(ファイルパス・内容などの引数の値が、移す前と完全に同一である必要があります)。ルートにmodule "archive"ブロックを追加してtofu initを実行したあと、tofu state mv local_file.renamed module.archive.local_file.renamedを実行し、ルートのmain.tfからは、そのリソースブロックを削除してください(ここでもコメントアウトではなく削除です。リソースの宣言は、今後はモジュールのファイルにだけあるべきです)。tofu planの出力を/root/tf/surgery/out/plan-after-module.txtに保存すると、No changesである必要があります。

モジュールブロックを先に作り、initを実行してから移します。モジュール内のリソースの引数の値が、移す前と完全に同じであってこそ、プランが空になります。

state rmが実体を削除しないことを証明する

tofu state rm random_pet.orphanを実行したあと、tofu planの出力を/root/tf/surgery/out/plan-after-rm.txtに保存してください。プランには、random_pet.orphanを再び作成するという内容(1 to add)がある必要があります。確認できたら、main.tfからも、そのリソースブロックを削除し、/root/tf/surgery/out/rm-note.txtに、state rmとdestroyの違いを1行で書いてください。

帳簿からだけ消すコマンドです。コードに宣言が残っていると、次のプランが何をしようとするかを見てください。

importブロックでリソースを取り込む

main.tfにresource "terraform_data" "adopted"を空の殻として宣言し、その上にimportブロックを置いて、to = terraform_data.adopted、id = "labhub-adopted"を指定したあと、適用してください。状態のterraform_data.adoptedのid属性が、labhub-adoptedである必要があります。

取り込む先(リソースブロック)を先に作る必要があります。ブロックには、どこへ(to)、何を(id)取り込むかを書きます。

コマンドラインのimportで取り込む

main.tfにresource "terraform_data" "legacy_job"を宣言し、今回は、コマンドラインでtofu import terraform_data.legacy_job labhub-legacyを実行してください。出力を/root/tf/surgery/out/import.txtに保存すると、Import successfulという文言が表示されている必要があり、状態のterraform_data.legacy_jobのidは、labhub-legacyである必要があります。

同じことを、すぐに実行する方式です。出力に成功の文言が残るので、ファイルに保存してください。

手術後の一致を終了コードで証明する

tofu plan -detailed-exitcodeを実行し、終了コードだけを/root/tf/surgery/out/final-exit.txtに書いてください(0である必要があります)。そして、/root/tf/surgery/out/inventory.jsonを作成して、addressesに現在の状態のすべてのアドレスを配列として入れ、backup_takenにtrueを入れてください。アドレスの数は、状態のインスタンス数とちょうど同じである必要があり、一覧には、module.archive.で始まるアドレスと、取り込んだadoptedのアドレスが含まれている必要があります。

人が目で読んで判定することは、自動化ではありません。プランの終了コードで判定し、状態のアドレスを1か所に集めて、数を合わせてみてください。