🐑

tfactionのセットアップ手順を解説

に公開
2

tfactionとは

TerraformのCI/CDをGitHub Actions上で実行するためのOSSです。

tfaction自体は複数のComposite ActionとJavaScript Actionの集合体になっています。またtfactionのRepositoryから、同開発者のtfcmtAquaなどを利用しており、これらを組み合わせてTerraformに必要なWorkflowを簡単に作成できるようになっています。

他にもTerraform CloudやAtlantisなど類似ツールはありますが、導入や運用のコストを考えると、非常にオススメしたいツールです。有名な会社も多数利用しています。(参考)

詳細は開発者の鈴木さんの記事も参照ください。
https://zenn.dev/shunsuke_suzuki/articles/tfaction-introduction
https://suzuki-shunsuke.github.io/tfaction/docs/

フォルダ構成

完成イメージは以下の構成になります。

.
├── .github
│   └── workflows
│       ├── test.yml
│       ├── plan.yml
│       └── apply.yml
├── environments
│   ├── dev
│   │   ├── .terraform-version
│   │   ├── backend.tf
│   │   ├── locals.tf
│   │   ├── main.tf
│   │   ├── providers.tf
│   │   ├── terraform.tf
│   │   └── tfaction.yaml
│   ├── prd
│   └── .tflint.hcl
├── modules
│   ├── init
│   │   ├── main.tf
│   │   ├── outputs.tf
│   │   ├── variables.tf
│   │   └── tfaction_module.yaml
│   └── .tflint.hcl
├── .trivyignore.yaml
├── tfaction-root.yaml
├── aqua.yaml
└── trivy.yaml

詳細は以下のリポジトリを参照ください。
僕の場合はtfmigrateの設定と、dev環境はTerragruntを使用していたり、その他のworkflowも存在していますが、この記事ではtfactionの導入までとして割愛します。
https://github.com/fumi-mura/tfaction-sample

セットアップの概要

  1. Repositoryの作成
  2. GitHub Appの作成とインストール
  3. GitHub Secretsの登録
  4. OIDC用のIAM Roleの作成
  5. S3 Bucketの作成
  6. tfaction用の設定ファイルの作成
  7. workflowファイルの作成

セットアップの詳細

1. Repositoryの作成

GitHub ActionsはRepositoryがPublicの場合は無料で利用できますが、Privateでは実行時間に上限が存在するため注意してください。
https://docs.github.com/ja/billing/concepts/product-billing/github-actions

2. GitHub Appの作成とインストール

tfactionがRepositoryにアクセスするために必要です。
Appの作成方法はGitHubのドキュメントを参照ください。必要な権限はtfactionのドキュメントに記載されています。

Appを作成したら1で作成したRepositoryにインストールします。

インストールが完了するとRopositoryにAppが表示されます。

3. GitHub Secretsの登録

作成したAppはこちらから確認できます。
Appを選択し、GeneralからApp ID、Private keysを取得します。

RepositoryのSettingsを選択し、Repository secretsにそれぞれ登録します。

4. OIDC用のIAM Roleの作成

GitHub ActionsからAWSに安全にアクセスするためには、OIDC(OpenID Connect)を利用したIAM Roleを作成します。アクセスキーを使わずに、Workflow実行時に一時的な認証情報を払い出せます。
tfactionでは4個のRole(terraform plan/apply、tfmigrate plan/apply)を使い分けます。

条件に応じてPolicyは変更してください。今回はシンプルにplanにはReadOnly、applyにはAdmin権限を付けていて、terraformとtfmigrateは共通のRoleを利用しています。

5. S3 Bucketの作成

tfstate、Plan file(現在はGitHub Artifactsに保存されるようになったようです)、tfmigrate historyに使用するBucketです。
それぞれのBucketを作成しても、共通にしてディレクトリで分けても問題ありません。

6. tfaction用の設定ファイルの作成

tfaction-root.yaml

Repositoryのルートディレクトリに作成してください。(.ymlではエラーになったため、.yamlにする必要があるようです)
このファイルでtfactionの全般的な設定をします。
設定値の詳細はtfactionの公式ドキュメントを参照ください。
https://github.com/fumi-mura/tfaction-sample/blob/main/tfaction-root.yaml

environments/{env}/tfaction.yaml

各ルートモジュールに作成したら、このファイルを元にtfactionが変更検知をして、CICDのworking directoryに追加します。
tfaction-root.yamlの設定をoverrideできるため、ルートモジュール単位で設定をカスタマイズできます。こちらも詳細は公式ドキュメントを参照ください。
https://github.com/fumi-mura/tfaction-sample/blob/main/environments/dev/tfaction.yaml

modules/{module}/tfaction_module.yaml

各チャイルドモジュールに作成したら、このファイルを元にtfactionがmoduleを検知します。
こちらも詳細は公式ドキュメントを参照ください。
https://github.com/fumi-mura/tfaction-sample/blob/main/modules/cloudwatch/logs/tfaction_module.yaml

