これは何
Atlantis を利用すると、PullRequestを利用したterraform実行環境として以下のようなWorkflowが実現出来る。
- terraform の変更を含む PR を作成
- PR上でplanをコメントで表示
atlantis apply
のコメントをPRにすることでterraform applyを実施して、コメントで表示
とはいえatlantisはサーバを立てる必要もあるし、GitHub Actionsのみで簡易的なものを実現してみた事例が、今回述べたいこと。
今回実現したこと
以下のことを実現している
- PRでterraform planの結果を表示
- PRにコメントをすることで、terraform applyを実施して、結果をコメントに表示
一方で、atlantisで出来るような以下のことについては対応していない。
- plan結果をもとにしたapply実施
- approve済みかどうかの確認
- 複数のworking directory対応
- 等々
事前準備
実施環境によるが、GCP等に対してOIDCで認証情報を得てGitHub Actionを実行する際の設定等を事前に実施する。
▷ GitHub ActionsでGCPを操作する際における実装例はここを展開
GitHub Actions と Terraform Cloud に対応した Workload Identity 連携の構成 | Google Cloud 公式ブログ
resource "google_iam_workload_identity_pool" "github_actions" { project = var.project_id workload_identity_pool_id = "github-actions" display_name = "github-actions" } resource "google_iam_workload_identity_pool_provider" "github_actions" { project = var.project_id workload_identity_pool_id = google_iam_workload_identity_pool.github_actions.workload_identity_pool_id workload_identity_pool_provider_id = "github-actions" display_name = "github-actions" attribute_condition = "assertion.repository_owner=='${local.repository_owner}'" attribute_mapping = { "google.subject" = "assertion.sub" "attribute.repository" = "assertion.repository" "attribute.repository_owner" = "assertion.repository_owner" "attribute.branch" = "assertion.sub.extract('/heads/{branch}/')" } oidc { issuer_uri = "https://token.actions.githubusercontent.com" } } resource "google_service_account" "terraform_plan" { project = var.project_id account_id = "github-actions-terraform-plan" display_name = "github-actions-terraform-plan" } resource "google_service_account_iam_member" "terraform_plan" { service_account_id = google_service_account.terraform_plan.id role = "roles/iam.workloadIdentityUser" member = "principalSet://iam.googleapis.com/${google_iam_workload_identity_pool.github_actions.name}/attribute.repository/${local.repository_owner}/${local.repository_name}" } resource "google_project_iam_member" "terraform_plan" { for_each = toset([ "roles/viewer", ]) role = each.key member = "serviceAccount:${google_service_account.terraform_plan.email}" project = var.project_id }
GitHub Actionsにおける設定
planを実施するworkflow
以下のようなworkflowを用意することで、PRに対してterraform planを実施してコメントをすることは可能。ここまではよくある基本例。
- base_ref に応じた設定の変更
- GCPに対するOIDC
- aquaを利用した各種ツールのinstall
- terraform planを実施して、tfcmtでコメントを付与
name: terraform plan on: pull_request: branches: - dev - prd types: - opened - synchronize paths: - infra/** - .github/workflows/terraform-plan.yml jobs: plan: runs-on: ubuntu-latest timeout-minutes: 20 permissions: id-token: write contents: read pull-requests: write steps: - name: env for dev if: github.base_ref == 'dev' run: | echo "WORK_DIR=infra/gcp/envs/dev" >> $GITHUB_ENV echo "PROJECT_ID=something-dev" >> $GITHUB_ENV echo "PROJECT_NUMBER=1234567890" >> $GITHUB_ENV - uses: actions/checkout@v4 - uses: actions/cache@v4 with: path: ~/.local/share/aquaproj-aqua key: v1-aqua-installer-terraform-${{runner.os}}-${{runner.arch}}-${{hashFiles('aqua.yaml')}} restore-keys: | v1-aqua-installer-terraform-${{runner.os}}-${{runner.arch}}- - uses: aquaproj/aqua-installer@v3.0.1 with: aqua_version: v2.28.0 - uses: google-github-actions/auth@v2 with: workload_identity_provider: 'projects/${{ env.PROJECT_NUMBER }}/locations/global/workloadIdentityPools/YOUR_POOL_NAME/providers/YOUR_PROVIDER_ID' service_account: github-actions-terraform-plan@${{ env.PROJECT_ID }}.iam.gserviceaccount.com - working-directory: ${{ env.WORK_DIR }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | terraform init -reconfigure -no-color -input=false tfcmt plan -patch -- terraform plan -no-color -input=false -lock=false
applyを実施するworkflow
以下のようなworkflowを用意することで、PRに /terraform apply
というコメントを付与することで、以下のことを実施する
- base_ref に応じた設定の変更
- GCPに対するOIDC
- aquaを利用した各種ツールのinstall
- terraformを実施して、tfcmtでコメントを付与
name: terraform apply on: # https://docs.github.com/ja/webhooks/webhook-events-and-payloads#issue_comment issue_comment: types: [created] jobs: apply: runs-on: ubuntu-latest timeout-minutes: 20 if: ${{ github.event.issue.pull_request }} && github.event.comment.body == '/terraform apply' permissions: id-token: write contents: read pull-requests: write steps: - uses: actions/checkout@v4 with: ref: refs/pull/${{ github.event.issue.number }}/head - name: get pull request info run: | echo "BASE_REF_NAME=$(gh pr view $PR_NO --repo $REPO --json baseRefName --jq '.baseRefName')" >> $GITHUB_ENV echo "HEAD_REF_NAME=$(gh pr view $PR_NO --repo $REPO --json headRefName --jq '.headRefName')" >> $GITHUB_ENV env: REPO: ${{ github.repository }} PR_NO: ${{ github.event.issue.number }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: env for dev if: env.BASE_REF_NAME == 'dev' run: | echo "WORK_DIR=infra/gcp/envs/dev" >> $GITHUB_ENV echo "PROJECT_ID=something-dev" >> $GITHUB_ENV echo "PROJECT_NUMBER=1234567890" >> $GITHUB_ENV - uses: actions/cache@v4 with: path: ~/.local/share/aquaproj-aqua key: v1-aqua-installer-terraform-${{runner.os}}-${{runner.arch}}-${{hashFiles('aqua.yaml')}} restore-keys: | v1-aqua-installer-infra-${{runner.os}}-${{runner.arch}}- - uses: aquaproj/aqua-installer@v3.0.1 with: aqua_version: v2.28.0 - uses: google-github-actions/auth@v2 with: workload_identity_provider: 'projects/${{ env.PROJECT_NUMBER }}/locations/global/workloadIdentityPools/YOUR_POOL_NAME/providers/YOUR_POOL_ID' service_account: github-actions-terraform-apply@${{ env.PROJECT_ID }}.iam.gserviceaccount.com - working-directory: ${{ env.WORK_DIR }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | terraform init -reconfigure -no-color -input=false tfcmt -pr ${{ github.event.issue.number }} apply -- terraform apply -no-color -input=false -auto-approve
issue_commentをtriggerにしてGitHub Actionsを実行している。issue_comment には以下のようなクセがある。
- default branchにおけるworkflowが実施される
- 検証を行う際にも、default branchへ内容を取り込む必要がある
- actions/checkoutで対応されるのも、未指定の場合default branchとなるため、
ref: refs/pull/${{ github.event.issue.number }}/head
のようにPRに対する変更を含めたものをcheckoutする
- pull requestに関する情報が少ない
- issue_numberは取得出来るものの、eventにはbase_refなどが含まれていない
- Webhook のイベントとペイロード - GitHub Docs に定義がある
- そのため、各種情報を取得するには
permissions.pull-requests
の設定を追加した上で、ghコマンドで取得する必要がある
- tfcmtにおいてもどのPRに対してコメントをするのかを明示する必要がある
Webhook のイベントとペイロード - GitHub Docs にある情報は利用出来るものの、approve済みかどうかなどもデフォルトのeventからは取得出来ない。こちらの機能に加えてatlantisのようにreview済みのみのものしか反映させたくない等の実装を加えたい場合は、随時 gh
コマンドなどで情報を取得するのが良い。
〆
PRをmergeした際にterraform applyを実施するという運用もあるが、以下の理由からmerge前に実施したいこともあると思う。
- terraform applyは失敗しうる
- merge後に走るcdの内容を、事前に変更したい
様々な運用がある中で、こういったやり方を求める人は参考にしてもらえると幸いである。
tfaction | tfaction で十分という人はそれでよい。