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.tomlでenabled: 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 IDTERRAFORM_AZURE_TENANT_ID: Azure Tenant IDTERRAFORM_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 ロック発生時
対応:
TF_CLI_ARGS_*でタイムアウトを 5 分に設定(基本対応)concurrencyで同一リソースの並行実行を防止- それでも失敗したら手動で確認
手動確認:
# ロック情報確認
yarn infra status <tenant> <environment>
# 必要に応じて解除
yarn infra unlock <tenant> <environment> <lock-id> --force
6.3 Resource Import 失敗時
方針: 手動対応を基本とする(自動化は危険)
手順:
- エラーログからリソースアドレスを特定
- Azure CLI で実体 ID を取得
terraform import実行- 再度 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.tomlのenabled: trueの環境のみ実行 - ✅ 変更されたテナントのみ検出:
infra detect-changesコマンドで自動検出
将来の拡張案:
- modules 依存解析(影響のあるテナントのみ)
9. 実装ステップ
Phase 1: MVP(最小機能)
-
.github/workflows/terraform-ci.ymlを作成- detect-changes ジョブ
- plan ジョブ(PR 時)
-
.github/workflows/terraform-apply.ymlを作成(手動実行用)- apply ジョブ(workflow_dispatch)
-
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 使用時)
-
テスト PR で動作確認
目標: 基本的な CI/CD が動作する
Phase 2: 安定化(必要に応じて)
- キャッシュの追加
- エラーメッセージの改善
- 通知機能(Slack 等)
Phase 3: 最適化(将来)
- ✅ 環境ごとの最適化実行(実装済み:
config.tomlのenabled設定を使用) - PR コメントでの plan 結果表示
- modules 依存解析
ファイル構成
.github/
└── workflows/
├── terraform-ci.yml # PR時の自動plan実行
└── terraform-apply.yml # 手動実行用のapplyワークフロー
terraform/
└── environments/
└── tenants/
├── code-agent/
├── excelence/
└── ...
特徴:
- PR 時の plan は自動実行
- Apply は手動実行のみ(安全性のため)
- 複雑なスクリプトファイルは不要
Resource Import 対応(失敗時)
方針
自動化は限定的。手動対応を基本とする。
手順
-
エラー特定
Error: resource already exists
Resource: module.app.azurerm_container_app.this -
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 -
Import 実行
cd terraform/environments/tenants/<tenant>/<env>
terraform import "<address>" "<id>" -
確認
yarn infra plan <tenant> <env>
# 差分がないことを確認
注意
- import は state に影響するため、main マージ前に PR で確認
- 命名規則:
core/src/lib/azure-resource-naming.ts参照
注意事項とベストプラクティス
必須事項
- State ロック:
concurrencyで同一リソースの並行実行を防止 - 暗号化キー:
DOTENV_PRIVATE_KEY_*シークレットの設定(環境とコンポーネントごと) - Azure 認証: OIDC 認証を使用(
TERRAFORM_AZURE_*シークレットが必要) - Service Principal ロール:
Storage Blob Data Contributorロールが必要(Terraform State 用) - 生成ファイル:
.tfファイルは自動生成されるため手動編集禁止 - Permissions: OIDC 認証のために
id-token: write権限が必要
推奨事項
- 段階的導入: まずは MVP で動作確認してから拡張
- ログ確認: 失敗時は GitHub Actions のログを確認
- plan 必須: 本番環境の apply 前は必ず plan を確認
- 命名規則遵守: Azure リソース名は統一された命名規則に従う
制限事項
- テナント数: 5-6 個程度を想定(規模拡大時は最適化検討)
- 実行時間: 有効な環境のみ実行されるため、テナント × 有効な環境数に比例
- 並行実行: plan ジョブは並列実行、apply は手動実行のため 1 件ずつ
トラブルシューティング
ロックエラー
症状: Error acquiring the state lock
対処:
- 5 分待機後に自動リトライ(
lock-timeout設定済み) - それでも失敗したら
yarn infra status <tenant> <env>で確認 - 必要に応じて
yarn infra unlock <tenant> <env> <lock-id> --force
認証エラー
症状: Error: authentication failed
対処:
- GitHub Secrets の設定を確認
- Azure Service Principal の権限を確認
- OIDC の場合、Federated Credentials の設定を確認
Plan 失敗
症状: plan が失敗する
対処:
- ローカルで
yarn infra plan <tenant> <env>を実行 config.tomlの設定を確認- 暗号化された環境変数ファイルの確認
将来の拡張案(必要になったら)
優先度: 中
- PR コメントでの plan 結果表示
- Slack/Teams への通知
- 環境ごとの最適化実行(実装済み:
config.tomlのenabled設定を使用)
優先度: 低
- modules 依存解析
- import 半自動化
- 詳細なエラー分類
- カスタムメトリクス収集