homeIcon

【CI/CD編】プロフィールサイトAWS移行メモ 〜インフラ初心者を添えて〜

インフラ
2025.09.15
2025.09.15

はじめに

この記事では、プロフィールサイトAWS移行の最終回として GitHub Actions を用いたCI/CDの構築をしていきたいと思います。
アーキテクチャ図では、以下の部分を構築します。

画像1

やりたいこととしては、主に以下の2つになります。

  • GitHub Actions を用いた S3 へのデプロイ自動化
  • microCMS の Webhook 連携を使用した GitHub Actions の自動実行

また、上記を行う際に IAM OIDC IDプロバイダーを使用した AssumeRole によるAWSアクセスを実現し、セキュリティー面も考慮していますので併せて記載します。

OIDC ロールの作成

まずは AWS CDK に OIDC ロールの作成を行うコードを追加していきます。
追加するファイルは以下になります。

My_profile-cdk/
...
├── lib/
│   ...
│   └── stacks/
│   │   ...
│   │   └── cicd-oidc-role-stack.ts
│   ...
├── parameters/
│   ...
│   └── oidc-parameter.ts
...

OIDC ロールとは?

OIDC ロールとは長期アクセスキーを使わずに、GitHub Actions などの外部サービスから AWS の一時的な権限を安全に取得する仕組みのことです。
主に以下のような利点がります。

  • 秘密鍵いらず:リポジトリやCIに長期アクセスキーを置かないため、漏洩リスク激減になる。
  • 自動ローテーション:毎回使い捨ての短命キーを使用する。
  • 最小権限:ロールに必要最小限の権限だけ付けることが可能。
  • 細かい制限:トークンの中身(claims)で repo/branch 単位に実行元を縛れる。

OIDC フェデレーション - AWS Identity and Access Management

AWS 上で実行しないアプリケーションが AWS にアクセスするための一時的な AWS セキュリティ認証情報を作成します。

docs.aws.amazon.com

no-image

AssumeRole + OIDC方式の全体フロー

以下はGitHub ActionsからS3バケットへアクセスする際のフロー図です。

画像2

CDK コードの実装

GitHub Actions から OIDC で 最小権限のデプロイ用ロールを払い出し、指定の S3 バケットにアップロード&CloudFront のキャッシュ無効化だけを許可するスタックになっています。
具体的には以下の処理を行っています。

  1. デプロイ対象ブランチの決定。
  2. GitHub OIDC プロバイダの用意。
    ・既存の OIDC Provider ARN があればそれを利用し、なければ新規作成。
  3. 信頼ポリシーの設定
  4. GitHub Actions 用 IAM ロールの作成
  5. S3, CloudFront に対する最小権限を設定。
    S3:バケットの一覧取得(s3:ListBucket)、オブジェクトの作成/削除(s3:PutObject, s3:DeleteObject
    CloudFrontcloudfront:CreateInvalidationを 対象ディストリビューション ARN に限定して付与。
  6. GitHub Actions の role-to-assume で使うロール ARN を出力。
/lib/stacks/cicd-oidc-role-stack.ts
import * as cdk from "aws-cdk-lib";
import { Construct } from "constructs";
import * as iam from "aws-cdk-lib/aws-iam";
import { CicdOidcRoleStackProperty } from "@/parameters/oidc-parameter";
 
export interface CicdOidcRoleStackProps
  extends cdk.StackProps,
    Omit<CicdOidcRoleStackProperty, "env"> {
  bucketName: string;
  distributionId: string;
}
 
export class CicdOidcRoleStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props: CicdOidcRoleStackProps) {
    super(scope, id, props);
 
    const branch = props.props.gitHub.branch ?? "main"; // 未指定なら main 限定
 
    // GitHub OIDC Provider(既存再利用 or 新規作成)
    const provider = props.props.useExistingProviderArn
      ? iam.OpenIdConnectProvider.fromOpenIdConnectProviderArn(
          this,
          "GithubOidcProviderImported",
          props.props.useExistingProviderArn
        )
      : new iam.OpenIdConnectProvider(this, "GithubOidcProvider", {
          url: "https://token.actions.githubusercontent.com",
          clientIds: ["sts.amazonaws.com"],
        });
 
