CI/CDのセットアップ③ (GitHub & Azure)

前回からの続きです。今回からは AWS の S3 というサービスに GitHub と Azure から CD (デプロイ) を行っていきます。
※(2025/10/02 追記) Azure Pipelines から S3 にデプロイする記事をまとめました。
今回の記事を作成するにあたり、引き続き以下の講座で勉強させていただきました。非常に分かりやすく大変勉強になりました。特に「OpenID Connect (OIDC)」と呼ばれる標準化仕様で GitHub に接続する方法がとても勉強になりました。
Udemy
GitHub Actionsで学ぶCI/CD入門―ビルド・デプロイの基本からAPI自動テスト・AWSへの自動デプロイまで
目次
今回の記事で使用している環境は以下の様になっています。
- VSCode・・・Windows版 1.104.2
- AWS CLI・・・バージョン 2.31.1
- AWS Toolkit for Azure DevOps・・・バージョン 1.22.0
AWS のアカウントを作成します
まず最初に AWS のアカウントを作成しておきます。公式の作成方法はこちらになっています。セキュリティのために、作業用のアカウントも作成しておいた方が安全と思われます。こちらのサイト様に初期アカウントの作成方法から、作業用のアカウントの作成方法まで詳しい手順が解説されています。よろしかったらご覧ください。
アカウント作成時の大まかな手順は以下の様な流れになります。
- AWS アカウントの作成 (ルート権限を持っています。)
- 権限を絞った、IAMユーザーと呼ばれる作業用ユーザーの作成
- それぞれのユーザーに多要素認証 (MFA) を設定
アカウントを作成した初期のユーザーはすべての権限を持っているので、権限を絞ったユーザー (IAM ユーザー) を作成して普段の作業はこのユーザーで行うようにします。またセキュリティのため、それぞれのユーザーに多要素認証 (MFA) を設定しておくと安全です。MFA を行うには、スマートフォンで動作する認証アプリが使用できます。
一例ですが、以下のアプリ等が使用できるようです。
- Google Authenticator
- Microsoft Authenticator
- Authy (Twilio Authy)
- 1Password(認証機能付き)
など
これらのアプリで、AWS の MFA 認証設定ページに表示された QR コードを読み取り、連続する 2 回分の乱数を入力すると紐づけされます。2 回目以降のログイン時は IAM ユーザー用の専用 URL にアクセスして、IAM ユーザー名とパスワードを入力すると認証番号を聞かれますので、スマートフォンにインストールされている認証アプリに表示されている認証番号を入力してログインを行います。
AWS CLI をインストールします
GitHub Actions や Azure Pipelines 等の CI/CD に必須ではありませんが、開発時等に手動で S3 等にデプロイできる様に、「AWS CLI」というコマンドラインツールをインストールしておきます。
公式のインストール方法のページはこちらになっています。Windows をお使いの場合は、こちらからインストーラーを直接ダウンロードできます。WSL2 をお使いの場合は以下の方法でインストールできます。
# 作業用のディレクトリを作成して移動します
mkdir aws-archive; cd aws-archive
# 必要なパッケージを更新します
sudo apt update
sudo apt install -y unzip curl
# AWS v2 をダウンロード
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
# ZIP ファイルを展開
unzip awscliv2.zip
# インストール
sudo ./aws/install
インストールが完了したら「aws --version」と入力するとバージョンが確認できます。
user01@DESKTOP-CQEA49P:~/aws-archive$ aws --version
aws-cli/2.31.1 Python/3.13.7 Linux/6.6.87.2-microsoft-standard-WSL2 exe/x86_64.ubuntu.24
user01@DESKTOP-CQEA49P:~/aws-archive$
更新や削除を行う場合は以下の様に入力します。
# 更新します
sudo ./aws/install --update
# 削除します
sudo ./aws/uninstall
AWS S3 バケットを作成します
※このセクションの説明は、2025年9月26日現在の設定方法に基づいています。ご覧いただいている時点では、画面構成が変更されている可能性があります。
AWS にログインして上部にある検索欄に「s3」と入力します。表示された候補から「S3」を選択します。リージョンと呼ばれるデータセンターの位置は「アジアパシフィック (東京)」か「アジアパシフィック (大阪)」のどちらか近いほうをお選びください。

表示されたページで「バケットを作成」をクリックします。

