こんにちは、@r_takaishi です。最近のおすすめYouTubeチャンネルは Namibia: Live stream in the Namib Desert です。今回は、Terraformのplan結果をmarkdownで整形するツールである reproio/terraform-j2md について紹介します。
どのようなツールなのか
まずはterrafororm-j2mdがどのようなツールなのかお見せします。まず、以下のようなTerraformのコードを用意します。
terraform { required_providers { env = { source = "tchupp/env" version = "0.0.2" } } } provider "env" { # Configuration options } resource "env_variable" "test" { name = "foo" }
terraform plan
を実行します。 -out
オプションを使うことでplanの結果をファイルに出力することができます。これがポイントです。
% terraform plan -out plan.tfplan Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols: + create Terraform will perform the following actions: # env_variable.test will be created + resource "env_variable" "test" { + id = (known after apply) + name = "foo" + value = (sensitive value) } Plan: 1 to add, 0 to change, 0 to destroy. ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── Saved the plan to: plan.tfplan To perform exactly these actions, run the following command to apply: terraform apply "plan.tfplan"
このplan.tfplanファイルを terraform show
コマンドに渡すことで、plan結果をjson形式に変換することができます。便利ですね。
% terraform show -json plan.tfplan {"format_version":"1.0","terraform_version":"1.1.8","planned_values":{"root_module":{"resources":[{"address":"env_variable.test","mode":"managed","type":"env_variable","name":"test","provider_name":"registry.terraform.io/tchupp/env","schema_version":0,"values":{"name":"foo"},"sensitive_values":{}}]}},"resource_changes":[{"address":"env_variable.test","mode":"managed","type":"env_variable","name":"test","provider_name":"registry.terraform.io/tchupp/env","change":{"actions":["create"],"before":null,"after":{"name":"foo"},"after_unknown":{"id":true,"value":true},"before_sensitive":false,"after_sensitive":{"value":true}}}],"configuration":{"provider_config":{"env":{"name":"env","version_constraint":"0.0.2"}},"root_module":{"resources":[{"address":"env_variable.test","mode":"managed","type":"env_variable","name":"test","provider_config_key":"env","expressions":{"name":{"constant_value":"foo"}},"schema_version":0}]}}}
そして、jsonに変換したplan結果をterraform-j2mdに渡すと、変更内容がmarkdown形式で出力されます。
% terraform show -json plan.tfplan | ./terraform-j2md
### 1 to add, 0 to change, 0 to destroy, 0 to replace.
- add
- env_variable.test
<details><summary>Change details</summary>
```diff
# env_variable.test will be created
@@ -1 +1,3 @@
-null
+{
+ "name": "foo"
+}
```
</details>
プレビューするとこのような見た目となり、追加・変更・削除などの概要とそれぞれの詳細を見ることができます。
背景
Reproではインフラの管理にTerraformを使っています。CIによってコミット毎にterraform plan
を実行するのですが、plan結果をRubyスクリプトで簡単に成形し、PullRequestにコメントするような仕組みをこれまでは使っていました。
しかし、Terraformの特定のバージョン以降、最後に実行された terraform apply
の外側で行われた変更(以降、Resource Driftsと呼称します)がplan結果に含まれるようになりました。
plan時にResource Driftsが分かること自体は何も問題はありません。しかし、plan結果をPullRequestにコメントしているのは、そのPullRequestによってどのような変更が加えられるのかを端的に確認するためです。 Resource Driftsはその目的には不要な情報です。また、Resource Driftsの量が多いとPullRequestによる変更が埋もれてしまい、レビューを行いにくいという問題もありました。
また、plan結果を色づけする程度でほぼそのままコメントしているため、ぱっと見てどのような変更が加わるのか認識しにくいという課題もありました。
これらの課題を解決するため、筆者が所属する開発基盤改善チームでは reproio/terraform-j2md というツールを作成しました。
なぜ作ったのか
まず、既存のRubyスクリプトを改修し、Resource Driftsをコメントから取り除くことを考えました。しかし、既存のスクリプトは構造化されていないplan結果を加工しています。ここにResource Driftsを取り除くコードや差分のサマライズを行うコードを加えるとなるど、将来plan結果のフォーマットが変わった際にうまくパースできない可能性が考えられました。
次に、既存ツールで似たようなことができないかを検討しました。suzuki-shunsuke/tfcmt というツールの存在は知っていたので、これを用いて実現できないかと検証を行いましたしかし、以下のような点が要件に合いませんでした。
- Reproでは環境毎にtfstateを分けており、CIではそれらについてterraform planを並列で実行している。これらのplan結果をtfcmtでPullRequestにコメントすると、コメントが複数追加され、やや見づらい
- 既存の仕組みでは、過去のplan結果はコメントを消し、最新のplan結果のみ残るようになっている。しかし、tfcmtでは過去のコメント削除は行われない
- テンプレートでコメントの形式をカスタマイズできるが、plan結果を整形した結果を標準出力に出すことができないため、手元で試しづらい
- 構造化されていないplan結果をパースしており、フォーマット変更時にうまく動かない可能性がある
なお、tfcmtがコメントするplan結果の形式はかなり見やすいと感じたため、今回作ったツールでは非常に参考にさせていただきました。
他に方法はないか調べる中で、 いつのまにかTerraformにplan結果をjsonに変換する機能が追加されていることに気がつきました。これが使えそうだったので、独自ツールの開発に着手しました。
reproio/terraform-j2md はGo製のツールです。既存のRubyスクリプトを改修してplan結果のjsonを加工することもできたのですが、 hashicorp/terraform-json というplan結果のjsonを扱うための型定義がライブラリとして公開されていた点、開発を担当したメンバーのGoへの習熟という点でGoを選択しました。
terraform-j2mdの持つ機能
terraform-j2mdはplan結果のjsonを標準入力から渡すとmarkdown形式に変換して標準出力に出す、という単純な機能しか持ちません。plan結果のファイルを直接読むことはできないし、PullRequestにコメントする機能もありません。ReproではCIの中で平行して複数のtfstateに対してplanを実行しますが、そのplan結果をmarkdownに変換した後、一つのテキストにマージしてPullRequestにコメントしています。マージ処理はBashスクリプトを使っているし、PullRequestへのコメントもそれまで使っていたスクリプトを少し改修して使っています。
このように単純な機能しか持たないterraform-j2mdですが、内部的には hashicorp/terraform-json という terraform show -json
の結果をパースするための型情報を扱うライブラリを使っています。これを使うことでplan結果を独自にパースする必要がなくなるメリットがありますが、terraform-jsonが型としてサポートしていない情報は扱えないというデメリットもあります。例えば、2022−06−30時点ではTerraform v1.1で追加されたmovedブロック関連の情報を扱うことができません。つまり、PullRequestにもmovedブロックに関する情報をコメントとして表示することがまだできないということです。
まとめ
terraform plan
の結果をPullRequestにコメントするため、plan結果のjsonをmarkdownに変換するツール reproio/terraform-j2md について紹介しました。現時点では対応できない機能があったり、うまくmarkdownに変換できていない差分もあることが分かっています。今後も、エンジニアがより楽にTerraformを扱えるように改善を続けていきます。