    // OIDC 信頼条件(repo/branch を厳密化)
    const principal = new iam.OpenIdConnectPrincipal(provider).withConditions({
      StringEquals: {
        "token.actions.githubusercontent.com:aud": "sts.amazonaws.com",
      },
      StringLike: {
        "token.actions.githubusercontent.com:sub": `repo:${props.props.gitHub.owner}/${props.props.gitHub.repo}:ref:refs/heads/${branch}`,
      },
    });
 
    // IAM Role(GitHub Actions 用)
    const role = new iam.Role(this, "GithubActionsDeployerRole", {
      assumedBy: principal,
      description:
        "OIDC for GitHub Actions: least-privilege deploy to S3 + CloudFront invalidation",
      roleName: "github-actions-oidc-deployer",
    });
 
    // S3 最小権限(対象バケット限定)
    role.addToPolicy(
      new iam.PolicyStatement({
        actions: ["s3:ListBucket"],
        resources: [`arn:aws:s3:::${props.bucketName}`],
      })
    );
    role.addToPolicy(
      new iam.PolicyStatement({
        actions: ["s3:PutObject", "s3:DeleteObject"],
        resources: [`arn:aws:s3:::${props.bucketName}/*`],
      })
    );
 
    // CloudFront 無効化(Distribution 限定)
    role.addToPolicy(
      new iam.PolicyStatement({
        actions: ["cloudfront:CreateInvalidation"],
        resources: [
          `arn:aws:cloudfront::${cdk.Stack.of(this).account}:distribution/${
            props.distributionId
          }`,
        ],
      })
    );
 
    // 出力(GitHub Actions 側で使う Assume Role ARN)
    new cdk.CfnOutput(this, "DeployerRoleArn", {
      value: role.roleArn,
      description: "AssumeRole ARN for GitHub Actions (role-to-assume)",
    });
 
    // OIDC Provider の ARN を出力
    new cdk.CfnOutput(this, "OidcProviderArn", {
      value: provider.openIdConnectProviderArn,
      description: "GitHub OIDC Provider ARN referenced by this stack",
    });
  }
}

デプロイ

これまでと同様に、「テンプレート生成 → 差分確認 → デプロイ」の手順で追加したリソースをデプロイします。