「バケット名」を入力します。バケット名は世界で一意になっている必要があります。入力が完了したら、ページの下部にある「バケットを作成」ボタンで作成します。

デフォルトでは、インターネットからアクセスできない様になっているので設定を変更します。新しく作成されたバケット名をクリックします。

「プロパティ」をクリックします。

表示されたページの一番下にある「静的ウェブサイトホスティング」欄にある「編集」をクリックします。

「静的ウェブサイトホスティング」を有効にして、インデックスドキュメント欄に「index.html」と入力します。入力が完了したら、ページの一番下にある「変更の保存」ボタンで保存します。

「アクセス許可」をクリックします。

「ブロックパブリックアクセス (バケット設定)」欄にある「編集」をクリックします。

「パブリックアクセスをすべて ブロック」のチェックをはずして、「変更の保存」をクリックします。

確認用のダイアログが表示されますので、入力欄に「確認」と入力して「確認」ボタンを押します。

次に「バケットポリシー」欄にある「編集」をクリックします。

表示された「ポリシー」欄に以下の設定を追加します。「<バケット名>」の部分は先ほど作成した S3 のバケット名 (mimura-soft 等) に置き換えてください。追加が完了したらページの下部にある「変更の保存」ボタンを押して保存します。
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicReadGetObject",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::<バケット名>/*"
}
]
}

以上で公開準備は完了です。確認のため再度バケット画面の「プロパティ」をクリックします。

ページの一番下にある「静的ウェブサイトホスティング」欄にある URL をクリックすると作成した S3 バケットにアクセスできます。

実際にアクセスしてみると、まだ何もコンテンツが無いので「404 Not Found」が返ってきます。もし「403 Forbidden」等が表示される場合は、インターネットに公開されていない状態なのでもう一度設定をご確認ください。(余談ですが、他に何かファイル等がある場合でもリストが表示されることはない様です。)

手動でコンテンツを S3 にアップロードします
・サンプルアプリをビルドします
まずはアップロードするサンプルアプリをビルドします。このシリーズの 1 回目 で作成したアプリを使用します。プロジェクトフォルダ内にある「vite.config.ts」というファイルを以下の様に変更します。
import { defineConfig } from "vitest/config";
import react from "@vitejs/plugin-react";
// https://vite.dev/config/
export default defineConfig({
plugins: [react()],
base: "/", // デプロイ先がサブパスなら '/repo/' 等に変更
build: {
outDir: "dist", // 出力先(デフォルト 'dist')
assetsDir: "assets", // 静的アセット格納先
emptyOutDir: true, // ビルド前に dist を削除
},
test: {
environment: "jsdom",
globals: true,
},
});
修正が完了したら、Ctrl + @ と入力してターミナル画面を開いて、「npm run build」と入力すると「dist」というフォルダ内にビルド成果物 (アーティファクト) が作成されます。後はこの dist フォルダの中身を AWS CLI で S3 に転送すればデプロイが完了します。
PS C:\MAMP\htdocs\ci_cd_projects\myapp> npm run build
> myapp@0.0.0 build
> tsc -b && vite build
vite v7.1.5 building for production...
✓ 30 modules transformed.
dist/index.html 0.46 kB │ gzip: 0.30 kB
dist/assets/index-D8b4DHJx.css 1.39 kB │ gzip: 0.71 kB
dist/assets/index-B1oX7L-6.js 187.36 kB │ gzip: 58.90 kB
✓ built in 803ms
PS C:\MAMP\htdocs\ci_cd_projects\myapp>
・グループとユーザーを作成します
次に、ローカルの AWS CLI コマンドで AWS に接続するために「OpenID Connect (OIDC)」を使用できる様に AWS を設定して行きます。(OIDC に関してはこちらのサイト様の説明がとても分かりやすかったです。OIDC は OAuth2 と呼ばれる認可プロトコルに認証機能を追加した拡張プロトコルになっています。シングルサインオン (SSO) という仕組みを実現する主流の技術になっています。OIDC を使用するには AWS CLI v2 以上が推奨されています。)
設定のおおまかな手順としては
- グループの作成
- ユーザーを作成してグループに追加
- 権限 (許可セット) の作成
- AWS のアカウントにグループと許可セットの紐づけ
という流れになっています。
まずAWS のコンソールの上部の検索欄に「iam id」等と入力して、表示されたリストから「IAM identity Center」を選択します。設定がややこしいので、できる限りすべてのスクリーンショットを添付します。

