静的キーレス CI/CD ワークフロー実装 — git push から本番反映まで

前記事で OIDC + IAM Role の準備ができたので、いよいよ 「git push したら自動デプロイ」 のワークフローを書きます。本記事では .github/workflows/frontend-deploy.yml を 1 行ずつ解説していきます。

ワークフロー全文

name: frontend-deploy

on:
  push:
    branches: [main]
    paths:
      - 'frontend/**'
      - '.github/workflows/frontend-deploy.yml'
  workflow_dispatch: {}

permissions:
  id-token: write
  contents: read

concurrency:
  group: frontend-deploy-${{ github.ref }}
  cancel-in-progress: true

env:
  AWS_REGION: ap-northeast-1
  AWS_ROLE_TO_ASSUME: arn:aws:iam::XXXXXXXXXXXX:role/iigtn-github-actions-deploy
  S3_BUCKET: iigtn-lab-web-prod-XXXXXXXXXXXX
  CF_DISTRIBUTION_ID: EXXXXXXXXXXXXX
  SITE_DIR: frontend
  FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: 'true'

jobs:
  deploy:
    runs-on: ubuntu-latest
    timeout-minutes: 10

    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Configure AWS credentials (OIDC)
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: ${{ env.AWS_ROLE_TO_ASSUME }}
          role-session-name: gha-frontend-deploy-${{ github.run_id }}
          aws-region: ${{ env.AWS_REGION }}

      - name: Verify caller identity
        run: aws sts get-caller-identity

      - name: Deploy to S3
        run: |
          aws s3 sync ./${{ env.SITE_DIR }}/ s3://${{ env.S3_BUCKET }}/ \
            --delete \
            --exclude ".git/*" \
            --exclude "*.md" \
            --exclude ".gitignore" \
            --exclude ".gitkeep"

      - name: Invalidate CloudFront
        run: |
          INV_ID=$(aws cloudfront create-invalidation \
            --distribution-id ${{ env.CF_DISTRIBUTION_ID }} \
            --paths "/*" \
            --query 'Invalidation.Id' --output text)
          aws cloudfront wait invalidation-completed \
            --distribution-id ${{ env.CF_DISTRIBUTION_ID }} \
            --id "$INV_ID"

      - name: Verify deployment
        run: curl -sI https://lab.iigtn.com/ | head -10

各セクション解説

1. on: — トリガー条件

on:
  push:
    branches: [main]
    paths:
      - 'frontend/**'
      - '.github/workflows/frontend-deploy.yml'
  workflow_dispatch: {}

2. permissions: — Job が要求する権限

permissions:
  id-token: write   # OIDC token 発行に必須
  contents: read    # actions/checkout でコードを clone するのに必要

デフォルトでは GitHub Actions の GITHUB_TOKEN はリポジトリへの広い権限を持ちますが、明示的に絞る ことでセキュリティを強化。issues / pull-requests への書き込みは要らないので最小権限。

3. concurrency: — 同時実行制御

concurrency:
  group: frontend-deploy-${{ github.ref }}
  cancel-in-progress: true

同じ main ブランチで連続 push した時、古い実行をキャンセルして最新だけ走らせる。デプロイレースが起きません。

4. env: — グローバル環境変数

env:
  AWS_REGION: ap-northeast-1
  AWS_ROLE_TO_ASSUME: arn:aws:iam::XXXXXXXXXXXX:role/...
  S3_BUCKET: iigtn-lab-web-prod-XXXXXXXXXXXX
  CF_DISTRIBUTION_ID: EXXXXXXXXXXXXX
  SITE_DIR: frontend
  FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: 'true'

ハードコードされた ARN や ID はここに集約。変更する時はこの 1 ブロックだけ触ればいい設計。

最後の FORCE_JAVASCRIPT_ACTIONS_TO_NODE24 は、Node.js 20 の deprecation 対応。actions/checkout@v4 や aws-actions/configure-aws-credentials@v4 を強制的に Node.js 24 で動かす。

5. Step: Checkout

- name: Checkout
  uses: actions/checkout@v4

リポジトリのソースを Runner の作業ディレクトリに clone。

6. Step: OIDC で AWS 認証

- name: Configure AWS credentials (OIDC)
  uses: aws-actions/configure-aws-credentials@v4
  with:
    role-to-assume: ${{ env.AWS_ROLE_TO_ASSUME }}
    role-session-name: gha-frontend-deploy-${{ github.run_id }}
    aws-region: ${{ env.AWS_REGION }}

裏側の挙動:

  1. GitHub に「OIDC token 発行して」を要求 → JWT 受信
  2. JWT を STS の AssumeRoleWithWebIdentity に渡す
  3. 1 時間有効の AccessKey/SecretKey/SessionToken を環境変数として注入

role-session-namegithub.run_id を入れることで、CloudTrail で「どのワークフロー実行か」が追跡可能になります。

7. Step: 確認

- name: Verify caller identity
  run: aws sts get-caller-identity

「自分が誰として動いているか」を表示。デバッグ用途。本番では削っても良いが、初回は入れておくと安心。

8. Step: S3 sync