aqua.yaml

tfactionで必要なCLIツールのバージョン管理をします。
https://github.com/fumi-mura/tfaction-sample/blob/main/aqua.yaml

デフォルトでインストールされるツールはtfactionのRepositoryのこちらから確認できます。ここに無い場合はaqua.yamlに指定する必要があります。デフォルトでインストールされるものも、バージョンを記載すればoverrideできます。

.tflint.hcl

tfactionでtflintが使用したい場合に必要です。
ルートモジュール、チャイルドモジュールで設定を分けたい場合はそれぞれに、共通の場合はルートディレクトリに作成してください。僕は分けるバージョンにしています。
https://github.com/fumi-mura/tfaction-sample/blob/main/environments/.tflint.hcl

https://github.com/fumi-mura/tfaction-sample/blob/main/modules/.tflint.hcl

trivy.yaml

tfactionでtrivyを使用したい場合に必要です。
https://github.com/fumi-mura/tfaction-sample/blob/main/trivy.yaml

.trivyignore.yaml

trivyの検知対象から除外したいものを記載してください。
https://github.com/fumi-mura/tfaction-sample/blob/main/.trivyignore.yaml

7. workflowファイルの作成

いつものように、.github/workflows/以下にyamlファイルを作成します。僕の場合はtest、plan、applyに分けました。ここではtfaction固有の設定を記載します。詳細は各ファイルを参照ください。

test.yml

https://github.com/fumi-mura/tfaction-sample/blob/main/.github/workflows/test.yml

plan.yml

https://github.com/fumi-mura/tfaction-sample/blob/main/.github/workflows/plan.yml

apply.yml

https://github.com/fumi-mura/tfaction-sample/blob/main/.github/workflows/apply.yml

長くなりましたが準備としてはこれで以上です。

動かしてみる

featureブランチからmainブランチへPRを作成します。
ルートモジュールを変更したら、変更したディレクトリのみが対象となり、チャイルドモジュールを変更したら、全てのディレクトリが対象となります。

ちなみにApplyが失敗すると、以下のようなfollow-upのPRを自動作成してくれます。(あくまでPRを作成するだけでソースコードの修正は自分でやる必要があります)

フォーマットを検知したら自動修正のコミットをしてくれます。

PRをマージするとapplyのworkflowが走り、apply結果がコメントされます。

ソースコードの変更無しでもCICDを走らせたい

tfactionはソースコードの変更を検知してCICDの対象としていますが、PRにtarget:{target}(e.g. target:dev)のlabelを付けることで強制的に対象とすることができます。
label名はtfaction-root.yamlで設定可能で、デフォルトはtargetです。{target}の部分はtarget_groupstargetで設定可能で、デフォルトはルートモジュールへの相対パスです。
(※labelを付けた後にworkflowのre-runをすることでsetup以降のstepが実行されます)

まとめ

以前までTerraformのCICD構築のために、自分でShellを書いたり、色々なツールを組み合わせて頑張って構築していましたが、tfactionを利用することで、本来のインフラ構築に集中できる環境を高速で構築できます。
各種セキュリティツールも対応していますし、今回紹介できなかった便利なworkflowもあったりするので、もし気になった方はぜひ試して貰えたらと思います。

参考

Discussion

Shunsuke SuzukiShunsuke Suzuki

tfstate、Plan file、tfmigrate historyに使用するBucketです。

Plan file は S3 ではなく GitHub Artifacts に保存されます。
https://suzuki-shunsuke.github.io/tfaction/docs/feature/plan-file#tfaction-v070-migrated-plan-files-to-github-actions-artifacts
https://docs.github.com/ja/actions/tutorials/store-and-share-data

デフォルトでインストールされるツールはtfactionのRepositoryのこちらから確認できます。

正しくはこちらですね。
内部的に install という action が実行されてここにあるものがインストールされます。
https://github.com/suzuki-shunsuke/tfaction/tree/main/install/aqua

label名はtfaction-root.yamlで設定可能で、デフォルトはtargetです。
(※labelを付けた後にコミットが必要です)

prefix は target: ですが、設定可能です。
tfaction-root-yaml の target_groupstarget という項目があるのでそこで設定が可能で、デフォルトは Root Module への相対パスになります。
コミットが必要とありますが、正確には workflow を re-run するだけでも大丈夫です。

fumifumi

Plan file は S3 ではなく GitHub Artifacts に保存されます。

ありがとうございます!記事に反映させていただきました。

内部的に install という action が実行されてここにあるものがインストールされます。

ありがとうございます!installのactionがあったんですねmm
こちらも記事に反映させていただきました。

tfaction-root-yaml の target_groups で target という項目があるのでそこで設定が可能で、デフォルトは Root Module への相対パスになります。
コミットが必要とありますが、正確には workflow を re-run するだけでも大丈夫です。

ありがとうございます!こちらも記事に反映させていただきました。