「有効にする」ボタンを押します。

次の画面で現在のリージョンを確認して「有効にする」ボタンを押します。

左側のサイドメニューから「グループ」を選択して「グループを作成」ボタンを押します。

任意のグループ名を入力して「グループを作成」ボタンを押します。

作成されたグループ名をクリックします。

「ユーザーをグループに追加」をクリックします。

「新しいユーザーを追加」をクリックします。

「ユーザー名」、「メールアドレス」と「氏名」を入力して、ページの下部にある「次へ」ボタンを押します。

先ほど作成したグループをチェックして「次へ」をクリックします。

確認画面が表示されますので、ページの下部にある「ユーザーを追加」ボタンを押してユーザーを作成します。

作成が完了すると登録されたメールアドレスに「Invitation to join AWS IAM Identity Center」というメールが送信されますので、本文にある「Accept invitation」というリンクをクリックして認証を行います。パスワードはまだ設定していないので、任意のパスワードを入力します。

認証が完了すると再度サインイン画面が表示されますので、先ほど作成したユーザー名でログインします。

認証が成功すると、多要素認証の設定画面が表示されますので「認証アプリ」を選択して次へボタンを押します。

任意の認証アプリを使用して、「Show QR code」をクリックして表示された QR コードを認証アプリで読み取ります。「認証コード」欄にアプリで表示された乱数を入力して「MFAを割り当て」ボタンを押すと多要素認証が設定されます。

認証が完了したら「完了ボタン」を押します。

・権限 (許可セット) を作成します
作成したグループ用の権限 (許可セット) を作成します。IAM Identity Center のサイドメニューから「許可セット」を選択して、表示された画面の「許可セットを作成」ボタンを押します。

「カスタム許可セット」を選択して「次へ」ボタンを押します。

「インラインポリシー」をクリックします。

表示されたエディタ画面に以下の設定を貼り付けます。12 行目と 17 行目はご自身のバケット名に置き換えてください。
ポリシーの設定内容は
- 登録されているバケットの一覧を取得
- 自分のバケットにあるオブジェクトの一覧を取得
- 自分のバケットへの読み書きと消去を許可
になっています。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "s3:ListAllMyBuckets",
"Resource": "*"
},
{
"Effect": "Allow",
"Action": "s3:ListBucket",
"Resource": "arn:aws:s3:::<バケット名>"
},
{
"Effect": "Allow",
"Action": ["s3:PutObject", "s3:GetObject", "s3:DeleteObject"],
"Resource": "arn:aws:s3:::<バケット名>/*"
}
]
}
ポリシーが設定できたら、ページの下部にある「次へ」ボタンを押します。

「許可セット名」を入力してページ下部にある「次へ」ボタンを押します。

確認画面が表示されますので、ページの下部にある「作成」ボタンを押します。

以上で許可セットの作成は終了です。
・AWS のアカウントにグループと許可セットを割り当てます
IAM Identity Center のサイドメニューから「AWS アカウント」を選択します。表示された画面から紐付けたい AWS アカウントを選択して「ユーザーまたはグループを割り当て」ボタンを押します。

先ほど登録したグループを選択して「次へ」ボタンを押します。

許可セットを選択して「次へ」ボタンを押します。

確認画面が表示されますので、「送信」ボタンを押します。

以上でグループと許可セットの紐づけは終了です。次は AWS CLI の設定を行っていきます。
・AWS CLI を設定します
VSCode を起動して、Ctrl + @ と入力してターミナル画面を開きます。ターミナル画面に以下のコマンドを入力します。
aws configure sso
最初にセッション名を聞かれますので、任意の名称を入力します。
SSO session name (Recommended): s3-deploy
次にシングルサインオン (SSO) の開始 URL を入力します。
SSO start URL [None]:
SSO の URL は「IAM Identity Center」の「設定」メニューで表示される画面の下の方にある「AWS access portal URL」の値を入力します。

次にリージョンを聞かれます。
SSO region [None]:
現在のリージョンは設定ページの上の方に表示されている「ap-」から始まる文字列になっています。

次に権限の範囲を聞かれますので、そのままリターンキーを押します。
SSO registration scopes [sso:account:access]:
ここまで入力するとブラウザーが起動してログイン処理を求められますので、IAM Identity Center で登録したユーザーでログイン処理を行います。

ログインが成功するとアクセスを許可するか確認する画面が表示されますので、「アクセスを許可」ボタンを押します。