- name: Deploy to S3
  run: |
    aws s3 sync ./${{ env.SITE_DIR }}/ s3://${{ env.S3_BUCKET }}/ \
      --delete \
      --exclude ".git/*" \
      --exclude "*.md" \
      --exclude ".gitignore" \
      --exclude ".gitkeep"

aws s3 sync はディレクトリ間の差分同期。重要オプション:

--delete は強力。frontend/ 配下が空のままで実行すると S3 が全削除されます。空のリポジトリ初回 push 等で事故が起きやすいので注意。

9. Step: CloudFront 無効化

- name: Invalidate CloudFront
  run: |
    INV_ID=$(aws cloudfront create-invalidation \
      --distribution-id ${{ env.CF_DISTRIBUTION_ID }} \
      --paths "/*" \
      --query 'Invalidation.Id' --output text)
    aws cloudfront wait invalidation-completed \
      --distribution-id ${{ env.CF_DISTRIBUTION_ID }} \
      --id "$INV_ID"

/* 全体無効化を 1 path カウントで実行。月 1,000 paths 無料枠内に余裕。

aws cloudfront wait で完了まで待ち、後続 step で確実に新版が見られるようにする。

10. Step: デプロイ確認

- name: Verify deployment
  run: curl -sI https://lab.iigtn.com/ | head -10

本番 URL に HTTPS GET して、レスポンスヘッダを表示。HTTP 200 が返れば成功。失敗ヘッダなら GitHub Actions の job が赤くなります。

初回 push の動き

git push 後、GitHub Actions のページで実行を見ると、各 step が順に green で完了します。本シリーズの初回実行は 54 秒 で完了。

✓ Set up job
✓ Checkout
✓ Configure AWS credentials (OIDC)
✓ Verify caller identity
✓ Deploy to S3
✓ Invalidate CloudFront
✓ Verify deployment
✓ Complete job

失敗時の典型例

症状原因対処
OIDC step で AssumeRole 失敗Trust Policy の sub 不一致repo:owner/repo:ref:refs/heads/main 表記が正しいか確認
S3 sync で AccessDeniedPermissions Policy の resource ARN ミスバケット ARN と /* 付加が正しいか確認
CloudFront invalidation 失敗Distribution ID ミス or 権限不足env の ID と Permissions Policy 確認
Verify step で 5xxOrigin (S3) で問題発生 / OAC 設定ミスS3 の中身を aws s3 ls で確認

本シリーズで使うワークフロー数

現状は frontend-deploy.yml の 1 個だけ。今後追加予定:

原則は「1 ワークフロー = 1 責務」。フロント / バック / Terraform を別ワークフローにすることで、片方の失敗が他に波及しない。

次の記事

静的サイトのデプロイ自動化が完了しました。次の記事から、いよいよ 動的バックエンド(API Gateway + Lambda + DynamoDB)の構築に入ります。問い合わせフォームを作って、ブラウザから POST されたデータを Lambda で処理し、DynamoDB に保存する、という流れです。

📚 用語集

GitHub Actions
GitHub 上で動く CI/CD サービス。ワークフローを YAML で書き、push や手動トリガーで実行する。
workflow (ワークフロー)
.github/workflows/*.yml ファイル 1 つ = 1 ワークフロー。複数の Job を順次・並行で実行できる。
job
ワークフロー内の独立した実行単位。Runner 1 台で動く。
step
Job 内の 1 ステップ。コマンド or アクション。
Action
再利用可能な処理ユニット。actions/checkout@v4 等、コミュニティや公式が提供している。
Runner
ワークフローを実行するサーバ。GitHub-hosted Runner(無料枠あり)/ self-hosted Runner(自分のサーバ)。
on (ワークフロートリガー)
ワークフローが起動する条件。push / pull_request / schedule / workflow_dispatch(手動)等。
paths filter
on.push.paths で指定する起動パスフィルタ。指定したパスのファイルが変更された時だけワークフローを起動。
workflow_dispatch
手動実行トリガー。GitHub UI に「Run workflow」ボタンが追加される。
permissions (ワークフロー)
Job が要求する GITHUB_TOKEN の権限。最小権限で書くのが推奨。
concurrency
同時実行制御の設定。同じ group の実行を 1 つだけにする等の制御。
env (グローバル環境変数)
ワークフロー全体で使える環境変数。${{ env.NAME }} で参照。
aws s3 sync
AWS CLI の S3 ディレクトリ同期コマンド。--delete でローカル不在ファイルを S3 から削除(完全ミラー)。
aws cloudfront create-invalidation
CloudFront のキャッシュを無効化する CLI コマンド。--paths "/*" で全パス無効化。
aws cloudfront wait invalidation-completed
無効化完了まで待機する CLI コマンド。CI/CD で「無効化が終わってから次の step」を保証するのに使う。
github.run_id
GitHub Actions が実行ごとに払い出す一意 ID。CloudTrail との突き合わせに使う。
role-session-name
AssumeRole 時の session 名。CloudTrail でこの名前で session が記録される。
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24
Node.js 20 で動く Action を Node.js 24 で強制実行する環境変数。Node.js 20 の deprecation 対応。
--exclude (aws s3 sync)
同期対象から除外するパターン指定。複数指定可。.git.md ファイルを S3 に置きたくない時に使う。