メインコンテンツまでスキップ

GitHub Actions ワークフロー設計書

Terraform インフラストラクチャの変更を GitHub Actions で自動検証・デプロイするワークフローの設計書です。

設計方針: 最小限の機能で動作する MVP をまず作り、実運用で必要になった機能を段階的に追加する。

目的

  • terraform/ ディレクトリ配下の変更を自動検出
  • PR 作成時に変更されたテナントに対して自動検証(plan)
  • GitHub の標準機能(Checks)で結果を確認

注意: 自動デプロイ(apply)は別の手動ワークフロー(terraform-apply.yml)で実行されます。

ワークフロー概要

PR作成/更新

terraform/変更検出

変更テナントと有効な環境を抽出

各テナント×有効な環境で並列実行:
- infra plan

ジョブ成否でPRチェック完了

デプロイ: 手動ワークフロー(terraform-apply.yml)を使用

詳細設計

1. トリガー条件

name: Terraform Plan (PR)

on:
pull_request:
types: [opened, synchronize, reopened]
paths: ["terraform/**"]

# OIDC認証に必要な権限
permissions:
id-token: write
contents: read
pull-requests: write
  • PR 時: terraform/** 配下の変更で plan 実行
  • 自動デプロイ: 実装されていません(手動ワークフロー terraform-apply.yml を使用)

2. 変更検出とテナント抽出

2.1 CLI コマンドを使用した検出(推奨)

infra detect-changes CLI コマンドを使用して、変更されたテナントと有効な環境を自動検出します:

detect-changes:
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.detect.outputs.matrix }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0

- uses: actions/setup-node@v4
with:
node-version: "20"
cache: "yarn"

- name: Install dependencies
run: |
yarn install --frozen-lockfile
yarn build

- name: Detect changed tenants and environments
id: detect
run: |
# PRの場合はbase..head、pushの場合はHEAD^..HEAD
if [ "${{ github.event_name }}" = "pull_request" ]; then
BASE=${{ github.event.pull_request.base.sha }}
HEAD=${{ github.event.pull_request.head.sha }}
else
BASE=${{ github.event.before }}
HEAD=${{ github.sha }}
fi

# CLIコマンドで変更されたテナントと有効な環境を検出
MATRIX=$(yarn infra detect-changes --base "$BASE" --head "$HEAD" --json)

echo "matrix=$MATRIX" >> $GITHUB_OUTPUT
echo "Detected matrix: $MATRIX"

重要な設計判断:

  • config.tomlenabled: true の環境のみを検出
  • enabled: false の環境は自動的に除外される
  • terraform/modules/ の変更時は全テナントを対象にする
  • 検出ロジックは CLI コマンドに集約され、保守性が向上

2.2 変更パターン

  • terraform/environments/tenants/{tenant-name}/config.toml → 該当テナント
  • terraform/environments/tenants/{tenant-name}/encrypted/** → 該当テナント
  • terraform/environments/tenants/{tenant-name}/{env}/** → 該当テナント
  • terraform/modules/**全テナント

3. PR 時のワークフロー

plan:
needs: detect-changes
if: needs.detect-changes.outputs.matrix != '[]'
runs-on: ubuntu-latest

# 同一テナント/環境の並行実行を防止
concurrency:
group: terraform-plan-${{ matrix.tenant }}-${{ matrix.environment }}

strategy:
fail-fast: false
matrix:
include: ${{ fromJson(needs.detect-changes.outputs.matrix) }}

env:
# Azure CLI認証用(azure/login@v2 が使用)
AZURE_CLIENT_ID: ${{ secrets.TERRAFORM_AZURE_CLIENT_ID }}
AZURE_TENANT_ID: ${{ secrets.TERRAFORM_AZURE_TENANT_ID }}
AZURE_SUBSCRIPTION_ID: ${{ secrets.TERRAFORM_AZURE_SUBSCRIPTION_ID }}
# Terraform AzureRM プロバイダー用(OIDC認証)
ARM_CLIENT_ID: ${{ secrets.TERRAFORM_AZURE_CLIENT_ID }}
ARM_TENANT_ID: ${{ secrets.TERRAFORM_AZURE_TENANT_ID }}
ARM_SUBSCRIPTION_ID: ${{ secrets.TERRAFORM_AZURE_SUBSCRIPTION_ID }}
ARM_USE_OIDC: true
# Terraform State バックエンドでAzure AD認証を使用(Storage Blob Data Contributorロール)
ARM_USE_AZUREAD: true
# 暗号化された環境変数ファイルの復号キー
DOTENV_PRIVATE_KEY_DEV: ${{ secrets.DOTENV_PRIVATE_KEY_DEV }}
DOTENV_PRIVATE_KEY_PRD: ${{ secrets.DOTENV_PRIVATE_KEY_PRD }}
DOTENV_PRIVATE_KEY_FRONTEND_DEV: ${{ secrets.DOTENV_PRIVATE_KEY_FRONTEND_DEV }}
DOTENV_PRIVATE_KEY_FRONTEND_PRD: ${{ secrets.DOTENV_PRIVATE_KEY_FRONTEND_PRD }}
DOTENV_PRIVATE_KEY_BACKEND_DEV: ${{ secrets.DOTENV_PRIVATE_KEY_BACKEND_DEV }}
DOTENV_PRIVATE_KEY_BACKEND_PRD: ${{ secrets.DOTENV_PRIVATE_KEY_BACKEND_PRD }}
# Terraform CLI引数
TF_CLI_ARGS_plan: -lock-timeout=5m

steps:
- uses: actions/checkout@v4

- uses: actions/setup-node@v4
with:
node-version: "20"
cache: "yarn"

- name: Install dependencies
run: |
yarn install --frozen-lockfile
yarn build

- uses: hashicorp/setup-terraform@v3

- uses: azure/login@v2
with:
client-id: ${{ secrets.TERRAFORM_AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.TERRAFORM_AZURE_TENANT_ID }}
subscription-id: ${{ secrets.TERRAFORM_AZURE_SUBSCRIPTION_ID }}

- name: Terraform plan
run: |
echo "🚀 Planning: ${{ matrix.tenant }} / ${{ matrix.environment }}"
yarn infra plan ${{ matrix.tenant }} ${{ matrix.environment }}

ポイント:

  • concurrency でロック競合を防止
  • TF_CLI_ARGS_plan でロック待機を延長(5 分)
  • ジョブ成否が自動的に GitHub Check として表示される

4. デプロイワークフロー(手動実行)

自動デプロイは実装されていません。代わりに、手動実行用の別ワークフロー(terraform-apply.yml)が用意されています:

name: Terraform Apply (Manual)

on:
workflow_dispatch:
inputs:
tenant:
description: "テナント名 (例: pj-japan, pj-naruto)"
required: true
type: string
environment:
description: "環境"
required: true
type: choice
options:
- dev
- prd

# OIDC認証に必要な権限
permissions:
id-token: write
contents: read

jobs:
apply:
runs-on: ubuntu-latest

# 同一テナント/環境の並行実行を防止
concurrency:
group: terraform-apply-${{ inputs.tenant }}-${{ inputs.environment }}
cancel-in-progress: false # 待機させる

env:
# Azure認証(Terraform実行用、azure/login@v2 アクションでも使用)
AZURE_CLIENT_ID: ${{ secrets.TERRAFORM_AZURE_CLIENT_ID }}
AZURE_TENANT_ID: ${{ secrets.TERRAFORM_AZURE_TENANT_ID }}
AZURE_SUBSCRIPTION_ID: ${{ secrets.TERRAFORM_AZURE_SUBSCRIPTION_ID }}
# Terraform AzureRM プロバイダー用(OIDC認証)
ARM_CLIENT_ID: ${{ secrets.TERRAFORM_AZURE_CLIENT_ID }}
ARM_TENANT_ID: ${{ secrets.TERRAFORM_AZURE_TENANT_ID }}
ARM_SUBSCRIPTION_ID: ${{ secrets.TERRAFORM_AZURE_SUBSCRIPTION_ID }}
ARM_USE_OIDC: true
# Terraform State バックエンドでAzure AD認証を使用(Storage Blob Data Contributorロール)
ARM_USE_AZUREAD: true
# 暗号化された環境変数ファイルの復号キー
DOTENV_PRIVATE_KEY_DEV: ${{ secrets.DOTENV_PRIVATE_KEY_DEV }}
DOTENV_PRIVATE_KEY_PRD: ${{ secrets.DOTENV_PRIVATE_KEY_PRD }}
DOTENV_PRIVATE_KEY_FRONTEND_DEV: ${{ secrets.DOTENV_PRIVATE_KEY_FRONTEND_DEV }}
DOTENV_PRIVATE_KEY_FRONTEND_PRD: ${{ secrets.DOTENV_PRIVATE_KEY_FRONTEND_PRD }}
DOTENV_PRIVATE_KEY_BACKEND_DEV: ${{ secrets.DOTENV_PRIVATE_KEY_BACKEND_DEV }}
DOTENV_PRIVATE_KEY_BACKEND_PRD: ${{ secrets.DOTENV_PRIVATE_KEY_BACKEND_PRD }}
# Terraform CLI引数
TF_CLI_ARGS_apply: -lock-timeout=5m

steps:
- uses: actions/checkout@v4
with:
ref: main # mainブランチから実行

- uses: actions/setup-node@v4
with:
node-version: "18"
cache: "yarn"

- name: Install dependencies
run: |
yarn install --frozen-lockfile
yarn build

- uses: hashicorp/setup-terraform@v3

- uses: azure/login@v2
with:
client-id: ${{ secrets.TERRAFORM_AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.TERRAFORM_AZURE_TENANT_ID }}
subscription-id: ${{ secrets.TERRAFORM_AZURE_SUBSCRIPTION_ID }}

- name: Terraform apply
run: |
echo "🚀 Applying: ${{ inputs.tenant }} / ${{ inputs.environment }}"
yarn infra apply ${{ inputs.tenant }} ${{ inputs.environment }} --auto-approve

ポイント:

  • GitHub Actions の UI から手動で実行
  • テナントと環境を選択して実行
  • 安全性のため自動デプロイは実装されていない

5. 必要な環境変数・シークレット

5.1 Azure 認証(OIDC 推奨)

GitHub Secrets に設定(TERRAFORM_ プレフィックス付き):

  • TERRAFORM_AZURE_CLIENT_ID: Service Principal ID
  • TERRAFORM_AZURE_TENANT_ID: Azure Tenant ID
  • TERRAFORM_AZURE_SUBSCRIPTION_ID: Azure Subscription ID

OIDC の場合: AZURE_CLIENT_SECRET は不要

5.2 暗号化された環境変数ファイルの復号キー

環境とコンポーネントごとに個別の復号キーが必要:

基本環境キー (monolith テンプレートなど):

  • DOTENV_PRIVATE_KEY_DEV: 開発環境の復号キー
  • DOTENV_PRIVATE_KEY_PRD: 本番環境の復号キー

フロントエンド/バックエンド分離テンプレート用:

  • DOTENV_PRIVATE_KEY_FRONTEND_DEV: フロントエンド開発環境の復号キー
  • DOTENV_PRIVATE_KEY_FRONTEND_PRD: フロントエンド本番環境の復号キー
  • DOTENV_PRIVATE_KEY_BACKEND_DEV: バックエンド開発環境の復号キー
  • DOTENV_PRIVATE_KEY_BACKEND_PRD: バックエンド本番環境の復号キー

これらのキーは encrypted/encrypted.env.* ファイルの復号に使用されます。

5.3 Terraform State 管理

  • OIDC 認証を使用: ARM_USE_AZUREAD: true により、Azure AD 認証で Terraform State にアクセス
  • 必要なロール: Service Principal に Storage Blob Data Contributor ロールが付与されている必要があります
  • 注意: ARM_ACCESS_KEY は使用しません(OIDC 認証を推奨)

5.4 GitHub トークン

  • GITHUB_TOKEN: 自動提供(明示的な設定不要)

5.5 オプション

  • DEVIN_API_KEY: Devin API キー(使用する場合のみ)

6. エラーハンドリング

6.1 基本方針

  • シンプルに: ジョブ失敗 = チェック失敗
  • 複雑なリトライロジック不要: lock-timeout で対応
  • ログは標準出力: GitHub Actions ログで確認

6.2 ロック発生時

対応:

  1. TF_CLI_ARGS_* でタイムアウトを 5 分に設定(基本対応)
  2. concurrency で同一リソースの並行実行を防止
  3. それでも失敗したら手動で確認

手動確認:

# ロック情報確認
yarn infra status <tenant> <environment>

# 必要に応じて解除
yarn infra unlock <tenant> <environment> <lock-id> --force

6.3 Resource Import 失敗時

方針: 手動対応を基本とする(自動化は危険)

手順:

  1. エラーログからリソースアドレスを特定
  2. Azure CLI で実体 ID を取得
  3. terraform import 実行
  4. 再度 plan/apply

詳細は後述の「Resource Import 対応」を参照。

7. セキュリティ考慮事項

7.1 認証

  • 推奨: OIDC を使用(シークレット不要)
  • Service Principal は最小権限
  • シークレットのローテーション

7.2 承認フロー(オプション)

本番環境に手動承認を追加する場合:

apply:
environment: production # GitHub Environments 設定で手動承認を要求

8. パフォーマンス最適化(オプション)

8.1 キャッシュ

- uses: actions/cache@v4
with:
path: |
~/.cache/yarn
~/.terraform.d/plugin-cache
key: ${{ runner.os }}-terraform-${{ hashFiles('**/yarn.lock') }}