承認画面が表示されますので、画面を閉じます。

VSCode に戻るとクライアント側のリージョンを聞かれますので、先ほどの「ap-」から始まる文字列を入力します。
Default client Region [None]:
CLI のデフォルトの出力フォーマットを聞かれますので、そのままリターンキーを押します。
CLI default output format (json if not specified) [None]:
プロファイル名を聞かれますので任意の文字列を入力します。この文字列は CLI コマンドを使用する際によく使う様なので、短い名称 (my-sso 等) にしておいた方がいいかもしれません。
Profile name [s3-deploy-permission-set-xxx...]:
以上で CLI の設定は終了です。設定ファイルはエクスプローラーを起動して URL 欄に「%USERPROFILE%\.aws」と入力して表示されたフォルダ内にある「config」というファイルに保存されています。
・コンテンツを S3 にアップロードします
準備が完了しましたので、コンテンツを手動で S3 にアップロードします。VSCode のターミナル画面で以下の様に入力します。
cd dist
aws s3 sync . s3://<バケット名>/ --profile <プロファイル名>
以下の様にファイルがアップロードされていれば成功です。
PS C:\MAMP\htdocs\ci_cd_projects\myapp> cd dist
PS C:\MAMP\htdocs\ci_cd_projects\myapp\dist> aws s3 sync . s3://mimura-soft/ --profile my-sso
upload: .\vite.svg to s3://mimura-soft/vite.svg
upload: assets\index-D8b4DHJx.css to s3://mimura-soft/assets/index-D8b4DHJx.css
upload: .\index.html to s3://mimura-soft/index.html
upload: assets\index-B1oX7L-6.js to s3://mimura-soft/assets/index-B1oX7L-6.js
PS C:\MAMP\htdocs\ci_cd_projects\myapp\dist>
AWS S3 の「汎用バケット」メニューからバケット内容を見てみるとアップロードされている様です。

確認のためプロパティページから実際の URL を開いてみると正常に表示されている様です。

GitHub Actions から S3 にデプロイします
GitHub からも OIDC (OpenID Connect) を使用して AWS に接続できるので設定していきます。OIDC 方式にするとアクセスキー方式の秘密鍵を自分で管理する (GitHub 上に保存する) 必要が無くなるのでセキュリティ的にも安心です。
このセクションの大まかな手順は以下の様な流れになります。
- ID プロバイダ (GitHub) を AWS に登録
- IAM ポリシーを作成
- IAM ロールを作成
- GitHub Actions の YAML ファイルを修正してデプロイ
AWS CLI を設定した時 (IAM Identity Center) と同じような設定を行いますが、GitHub 等からの接続時は MFA などの認証は行えないので AWS CLI とは別の「IAM」というサービスを使用します。
・ID プロバイダ (GitHub) を AWS に登録します
AWS のコンソールで上部にある検索欄に「iam」と入力して表示されたリストから「IAM」を選択します。

左側のサイドメニューから「ID プロバイダ」を選択して「プロバイダを追加」ボタンを押します。

「OpenID Connect」を選択します。ブラウザーでこちらのリンクを開いて、「プロバイダのURL」と「対象者」名をコピーして貼り付けて「プロバイダを追加」ボタンを押します。

AWS への ID プロバイダーの追加
GitHub OIDC プロバイダーを IAM に追加するには、AWS のドキュメントを参照してください。
プロバイダURLには https://token.actions.githubusercontent.com を指定します
「対象者」には sts.amazonaws.com を指定します。(公式のアクションを利用する場合)。
・IAM ポリシーを作成します
IAM ダッシュボードの左側のメニューから「ポリシー」を選択して、表示された画面の「ポリシーの作成」ボタンをクリックします。

ポリシーエディタの「JSON」をクリックして、表示されたエディタに以下の設定を追加します。7 行目と 12 行目のバケット名をご自身のバケット名に変更してください。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "s3:ListBucket",
"Resource": "arn:aws:s3:::<バケット名>"
},
{
"Effect": "Allow",
"Action": ["s3:PutObject", "s3:GetObject", "s3:DeleteObject"],
"Resource": "arn:aws:s3:::<バケット名>/*"
}
]
}
ポリシーを追加したらページの下部にある「次へ」ボタンを押します。

ポリシーの確認画面で任意のポリシー名を入力して、ページの下部にある「ポリシーの作成」ボタンを押します。

