🤖

tfactionを使ってTerraformの実行を自動化した!!

2024/12/16に公開

この記事は、tacoms Advent Calendar 2024の16日目です!
他メンバーのAdvent Calendarはこちらからご覧ください!👇
https://qiita.com/advent-calendar/2024/tacoms

はじめに

どうもー!株式会社tacomsのSREのMorixです!
入社して1ヶ月経ちました。毎日学ぶことが多くあっという間の1ヶ月でした!

この1ヶ月で色々やったのですが、その中のひとつである「tfactionを使ったTerraform自動化」についてお話したいと思います。

tfactionとは?

tfaction@shunsuke_suzuki氏が開発している、GitHub Actionsを使っていい感じのTerraform Workflowを構築するためのactionです。
単一のactionではなく複数のactionが用意されており、それらを組み合わせて使用します。

プルリクエストを作ると変更のあったディレクトリに対してterraform planを行い、差分をプルリクエストにコメントしてくれます。
レビューが済みmainブランチへマージされるとterraform applyをしてくれます。

tfactionを採用するまでの状況

まずtacomsのTerraform環境がどんな感じだったのかを説明したいと思います。
tacomsのTerraformのplanやapplyはGitHub Actionsで実行するようになっていました。
しかしworkflow_dispatchを使った手動実行のみのサポートで、以下のような手順でapplyまで持っていってました。

  1. Terraformのコード修正・プルリク作成
  2. Terraform FormatチェックがCI(GitHub Actions)で実行
  3. Plan用Workflowを手動実行
  4. 3の結果をプルリクエストに貼り付けてレビュー依頼
  5. レビュワーは3の結果を見に行き問題ないことを確認する
  6. プルリクエストをマージ
  7. Apply用Workflowを手動実行

applyするまでにやることが多く、作業の漏れもあったりしました。
この状況を改善するために、Terraformのplan/applyの実行を自動化したいと考えました。

tfactionを選定する意思決定の流れ

前述のような状況だったので、まずはどうやってplan/applyの実行の自動化をするかを検討しました。
tacomsではArchitectural Decision Records(ADR)で技術選定の意思決定を行なっているため、ADRを作成しました。
案としては次のものが出ました。

  1. Atlantisを使う
  2. GitHub Actionsで実現する(プルリクマージ前にapplyできる)
  3. GitHub Actionsで実現する(プルリクマージ後にapplyできる)

候補1.Atlantisを使う

Atlantisはプルリク上で特定のコメントするとplanやapplyをしてくれるセルフホスティング型のアプリケーションです。

これを使った場合のTerraformリリースフローは以下のようになります。

  1. プルリク作成
  2. 自動でplanされ、差分がプルリクにコメントされる
  3. approveもらったらプルリクコメントに terraform apply と書く
  4. apply結果がプルリクにコメントされる
  5. マージする

非常にわかりやすいです。
ディレクトリ単位のロック機構もあるので、他の人が別プルリクで同じディレクトを触っててもそのplan/applyの実施を抑制してくれます。
またマージ前にapplyできるのも良いです。Terraformではplanが実行できたからといってapplyできるわけではないので・・・。その試行錯誤をマージしなくてもできるのはすごくポイントが高いです。

しかしこれはアプリケーションをセルフホストしないといけないので、動かすインフラが必要になったり、運用保守も必要になってくるため見送りました。

候補2.GitHub Actionsで実現する(プルリクマージ前にapplyできる)

Finatextグループ様が寄稿されたGitHub ActionsのみでAtlantis likeなTerraform CICDの仕組みを構築するという記事では、Atlantisを使わずGitHub ActionsのみでAtlantisライクなことを実現するというのを紹介されていました。
これは大変素晴らしいアプローチで、インフラ運用不要で同じようなことができるのは大変魅力的です。

リリースフローはAtlantisと同様です。
メリットもAtlantisと同様で、マージ前にapplyできるのがポイント高いです。

しかしロック機構が複雑になりそうで、保守の観点で不安がありました。

候補3.GitHub Actionsで実現する(プルリクマージ後にapplyできる)

プルリクマージ前にapplyするのを諦めれば、複雑なロック機構は不要だと判断しました。
なぜ複雑なロック機構が不要かと判断したかというと、mainブランチにマージされたらapplyをするので、mainブランチをロックしておけば簡単に実現できると思ったからです。

さて、この場合次のようなリリースフローになります。

  1. プルリク作成
  2. 自動でplanされ、差分がプルリクにコメントされる
  3. プルリクがapproveされたらマージする
  4. apply結果がプルリクにコメントされる