$ cdk synth
$ cdk diff MyProfileCdkStage/*
$ cdk deploy MyProfileCdkStage/DnsStack \
             MyProfileCdkStage/EdgeCertStack \
             MyProfileCdkStage/MailApiStack \
             MyProfileCdkStage/WebsiteStack \
             MyProfileCdkStage/CicdOidcRoleStack \
             --concurrency 1

ここで、デプロイした際に出力される「DeployerRoleArn」をメモしておきます。

S3 へのデプロイワークフロー

GitHub Secrets の設定

GitHub Actions が使用する環境変数を設定します。
Next.jsプロジェクトが使用する環境変数と併せて、先ほどメモした「DeployerRoleArn」も設定します。

画像3

deploy.ymlの作成

.github/workflows/ ディレクトリに GitHub Actions のワークフローを定義していきます。

処理内容

  • on
    • pushmain ブランチに push されたら実行。
    • workflow_dispatch:Actions 画面から 手動実行も可。
  • permissions
    • id-token: write:OIDC トークン発行を許可(AssumeRole 用)。
    • contents: read:リポジトリをチェックアウトする権限。
  • defaults.run.working-directory
    • 以降の run: は my_profile ディレクトリ配下で実行。
  • concurrency
    • deploy-static グループで 同時実行を1つに制限。新しい実行が来たら 古い実行をキャンセル(重複デプロイ防止)。
  • env:ビルド&デプロイ時に使う環境変数を設定。
  • jobs.deploy.steps
    1. Checkout:リポジトリのソースを取得。
    2. Setup Node:依存キャッシュ対象を my_profile/package-lock.json に固定して npm キャッシュを有効化。
    3. Install depsnpm ci でクリーンインストール。
    4. Buildnpm run build:static を実行(Next.js を静的書き出し)。
    5. Configure AWS credentialsrole-to-assume に Secrets の AWS_ROLE_ARN を指定して STS で一時クレデンシャル取得(先ほど作成した OIDC ロールをここで引き受ける)。
    6. Sync to S3:ローカルと S3 のファイルを一致させ、アップロード。
    7. Invalidate CloudFront/* を 全無効化して最新ファイルを即時配信。
.github/workflows/deploy.yml
name: Deploy static site to S3
 
on:
  push:
    branches: [main]
 
  workflow_dispatch:
 
permissions:
  id-token: write
  contents: read
 
defaults:
  run:
    working-directory: my_profile
 
concurrency:
  group: deploy-static
  cancel-in-progress: true
 
env:
  AWS_REGION: ${{ secrets.AWS_REGION }}
  S3_BUCKET: ${{ secrets.S3_BUCKET }}
  CLOUDFRONT_DISTRIBUTION_ID: ${{ secrets.CLOUDFRONT_DISTRIBUTION_ID }}
  MICROCMS_SERVICE_DOMAIN: ${{ secrets.MICROCMS_SERVICE_DOMAIN }}
  MICROCMS_API_KEY: ${{ secrets.MICROCMS_API_KEY }}
  MICROCMS_PREVIEW_SECRET: ${{ secrets.MICROCMS_PREVIEW_SECRET }}
  NEXT_PUBLIC_CONTACT_API_ENDPOINT: ${{ secrets.NEXT_PUBLIC_CONTACT_API_ENDPOINT }}
  NEXT_TELEMETRY_DISABLED: "1"
 
jobs:
  deploy:
    runs-on: ubuntu-latest
 
    steps:
      - name: Checkout
        uses: actions/checkout@v4
 
      - name: Setup Node
        uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: "npm"
          cache-dependency-path: "my_profile/package-lock.json"
 
      - name: Install deps
        run: npm ci
 
      - name: Build (static export via script)
        run: npm run build:static
 
      - name: Configure AWS credentials (OIDC)
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
          aws-region: ${{ env.AWS_REGION }}
 
      - name: Sync to S3
        run: |
          aws s3 sync ./out s3://$S3_BUCKET --delete --exact-timestamps
 
      - name: Invalidate CloudFront
        run: |
          aws cloudfront create-invalidation \
            --distribution-id "${CLOUDFRONT_DISTRIBUTION_ID}" \
            --paths "/*"

動作確認

では、作成したワークフローをmainにプッシュして、正常に Actions が実行されるか試してみます。

画像4

エラーなく実行されたことが確認できました!
AWS コンソールから S3 バケットにあるファイルの更新日時を確認すると最新のものになっていたので問題ないかと思います。

microCMS の Webhook 連携

ここからは、microCMSでコンテンツ更新時に、WebhookでGitHub Actionsを実行する設定を行っていきます。

GitHub側での設定

deploy.ymlの修正

先ほど作成したdeploy.ymlに、Webhook 連携のための設定を追加します。

  • on
    • repository_dispatchmicrocms_build イベントで実行(microCMS → GitHub API 経由のトリガ)。
.github/workflows/deploy.yml
name: Deploy static site to S3
 
on:
  push:
    branches: [main]
 
  repository_dispatch: 
    types: [microcms_build] 
 
  workflow_dispatch:
...

GitHubトークンの作成

そして、GitHub側で認証に必要なトークンを発行します。
※ ここでは Fine-grained tokens(Beta)で設定しています。

  1. GitHubのユーザー設定ページにアクセスします。
  2. ユーザー設定ページにて、左カラムの最下部にある「Developer settings」にアクセスします。
  3. 左カラムから「Personal access tokens」のメニューを押下し、Fine-grained tokensを押下します。
  4. トークン一覧画面にて「Generate new token」ボタンを押下します。
  5. 「New fine-grained personal access token」の画面で、以下の情報を入力します。
    Token name(必須):任意の名前を入力してください。
    Expiration(必須):任意の有効期限を選択してください。
    Description:任意の説明文を入力してください。
    Resource owner:連携したいリポジトリのオーナーを選択してください。
  6. 次に、「Repository access」セクションへ進み、「Only select repositories」を選択します。ここで連携したいリポジトリを選択してください。
  7. 次に、「Permissions」セクションへ進み、「Repository permissions」のメニューを展開します。ここで、「Contents」の権限に Read and write を付与します。なお、「Account permissions」の設定はデフォルトのままとします。
  8. ページ下部の「Generate token」ボタンを押下します。
  9. トークンの一覧画面に戻り、作成したばかりのトークンが表示されているので、コピーして保存しておきます。トークンの作成はここで終了です。

microCMS側での設定

次に、microCMS側での設定方法について、手順を示します。

  1. Webhookの設定をしたいAPIの「API設定」>「Webhook」画面にアクセスし、「+ 追加」ボタンを押下します。
  2. 次に、Webhook通知を行いたいサービスの選択画面が表示されるので、GitHub Actionsを選択します。
  3. GitHub Actionsの設定画面内の「基本設定」セクションにて、以下のように入力します。
    Webhookの識別名
    GitHubトークン:ここで、GitHub側で取得したアクセストークンを入力してください
    リポジトリのユーザー名
    リポジトリ名
    トリガーイベント名ワークフローファイル内の repository_dispatch 欄で定義した event_types の値を入力してください
  4. 次に「通知タイミングの設定」セクションにて、任意の通知タイミングを選択してください。
  5. 最後に「設定する」を押下し、設定は完了です。

動作確認

では、microCMS側でコンテンツを公開するとGitHub Actions が実行されるか試してみます。

画像5

無事に実行され、エラーなく完了していました!

さいごに

いかがでしたでしょうか。
ここまで、インフラ初心者がプロフィールサイトを Vercel から AWS へ移行した時の手順を備忘録として記載してきました。
初めは、資格勉強で得た知識だけを持っている状態でしたが、AWS移行を通して各サービスの詳細な設定内容や連携方法について学ぶことができました。
また、CDK を用いてインフラを定義したことで、IaC の概念についても理解することができ、より実践的な知識が得られたのではないかと感じています。
今回は、静的サイトを構築するための最低限の構成でしたが、今後は、さらに詳細な設定を行ったりバックエンドを持つインフラの構築など、よりレベルアップした内容にも挑戦していきたと思います。
最後までご覧いただきありがとうございました!

参考

GitHub ActionsでウェブサイトをAmazon S3にデプロイする | DevelopersIO

No description available.

dev.classmethod.jp

OGP Image

【コンテンツのWebhook連携】GitHub Actionsの設定方法について

microCMSではコンテンツの状態やAPI変更時にWebhookを発行し、外部のシステムと連携することができます。 このヘルプでは、コンテンツのWebhook連携におけるGitHub Actionsの設定方法について解説します。Webhook機能の詳細につきましては、microCMSドキュメント「コンテンツのWebhookを設定」をご覧ください。 GitHub Actionsと連携する際には、G

help.microcms.io

OGP Image

microCMSでコンテンツ更新時に、WebhookでGitHub Actionsを実行する

microCMSで記事を作成したり、編集・削除等更新した際にそのままサイトに反映されるようにWebh...

blog.tesoro-crea.com

OGP Image

【IAM OIDC IDプロバイダーの仕組みを見てみる】GitHub ActionsからAWSに一時認証情報を使用してアクセスする

No description available.

zenn.dev

OGP Image

【CI/CD】AssumeRole + OIDC方式の仕組みを整理してみた - Qiita

はじめに 以前、CI/CDパイプラインでAWSリソースにアクセスする際に「AssumeRole + OIDC方式」を使った仕組みを構築しました。手順通りに設定して動作することは確認できましたが、振り返ってみると仕組みについての理解があいまいなままでした。そこで、自分の理解...

qiita.com

OGP Image
Share