・IAM ロールを作成します
左側のメニューから「ロール」を選択して、表示された画面の「ロールを作成」ボタンをクリックします。

表示された画面で「カスタム信頼ポリシー」を選択してポリシーを追加します。

ポリシーは以下の様になっています。7 行目と 13 行目を修正します。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "<プロバイダのARN>"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"token.actions.githubusercontent.com:aud": "sts.amazonaws.com",
"token.actions.githubusercontent.com:sub": "repo:<GitHubのアカウント名>/<リポジトリ名>:ref:refs/heads/main"
}
}
}
]
}
プロバイダの ARN は「IAM」ページの「ID プロバイダ」メニューを選択して、先ほど作成したプロバイダ名 (token.actions.githubusercontent.com) をクリックすると表示されます。(余談ですが ARN とは Amazon Resource Name の略ですべてのリソース (オブジェクト等) に割り振られた、(世界で) 一意の ID になっている様です。)

ポリシーの 13 行目は GitHub のアカウント名とリポジトリ名を入力します。(リポジトリ名を「*」にするとアカウント内のすべてのリポジトリから使用できるようです。)

ページの下部にある「次へ」ボタンを押すとポリシーを追加する画面が表示されますので、先ほど作成したポリシーを検索してチェックしてから「次へ」ボタンを押します。

ロールの確認画面で任意のロール名を入力して、ページの下部にある「ロールを作成」ボタンを押します。

・GitHub Actions の YAML ファイルを修正してデプロイ
このシリーズの 1 回目の記事で作成した YAML ファイル (.github/workflows/ci.yml) を以下の様に修正します。64 行目には 先ほど AWS で作成したロールの ARN を記述します。いきなりこの様な設定を書くのは大変なので、ChatGPT に作成してもらいました。設定内容を詳しくお知りになりたい方は、公式のこちらのサイト様で検索すると対応するリポジトリを教えてもらえるようです。
name: CI
on:
push:
branches:
- main
jobs:
test-and-build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 24
cache: 'npm'
- run: npm ci
- run: npm run lint
- run: npm run test:run
- run: npm run coverage
- run: npm run build
# HTML レポートを成果物として保存(Artifacts)
- uses: actions/upload-artifact@v4
if: always()
with:
name: coverage-html
path: coverage
# ビルド成果物(dist)を一時保存(Artifacts)
- name: Upload build artifact (dist)
uses: actions/upload-artifact@v4
if: success()
with:
name: web-dist # ダウンロード側で同名を指定します
path: dist # 例: dist/** でもOK
if-no-files-found: error
retention-days: 3 # 必要に応じて短めに
deploy:
# CIが成功したらCDを実行
needs: test-and-build
runs-on: ubuntu-latest
# GitHub OIDCでAWSのロールを引き受ける場合に必要
permissions:
id-token: write
contents: read
steps:
# アーティファクトを取得
- name: Download build artifact
uses: actions/download-artifact@v4
with:
name: web-dist
path: dist
# (任意)中身の確認
- run: ls -la dist
# AWS認証(OIDC)— 事前に信頼ポリシーを設定したAssumeRoleを用意しておく
- name: Configure AWS credentials (OIDC)
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: <AWS で作成したロールの ARN (arn:aws:iam::xxx...:role/s3-github-deploy-role)>
aws-region: ap-northeast-1
# S3へデプロイ
- name: Sync to S3
run: |
aws s3 sync dist s3://mimura-soft/ --delete
実際に実行してみると、うまく動作している様です。

ローカル環境 (Self-hosted runner) でも問題ない様です。

Azure Pipelines から S3 にデプロイします
Azure Pipelines も GitHub と同様に OIDC (OpenID Connect) を使用して AWS に接続できるようなので設定していきます。こちらのサイト様で教えていただいた情報をもとに作成しています。OIDC 方式にするとアクセスキー方式の秘密鍵を自分で管理する (Azure DevOps 上に保存する) 必要が無くなるのでセキュリティ的にも安心です。
最初に Azure DevOps 側の設定が必要ですが、その後は GitHub とほぼ同じ流れになります。
- Azure DevOps の設定を行います
- ID プロバイダ (Azure Pipelines) を AWS に登録
- IAM ポリシーを作成
- IAM ロールを作成
- Azure Pipelines の YAML ファイルを修正してデプロイ
・Azure DevOps の設定を行います
まず最初に Azure DevOps に 「AWS Toolkit for Azure DevOps」という拡張機能をインストールします。こちらのリンクを開いて「Get it free」ボタンを押します。(Azure DevOps で AWS の OIDC を使用するには AWS Toolkit v1.5 以上が必要なようです。)

