Refactoring Terraform with moved blocks

2021、年の暮れからこんにちは。
サービス基盤本部インフラ部の @grezarjp です。

2021年も終わろうとしているこのときに、嬉しいニュースがあります。Terraformのrefactoringがかなりやりやすくなりました。今日はTerraform v1.1.0から導入されたmoved blocksという機能によってTerraformのrefactoringがどのように楽になったのか、その機能とともに紹介したいと思います。

v1.1.0で導入されたmoved blocksについて

まずはTerraform v1.1.0のリリースノートをご覧ください

https://github.com/hashicorp/terraform/releases/tag/v1.1.0

moved blocks for refactoring within modules: Module authors can now record in module source code whenever they’ve changed the address of a resource or resource instance, and then during planning Terraform will automatically migrate existing objects in the state to new addresses.

moved blcoksが導入され、module内でplan時点でのstateの移動が可能になったとあります。

これまでもTerraformではresource名やmodule名などを変えるようなrefactoringを伴うときに、リソースを再作成しないようにするために terraform state mv によってコードを変更後にstateの移行を行うことができました。しかし、terraform state mv にはいくつかの問題点があります。

  1. CLIベースの操作で、宣言的に記述できない
  2. Refactoring後にPlan時点で差分がないことを確認したいが、その場合mainのブランチにマージする前に terraform state mv する必要がある

1についてはCLIベースで手続き的にstateの移行操作を行うしかなく、変更量が多いときには作業量が多くなりミスも発生しやすくなります。

2はもっと大きな問題です。普通Terraformのコードをrefactoringしたときはrefactoring前後でTerraform Planの結果に差分がないことを確認してrefactoringが安全に行えているのかどうかを判断するはずです。ただし、これを達成するためにはmainブランチにマージする前の状態で terraform state mv を行い、stateを破壊的に操作する必要があります。これは安全な状態であることを担保するために安全でない操作をするというある種の矛盾を伴うものでした。

moved blocksがもたらしたもの

基本的な使い方は こちらにまとまっています。moved blocksの構文はシンプルです。変更前のobject名と変更後のobject名をそれぞれfromとtoに対応させて書きます。

moved {
  from = aws_instance.a
  to   = aws_instance.b
}

複数のresourceを変更している場合にはその数だけmoved blocksを書くことになります。moved blocksが定義されている場合、TerraformはPlan実行前に

  1. fromで指定されたobjectがstate内にあるか確認する
  2. もし1でstate内に既存のobjectが確認できればTerraformはそのobjectをtoで指定されている名前にrenameして扱う

という処理をはさみます。2の操作は内部的なものでstateへの実際の変更はapply時に反映されるためplan時点では安全な操作になります。このような内部的なstateのrenameが行われることで破壊的な変更を伴わずにrefactoring後のコードでTerraform Planに差分がないことを確認できます。また、Terraformのコードによって宣言的にstateの移行を記述できるためどのような変更が行われるのかが明確でありレビューもしやすくなります。

実際にmoved blocksを使ってrefacotringを行う際には以下のような手順になるでしょう

  1. Terraformコードのrefactoringを行う
  2. 変更したobjectについてはmoved blocksでstateの移行を記述する
  3. Terraform Planを実行して変更後のコードに差分がないことを確認する
  4. もしPlanで変更に起因する差分が会った場合は2と3を繰り返す
  5. Terraform Applyして実際にstateへ変更を反映する

Tips?

このupdateを受けて、実際にmoved blocksを使ってTerraformの大規模なrefacotringを行ってみたのですが変更量が多い場合moved blocksとして記述する対象が多くなるのでそこはまだ大変だと感じました。とはいえ、これまでの terraform state mv による操作よりは格段に楽で安全になっていますし、私の場合はコードの変更差分からmoved blocksを生成する雑なスクリプト等を書いて対応したりしました。誰かの参考になればと思い、私が使ったスクリプトを置いておきます。Terraformをrefactoringしたコードをgitにコミットした上で

  1. 以下のファイルを適当な名前で保存
$stdin.readlines.each_slice(2) do |a, b|
  puts <<EOF
moved {
  from = #{a.chomp}
  to   = #{b.chomp}
}

EOF
end
  1. gitの差分からうまく変更前後のobject名を抽出し、1のRubyスクリプトに渡してmoved blocksを生成
git show | grep 'resource "' | grep -v @@ | sed -e 's/.*resource "\(.*\)" "\(.*\)".*$/\1\.\2/' | ruby moved-blocks.rb >> moved_blocks.tf

まとめ

これまでTerraformでrefacotringをする際にはstateの変更が必要になるというのが大きなネックとなっていましたが、moved blocksの登場によりrefactoringで発生するすべての変更をTerraformのコードとして定義できるようになったのでより安全に、レビューしやすい状態でTerraformのrefactoringを行えるようになりました。Terraformの大きな課題だった部分に一定の改善が見られたのは非常に嬉しいニュースですね。ではでは。

May your Terraforming be happy!


マネーフォワードでは、エンジニアを募集しています。
ご応募お待ちしています。

【サイトのご案内】
マネーフォワード採用サイト
Wantedly
京都開発拠点

【プロダクトのご紹介】
お金の見える化サービス 『マネーフォワード ME』 iPhone,iPad Android

ビジネス向けバックオフィス向け業務効率化ソリューション 『マネーフォワード クラウド』

おつり貯金アプリ 『しらたま』

お金の悩みを無料で相談 『マネーフォワード お金の相談』

だれでも貯まって増える お金の体質改善サービス 『マネーフォワード おかねせんせい』

金融商品の比較・申し込みサイト 『Money Forward Mall』

くらしの経済メディア 『MONEY PLUS』

Pocket