ということでこの案で行くことにしました!

この流れ、当初はすべて自作しようとしてました。理由は前職で同じようなものを作っていたので簡単に作れると思ったからです。
しかし調べてみると作ろうと思ってた次の機能がすべてtfactionを使えば簡単に実現できそうだったので、tfactionを導入することにしました!

  • 変更があったディレクトリのみにplan/applyを実行
  • 変更があったディレクトリにplan/applyを並列実行
  • planの差分を整形してプルリクエストにコメント
  • ロック機能

複数Terraformリポジトリへのtfactionの導入方法

tfactionの導入はドキュメントにしっかり書いてくださっているので非常に簡単でした。
https://suzuki-shunsuke.github.io/tfaction/docs/config/setup
https://zenn.dev/shunsuke_suzuki/articles/tfaction-setup

しかしtacomsにはTerraformのコードがあるGitリポジトリが複数あります。
この手順を1つ1つのリポジトリにやるには少々時間がかかります。
この問題をどう解決したのかをご紹介します。

どうやったかというと、Terraform plan/applyをするWorkflowを1つのGitリポジトリに集約し、Terraform管理リポジトリはそのWorkflowを呼び出す です。

Terraform plan/applyするWorkflowを集約するGitリポジトリの構成

tfactionを使ったplanやapplyは複数のReusable Workflowを使って実現します。
tfaction-exampleを見るとコード構成がイメージしやすいです。
このworkflow群を1つのGitリポジトリで管理します。

実際の構成は次のようになってます。
workflowsにあるplan.yamlapply.yamlが他のGitリポジトリから呼び出されるReusable Workflowです。

.aqua
├── aqua-checksums.json
├── aqua.yaml
└── imports
    ├── github-comment.yaml
    ├── reviewdog.yaml
    └── etc...
.github/workflows
├── apply.yaml
├── plan.yaml
├── wc-hide-comment.yaml
├── wc-path-filter.yaml
├── wc-plan.yaml
├── wc-setup.yaml
├── wc-test-module.yaml
└── wc-test.yaml

ここのポイントはaquaの設定ファイルです。
aquaはコマンドラインツールの宣言型のバージョン管理ツールです。tfactionを使う場合このaquaを導入する必要があります。
tfactionは複数のコマンドラインツールに依存しています。reviewdogとかtrivyなどです。
これらのツールはtfactionには必要ですが、通常のローカルで実行するときのTerraformのplanには必要ありません。
なのでTerraform管理リポジトリでこれらの管理をしたくありません。
そのためtfactionでしか使わないコマンドラインツールのバージョン管理はこの集約リポジトリのみで行うことにしました。

しかし集約リポジトリのReusable Workflowが呼び出されたとして、集約リポジトリにあるaquaの設定ファイルは参照できません。
そのためplanとapplyのWorkflowの先頭に、集約リポジトリのaqua設定をダウンロードする処理を入れました。
こうすることでTerraformリポジトリではTerraformのaqua設定だけ入れておけばいい状態にできました!

Terraform管理リポジトリからplan/applyのWorkflowの呼び出し方

集約リポジトリにあるplan/applyをTerraform管理リポジトリから呼び出すだけで簡単にTerraform自動化ができます!
もちろんaquaの設定やtfaction-root.yamltfaction.yamlの配置は必要ですが!

name: plan
on:
  pull_request_target:
    branches:
      - main

concurrency:
  group: ${{ github.workflow }}-${{ github.head_ref }}
  cancel-in-progress: true

permissions: {}

jobs:
  plan:
    uses: xxx/xxx(集約リポジトリ名)/.github/workflows/plan.yaml@main
    permissions:
      id-token: write
      contents: read
    secrets:
      gh_app_id: ${{secrets.xxx }}
      gh_app_private_key: ${{secrets.xxx}}

tfactionを採用した感想

まだ採用して日が浅いですが、よかった点を書いてみます!

  • めちゃくちゃ簡単に導入できる
    • 上述したようにチュートリアルが充実してるので簡単に実現できます。ただplanを実行するだけでいいなら2〜3時間で出来ました!
  • 差分結果がめちゃくちゃわかりやすい
    • ただplan結果を出すんじゃなくて、いい感じに整形したうえでコメントしてくれるので読みやすいです
  • ロック機能が簡単に実現できる
  • apply失敗したらフォローアッププルリクエストを作ってくる
    • apply失敗したらほとんどの場合、修正するプルリクエストを作る羽目になりますが、それを自動でやってくれます。これ便利です。
tacomsテックブログ

Discussion