自分の Azure DevOps 組織が選択されているのを確認してから、「Install」ボタンを押します。

インストールが終了したら、Azure DevOps にログインして、連携したいプロジェクトを選択します。プロジェクト画面の左下にある「Project settings」メニューをクリックします。

左側にあるサイドメニューから「Service connections」を選択して、「Create service connection」ボタンを押します。

表示された画面で「AWS」を選択して「Next」ボタンを押します。

表示された画面を中ほどまでスクロールして、「Role to Assume (optional)」欄にAWS の ロールの ARN を設定します。AWS のロールを作成するには Azure DevOps の「組織のGUID」等の情報が必要で、現時点ではまだ作成できないので手動で作成します。その下にある「Role Session Name (optional)」は任意で入力します。省略された場合はデフォルトで「aws-vsts-tools」という文字列が使用されます。

AWS のロールの ARN は以下のようになっています。
arn:aws:iam::<AWSのアカウントID>:role/<ロール名>
AWS のアカウントID はマネジメントコンソール等の右上に表示されています。ハイフン (-) を取り除いた 12 桁の数値を使用します。アカウントID のドロップダウンを展開するとコピーボタンが表示されます。ロール名は任意の名称が使用できますが、後で AWS で作成するロール名と揃える必要があります。今回は例に倣って「azdo-federation」としました。

画面を下側までスクロールして、「Use OIDC (optional)」をチェックします。「Service Connection Name」欄に任意の名称を入力して「Save」ボタンを押します。

このシリーズの 2 回目で作成した Azure Pipelines 用の YAML ファイルを以下のように修正します。15 行目に作成した接続名 (Service Connection Name) を設定します。16 行目のリージョン名はお住まいの地域に合わせて変更してください。
trigger:
- main
pool:
vmImage: ubuntu-latest
variables:
aws.rolecredential.maxduration: "3600" # Can be set from 900 - 43200s
steps:
- task: AWSCLI@1
displayName: "Running aws-cli get-caller-identity"
# continueOnError: true # If you need the pipeline to succeed
inputs:
awsCredentials: "aws-oidc-federation"
regionName: 'ap-northeast-1'
awsCommand: 'sts'
awsSubCommand: 'get-caller-identity'
変更をコミットしてプッシュしたら、Azure DevOps で「Pipelines」を選択して、リストから実行中のパイプラインを選択します。

権限が必要と表示されていたら、「View」ボタンを押します。

表示された画面で「Permit」ボタンを押して、表示されたダイアログでさらに「Permit」ボタンを押します。

設定が完了していないのでジョブは失敗しますが、ログから設定に必要な情報を取得します。「Running aws-cli get-caller-identity」項目を選択して、表示されたログから「OIDC Token generated:」で始まる行をコピーしてメモ帳等に貼り付けておきます。