8.2 実行時間短縮

実装済み:

  • ✅ 環境ごとの最適化実行: config.tomlenabled: true の環境のみ実行
  • ✅ 変更されたテナントのみ検出: infra detect-changes コマンドで自動検出

将来の拡張案:

  • modules 依存解析(影響のあるテナントのみ)

9. 実装ステップ

Phase 1: MVP(最小機能)

  1. .github/workflows/terraform-ci.yml を作成

    • detect-changes ジョブ
    • plan ジョブ(PR 時)
  2. .github/workflows/terraform-apply.yml を作成(手動実行用)

    • apply ジョブ(workflow_dispatch)
  3. GitHub Secrets を設定

    必須のシークレット:

    • TERRAFORM_AZURE_CLIENT_ID (Azure 認証 - Service Principal ID)
    • TERRAFORM_AZURE_TENANT_ID (Azure 認証 - Azure Tenant ID)
    • TERRAFORM_AZURE_SUBSCRIPTION_ID (Azure 認証 - Azure Subscription ID)
    • DOTENV_PRIVATE_KEY_DEV (開発環境復号キー - monolith テンプレート用)
    • DOTENV_PRIVATE_KEY_PRD (本番環境復号キー - monolith テンプレート用)
    • DOTENV_PRIVATE_KEY_FRONTEND_DEV (フロントエンド開発環境復号キー)
    • DOTENV_PRIVATE_KEY_FRONTEND_PRD (フロントエンド本番環境復号キー)
    • DOTENV_PRIVATE_KEY_BACKEND_DEV (バックエンド開発環境復号キー)
    • DOTENV_PRIVATE_KEY_BACKEND_PRD (バックエンド本番環境復号キー)

    注意: ARM_ACCESS_KEY は使用しません(OIDC + Azure AD 認証を使用)

    オプション:

    • DEVIN_API_KEY (Devin API 使用時)
  4. テスト PR で動作確認

