tfaction の導入ガイド
tfaction という、 GitHub Actions で良い感じの Terraform Workflow を構築するための Action を開発しています。
今回は tfaction の導入ガイドのようなものを書こうかと思います。
AWS Account が必要です。
執筆時点で tfaction の最新バージョンは v0.4.5 です。
2022-02-07 追記
Getting Started を作成しました。
README に従っていけば tfaction の workflow を動かして terraform を実行するのを体験できると思います。
IAM OpenID Connect provider の作成
IAM OpenID Connect provider を作成します。
data "tls_certificate" "github" {
url = "https://token.actions.githubusercontent.com/.well-known/openid-configuration"
}
resource "aws_iam_openid_connect_provider" "github" {
url = "https://token.actions.githubusercontent.com"
thumbprint_list = [data.tls_certificate.github.certificates[0].sha1_fingerprint]
client_id_list = ["sts.amazonaws.com"]
}
S3 Bucket の作成
tfaction は 3 種類の S3 Bucket を使います。
- Terraform Backend
- Plan File
- tfmigrate history
これらは共通化しても tfaction の動作的には特に問題ありません。
IAM Role の作成
Assume Role する IAM Role を作成します。
module "aws" {
source = "github.com/suzuki-shunsuke/terraform-aws-tfaction?ref=v0.1.2"
name = "AWS" # Module のコードを見れば分かるが、 IAM Role や Policy の名前の一部に使われる
repo = "suzuki-shunsuke/tfaction-example"
main_branch = "main"
s3_bucket_tfmigrate_history_name = "<S3 Bucket Name for tfmigrate hisotry>"
s3_bucket_terraform_plan_file_name = "<S3 Bucket Name for terraform plan file>"
s3_bucket_terraform_state_name = "<S3 Bucket Name for terraform state>"
}
tfaction は GitHub Actions の 4 種類の job に応じて IAM Role を使い分けます。
- terraform plan
- terraform apply
- tfmigrate plan
- tfmigrate apply
上記の Module はこれらの 4 つの job 用に IAM Role を 4 つ作ります。それらの IAM Role には job に応じて 3 つの S3 Bucket に対して必要な権限のみが付与されています。
AWS Provider 以外では IAM Role の権限的にはこれで十分なはずです。
AWS Provider の場合には適宜権限を付与します。
以下では雑に AdministratorAccess と ReadOnlyAccess を付与していますが、より権限を絞るのが望ましいでしょう。
resource "aws_iam_role_policy_attachment" "terraform_apply_admin" {
role = module.aws.aws_iam_role_terraform_apply_name
policy_arn = "arn:aws:iam::aws:policy/AdministratorAccess"
}
resource "aws_iam_role_policy_attachment" "terraform_plan_readonly" {
role = module.aws.aws_iam_role_terraform_plan_name
policy_arn = "arn:aws:iam::aws:policy/ReadOnlyAccess"
}
resource "aws_iam_role_policy_attachment" "tfmigrate_plan_readonly" {
role = module.aws.aws_iam_role_tfmigrate_plan_name
policy_arn = "arn:aws:iam::aws:policy/ReadOnlyAccess"
}
resource "aws_iam_role_policy_attachment" "tfmigrate_apply_readonly" {
role = module.aws.aws_iam_role_tfmigrate_apply_name
policy_arn = "arn:aws:iam::aws:policy/ReadOnlyAccess"
}
tfaction は setup
Action で https://github.com/aws-actions/configure-aws-credentials を実行し、上記の IAM Role を Assume しています。
GCP の場合
tfaction では GCP にも Access Key なしでアクセスできます。
GCP を使わない人は無視して構いません。
GCP 関連の設定がしてある場合、 setup
Action で https://github.com/google-github-actions/auth を実行します。
https://github.com/google-github-actions/auth#setup などに従って Workload Identity Pool や Provider, Service Account を作成してください。
Personal Access Token か GitHub App の作成
tfaction では secrets.GITHUB_TOKEN
以外に GitHub Access Token が必要です。
tfaction は自動でコードを修正して commit, push したり、自動で pull request を作成したりしますが、その際 secrets.GITHUB_TOKEN
では workflow が起動しないからです。
設定ファイルの追加
幾つかの設定ファイルが必要です。
tfaction-root.yaml
一旦空で作成しておきます。
target_groups: []
GitHub Secrets も追加してください。
working directory に tfaction.yaml を作成
tfaction は一つのリポジトリに複数の working directory がある Monorepo を想定しています。
working directory とは terraform init や plan, apply といった terraform を実行するディレクトリのことです。
それらの directory に tfaction.yaml
というファイルを置いてください。
中身は空でも大丈夫です。
{}
tfaction では setup
job で list-targets
という action を実行して対象の working directory のリストを抽出し、後続の job (tfmigrate-plan, terraform-plan) に list を渡して
build matrix で working directory ごとに並列で terraform などを実行します。
対象の working directory は、 Pull Request でコードが変更された working directory 及び Pull Request Label によって指定された working directory です。
tfaction.yaml
があるディレクトリから対象のディレクトリを絞り込むので、 tfaction.yaml
がないとそもそも対象になりません。
# 一途抜粋。省略しているのでこのままでは動かない
jobs:
setup:
outputs:
tfmigrate_targets: ${{ steps.list-targets.outputs.tfmigrate_targets }}
terraform_targets: ${{ steps.list-targets.outputs.terraform_targets }}
steps:
# ...
- uses: suzuki-shunsuke/tfaction/list-targets@v0.4.4
id: list-targets
tfmigrate-plan:
needs: setup
strategy:
matrix:
target: ${{fromJSON(needs.setup.outputs.tfmigrate_targets)}}
terraform-plan:
needs: setup
strategy:
matrix:
target: ${{fromJSON(needs.setup.outputs.terraform_targets)}}
tfaction-root.yaml の target_groups を設定する
以下のような設定を書く必要があります。
- https://suzuki-shunsuke.github.io/tfaction/docs/config/tfaction-root-yaml
- https://suzuki-shunsuke.github.io/tfaction/config/tfaction-root.yaml.html
target_groups:
- working_directory: aws/services/
target: aws/
aws_region: ap-northeast-1
s3_bucket_name_plan_file: '<S3 Bucket Name for Terraform Plan File>'
s3_bucket_name_tfmigrate_history: '<S3 Bucket Name for tfmigrate history files>'
terraform_plan_config:
aws_assume_role_arn: arn:aws:iam::000000000000:role/GitHubActions_Terraform_AWS_terraform_plan
tfmigrate_plan_config:
aws_assume_role_arn: arn:aws:iam::000000000000:role/GitHubActions_Terraform_AWS_tfmigrate_plan
terraform_apply_config:
aws_assume_role_arn: arn:aws:iam::000000000000:role/GitHubActions_Terraform_AWS_terraform_apply
tfmigrate_apply_config:
aws_assume_role_arn: arn:aws:iam::000000000000:role/GitHubActions_Terraform_AWS_tfmigrate_apply
tfaction は対象の working directory の設定を target_groups から探索します。
配列の要素を上から一つずつチェックし、マッチするものがあればそれを採用して探索を終了します。
マッチするか否かは、 working_directory
ないし target
で決まります。
tfaction には TFACTION_TARGET
という環境変数があるように、 target という概念があります。
これは working directory のエイリアスのようなもので、 working directory と 1 対 1 に対応します。
working directory と同じでも構いません。
target は Pull Request Label や Pull Request Comment などにも含まれます。
working directory の path から余計な文字列を除外したり、置換したりするのに使えます。
target_groups の working_directory
, target
はそれぞれ working directory と target の prefix で、
これらの prefix にマッチするか否かで決まります。
prefix なので、末尾の /
には気をつけてください。
aws/
は aws/foo
にはマッチしますが、 aws
にはマッチしません。
逆に aws
は aws/foo
と aws
にマッチしますが、 aws-foo
にもマッチします。
あとは見れば分かるかと思いますが、 Assume する IAM Role や S3 Bucket を指定しています。
一部の設定は target group 直下に設定も出来ますし、各 job ごとに設定をすることも出来ます。
tfaction.yaml で設定を上書きする
tfaction-root.yaml では target group という単位で設定をしますが、
working directory に配置する tfaction.yaml によってそれらの設定の一部は上書きができます。
Secret を参照する
working directory に aqua.yaml を置く
- https://github.com/suzuki-shunsuke/tfaction-example/blob/ec2acf53ac500155e0c33628b8f003597b887a60/github/services/foo/aqua.yaml
- https://github.com/suzuki-shunsuke/tfaction-example/tree/ec2acf53ac500155e0c33628b8f003597b887a60/github/services/foo/aqua
tflint や tfsec を working directory ごとにバージョン管理します。
working directory ごとにバージョン管理する代わりに、リポジトリ直下の aqua.yaml で管理してしまう手もあります。
Pull Request を作成し、動作を確認する
上記のように設定したら、適当に working directory を作成して Pull Request を投げましょう。
tfaction の workflow がいい感じに動くはずです。
.github/workflows/
test.yaml
apply.yaml
hide_comment.yaml
aqua.yaml
tfaction-root.yaml
foo/ # working directory
tfaction.yaml
main.tf
...
plan がいい感じに動いたら、マージして apply までしてみましょう。
tflint, tfsec などを使わない場合
tfaction の test
action では tflint, tfsec などを実行し、 Reviewdog で結果を通知します。
tflint や tfsec を使わない場合は、 test
action を使うのをやめてください。
test
action を消しても問題なく workflow は動作するはずです。
そして test
action の代わりに独自の step, action を追加すればよいでしょう。
scaffold 用の template を追加する
templates 配下に working directory を scaffold するための template を追加します。
template は tatget group ごとに変更できます。
templates/
aws/
foo/
.tfsec/config.yml
.tflint.hcl
terraform.tf
aqua.yaml や .tfmigrate.hcl などは template になくても自動で生成されます。
template 中の %%TARGET%%
という文字列は target に置換されます。このへんの仕組みはあまり洗練されていないので、より改善したいとは思っています(そのうち変わるかもしれません)。
template を追加したら tfaction-root.yaml
でその directory を指定します。
target_groups:
- working_directory: aws/services/
target: aws/
template_dir: templates/aws/foo
そして workflow を追加します。
追加したら workflow を実行してみましょう。
input として target を入力します。
上の例だと target を aws/zoo
とすると template templates/aws/foo
を使って aws/services/zoo
に working directory を追加する Pull Request が作成されます。
scaffold をカスタマイズする
scaffold 時にコマンドを実行してコードを生成したい場合、 scaffold-working-dir
action を実行したあとに
独自の step を追加すればよいでしょう。
ただし、 create-scaffold-pr
は working directory 配下のコードしか commit, push しないので、
それ以外のコード (e.g. tfaction-root.yaml
) を変更しても反映されません。
さいごに
以上、 tfaction を導入するための手順をざっと説明しました。
参考になれば幸いです。
Discussion