この行には以下のような情報が含まれています。
OIDC Token generated:
issuer: {https://vstoken.dev.azure.com/<組織のGUID>}
sub: {sc://mimura-soft/myapp/aws-oidc-federation},
aud: {api://AzureADTokenExchange}
・ID プロバイダ (Azure Pipelines) を AWS に登録
AWS のコンソールで上部にある検索欄に「iam」と入力して表示されたリストから「IAM」を選択します。

左側のサイドメニューから「ID プロバイダ」を選択して「プロバイダを追加」ボタンを押します。

「OpenID Connect」を選択します。「プロバイダのURL」欄には先ほど取得した「issuer」の値を入力します。「対象者」欄は「aud」の値「api://AzureADTokenExchange」を入力してページの下部にある「プロバイダを追加」ボタンを押します。

・IAM ポリシーを作成
IAM ダッシュボードの左側のメニューから「ポリシー」を選択して、表示された画面の「ポリシーの作成」ボタンを押します。

ポリシーエディタの「JSON」ボタンを押して、表示されたエディタに以下の設定を追加します。7 行目と 12 行目のバケット名をご自身のバケット名に変更してください。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "s3:ListBucket",
"Resource": "arn:aws:s3:::<バケット名>"
},
{
"Effect": "Allow",
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::<バケット名>/*"
}
]
}
ポリシーを追加したらページの下部にある「次へ」ボタンを押します。

ポリシーの確認画面で任意のポリシー名を入力して、ページの下部にある「ポリシーの作成」ボタンを押します。

・IAM ロールを作成
左側のメニューから「ロール」を選択して、表示された画面の「ロールを作成」ボタンを押します。

表示された画面で「ウェブアイデンティティ」を選択して、下にスクロールします。

「アイデンティティプロバイダー」と「Audience」のドロップダウンから先ほど作成した、IDプロバイダー名と Audience の値を選択して「条件を追加」ボタンを押します。

表示された条件で、キーを 先程ログに表示されていた「issuer」と同じ文字列で始まるものを選択して、条件を「StringEquals」にします。値にはログの「sub」項目で表示されていた「sc」から始まる文字列を入力します。入力が完了したら「次へ」ボタンを押します。

先ほど作成したポリシーを選択して「次へ」ボタンを押します。

最後に Azure DevOps で手動で作成したロール名「azdo-federation」を入力して、ページの下部にある「ロールを作成」ボタンを押します。

以上で AWS 側の設定は終了です。
・Azure Pipelines の YAML ファイルを修正してデプロイ
まずは先ほどの YAML ファイルが実行できるかテストしてみます。失敗したジョブを再度実行してみると今度は成功したようです。

シリーズ 2 回目で作成した Azure Pipelines 用の YAML ファイルを以下の様 (56 ~ 89 行目) に修正しました。
# azure-pipelines.yml
trigger:
- main
pr: none
jobs:
- job: test_and_build
displayName: "Test & Build"
pool:
vmImage: 'ubuntu-latest'
# Self-hosted runnerを使用
# name: "Default"
steps:
- checkout: self
fetchDepth: 0
- task: UseNode@1
displayName: "Use Node.js 24.x"
inputs:
version: "24.x"
checkLatest: true
- script: npm ci
displayName: "Install dependencies (npm ci)"
- script: npm run lint
displayName: "Lint"
- script: npm run test:run
displayName: "Test"
- script: npm run coverage
displayName: "Coverage"
- script: npm run build
displayName: "Build"
- task: CopyFiles@2
displayName: "Stage coverage to artifacts directory"
inputs:
SourceFolder: "$(Build.SourcesDirectory)/coverage"
Contents: "**"
TargetFolder: "$(Build.ArtifactStagingDirectory)/coverage-html"
# HTML レポートを成果物として保存(常に)
- task: PublishBuildArtifacts@1
displayName: "Publish coverage HTML as artifact"
condition: always()
inputs:
path: "$(Build.ArtifactStagingDirectory)/coverage-html"
artifactName: "coverage-html"
# dist をパイプライン成果物に
- task: PublishPipelineArtifact@1
displayName: 'Publish web-dist'
condition: succeeded()
inputs:
targetPath: 'dist'
artifact: 'web-dist'
- job: deploy
displayName: 'Sync dist to S3'
dependsOn: test_and_build # ← これで順序制御
condition: succeeded() # ← 先行 job 成功時のみ実行
pool:
vmImage: 'ubuntu-latest'
steps:
- task: DownloadPipelineArtifact@2
displayName: 'Download web-dist'
inputs:
artifact: 'web-dist'
path: 'dist'
- script: ls -la dist
displayName: 'List dist'
# GitHub の aws-actions/configure-aws-credentials + aws s3 sync に相当
- task: AWSCLI@1
displayName: 'Sync to S3'
inputs:
awsCredentials: "aws-oidc-federation" # ← 作成したサービスコネクション名に置換
regionName: 'ap-northeast-1'
awsCommand: 's3'
awsSubCommand: 'sync'
awsArguments: 'dist s3://mimura-soft/ --delete'
failOnStandardError: true
パイプラインの実行も問題ないようです。

YAML ファイルの 11 行目をコメントアウトして 14 行目のコメントをはずすと、ローカル (Self-hosted エージェント) で実行できます。ローカル環境での実行も問題ないようです。これで利用料金の心配もなくなりました。

以上でひとまず本シリーズは終了とさせていただきます。
実際に公開するにはサーバー (EC2) や HTTPS 化 (CloudFront)、DNS (Route 53) といった設定が必要になりますので、機会を見て取り組んでいきたいと思います。
以上です。よろしかったらお試しください。