目標: 基本的な CI/CD が動作する

Phase 2: 安定化(必要に応じて)

  1. キャッシュの追加
  2. エラーメッセージの改善
  3. 通知機能(Slack 等)

Phase 3: 最適化(将来)

  1. ✅ 環境ごとの最適化実行(実装済み: config.tomlenabled 設定を使用)
  2. PR コメントでの plan 結果表示
  3. modules 依存解析

ファイル構成

.github/
└── workflows/
├── terraform-ci.yml # PR時の自動plan実行
└── terraform-apply.yml # 手動実行用のapplyワークフロー

terraform/
└── environments/
└── tenants/
├── code-agent/
├── excelence/
└── ...

特徴:

  • PR 時の plan は自動実行
  • Apply は手動実行のみ(安全性のため)
  • 複雑なスクリプトファイルは不要

Resource Import 対応(失敗時)

方針

自動化は限定的。手動対応を基本とする。

手順

  1. エラー特定

    Error: resource already exists
    Resource: module.app.azurerm_container_app.this
  2. ID 取得(命名規則から推定)

    # リソースグループ
    az group show -n rg-<tenant>-<env> --query id -o tsv

    # Container Apps
    az containerapp show \
    -n ca-<tenant>-<component>-<env> \
    -g rg-<tenant>-<env> \
    --query id -o tsv
  3. Import 実行

    cd terraform/environments/tenants/<tenant>/<env>
    terraform import "<address>" "<id>"
  4. 確認

    yarn infra plan <tenant> <env>
    # 差分がないことを確認

