TORANA TECH BLOG

株式会社トラーナのエンジニアチームの開発ブログ

terraform を GitHubActions で実行する際のお供 tfdir の紹介

SREのクラシマです。

トラーナで公開しているOSS、tfdirを紹介します。
github.com

以下のようなterraformのディレクトリ構成を取っているとします。

❯ tree
.
├── environment
│   ├── prd
│   │   └── backend
│   │       └── main.tf
│   └── stg
│       └── backend
│           └── main.tf
└── modules
    └── backend
        └── hoge.tf

environment ディレクトリ配下で修正があった場合は、同ディレクトリへ移動して plan / apply を行えばよいです。
modules/backend に修正があった場合は、environment/prd/backend、environment/stg/backend の双方で plan / apply を行う必要があります。
この、依存関係の判定を自動的に行えればいいのにな、ということで、Hashicorp公式が GitHub - hashicorp/terraform-config-inspect: A helper library for shallow inspection of Terraform configurations というツールを出してくれています。
コマンドラインで実行すると、依存しているmoduleが判定できるツールです。

❯ cd environment/stg/backend
❯ terraform-config-inspect . --json | jq -r .module_calls
{
  "backend": {
    "name": "backend",
    "source": "../../../modules/backend",
    "pos": {
      "filename": "main.tf",
      "line": 1
    }
  }
}

上記 terraform-config-inspect をラップしてCIから使いやすくしたのが tfdir です。
まず、設定ファイルを作ります。

❯ cat .tfdir/pull_request/develop.yaml 
---
ExecutedDirs:
  - "environments/prd/backend"
  - "environments/stg/backend"

で、modules 配下で更新差分を作って、tfdir get に渡すと、以下のように modules に依存している environments が抽出できます。

❯ git diff --name-only "terraform/**.tf"
modules/backend/hoge.tf
❯ git diff --name-only "terraform/**.tf" | tfdir get --config .tfdir/pull_request/develop.yaml
environments/prd/backend
environments/stg/backend

トラーナでは、feature ブランチで開発、staging ブランチへのマージ時に staging 環境へデプロイ、動作確認後に master ブランチへマージ時に production 環境へデプロイ、としています。
stagingブランチへプルリクエストでは staging / production の双方で plan が動作してほしい、などを設定ファイルで制御ができます。
GitHub Actions に組み込むときは↓こんな感じ

on:
  pull_request:
     paths:
       - '**.tf'
jobs:
  get_target_dirs:
    runs-on: ubuntu-latest
    permissions:
      id-token: write
      contents: read
      pull-requests: write
    steps:
      - uses: actions/checkout@v4
      - name: install tfdir
        run: |
          curl "https://raw.githubusercontent.com/torana-us/tfdir/master/installer.sh" | bash
      - name: target branch
        id: target_branch
        run: |
          set -xv
          branch=""
          from=""
          to=""
          if [ "$GITHUB_EVENT_NAME" = "push" ]; then
            branch="$GITHUB_REF_NAME"
            from="${{ toJSON(github.event.before) }}"
            to="${{ toJSON(github.event.after) }}"
          else
            branch="$GITHUB_BASE_REF"
            from="origin/$GITHUB_BASE_REF"
            to="${GITHUB_REF/refs\//}"
          fi
          {
            echo "branch=$branch"
            echo "from=$from"
            echo "to=$to"
          } >> "$GITHUB_OUTPUT"
      - name: get diff
        run: |
          set -xv
          diff=$(git diff --diff-filter="AMRCD" \
                  --name-only \
                  "${{ steps.target_branch.outputs.from }}...${{ steps.target_branch.outputs.to }}" \
                  -- "**.tf" \
                  | xargs)
          echo "GIT_DIFF=$diff" >> "$GITHUB_ENV"
      - name: get target dir
        id: target_dirs
        run: |
          set -xv
          targets=$(echo ${{ env.GIT_DIFF }} \
            | tr ' ' '\n' \
            | ./tfdir get --config "./.tfdir/$GITHUB_EVENT_NAME/${{ steps.target_branch.outputs.branch }}.yaml" \
            | jq -cnR '[inputs | select(length > 0)]')
          echo "targets=$targets" >> "$GITHUB_OUTPUT"

get_target_dirs の output を使って、matrix strategy で plan / apply を実行しています。

まとめ

GitHub - suzuki-shunsuke/tfaction: Framework for Monorepo to build high level Terraform Workflows by GitHub ActionsGitHub - terramate-io/terramate: Terramate CLI is an open-source Infrastructure as Code (IaC) Orchestration and Code Generation tool for Terraform, OpenTofu and Terragrunt. でも同じことはできますが、tfdir はちっちゃいツールなので小回りが効きます。
よかったら使ってみてください!

PHP、TypeScriptだけでなく Go も書いてます。ご興味ありましたら、カジュアル面談いかがでしょうか? www.wantedly.com