注意

  • import は state に影響するため、main マージ前に PR で確認
  • 命名規則: core/src/lib/azure-resource-naming.ts 参照

注意事項とベストプラクティス

必須事項

  1. State ロック: concurrency で同一リソースの並行実行を防止
  2. 暗号化キー: DOTENV_PRIVATE_KEY_* シークレットの設定(環境とコンポーネントごと)
  3. Azure 認証: OIDC 認証を使用(TERRAFORM_AZURE_* シークレットが必要)
  4. Service Principal ロール: Storage Blob Data Contributor ロールが必要(Terraform State 用)
  5. 生成ファイル: .tf ファイルは自動生成されるため手動編集禁止
  6. Permissions: OIDC 認証のために id-token: write 権限が必要

推奨事項

  1. 段階的導入: まずは MVP で動作確認してから拡張
  2. ログ確認: 失敗時は GitHub Actions のログを確認
  3. plan 必須: 本番環境の apply 前は必ず plan を確認
  4. 命名規則遵守: Azure リソース名は統一された命名規則に従う

制限事項

  1. テナント数: 5-6 個程度を想定(規模拡大時は最適化検討)
  2. 実行時間: 有効な環境のみ実行されるため、テナント × 有効な環境数に比例
  3. 並行実行: plan ジョブは並列実行、apply は手動実行のため 1 件ずつ

トラブルシューティング

ロックエラー

症状: Error acquiring the state lock

対処:

  1. 5 分待機後に自動リトライ(lock-timeout 設定済み)
  2. それでも失敗したら yarn infra status <tenant> <env> で確認
  3. 必要に応じて yarn infra unlock <tenant> <env> <lock-id> --force

認証エラー

症状: Error: authentication failed

対処:

  1. GitHub Secrets の設定を確認
  2. Azure Service Principal の権限を確認
  3. OIDC の場合、Federated Credentials の設定を確認

Plan 失敗

症状: plan が失敗する

対処:

  1. ローカルで yarn infra plan <tenant> <env> を実行
  2. config.toml の設定を確認
  3. 暗号化された環境変数ファイルの確認

将来の拡張案(必要になったら)

優先度: 中

  • PR コメントでの plan 結果表示
  • Slack/Teams への通知
  • 環境ごとの最適化実行(実装済み: config.tomlenabled 設定を使用)

優先度: 低

  • modules 依存解析
  • import 半自動化
  • 詳細なエラー分類
  • カスタムメトリクス収集

関連ドキュメント