OIDC 認証で AWS に接続し ECS Fargate へ自動デプロイするワークフローを構築する
このハンズオンでは GitHub Actions から AWS に OIDC(OpenID Connect) で認証し、アクセスキーを使わずに安全に ECR へ Docker イメージをプッシュして ECS Fargate サービスを自動デプロイするワークフローを構築します。
| 項目 | 内容 |
|---|---|
| 対象サービス | GitHub Actions、IAM(OIDC)、ECR、ECS Fargate |
| 主な学習内容 | OIDC 連携・IAM ロール設定・GitHub Actions ワークフロー・ECS デプロイ |
| 所要時間 | 75〜90 分 |
| 難易度 | ★★★☆☆(中級者向け) |
| 前提知識 | Docker・ECS Fargate の基礎・GitHub の基本操作 |
| 費用目安 | 約 0〜2 USD(GitHub Actions 無料枠 2000 分/月 あり) |
| 方式 | 安全性 | 運用負荷 |
|---|---|---|
| IAM ユーザー + アクセスキー(非推奨) | キー漏洩リスク・ローテーション必要 | 高い |
| OIDC(推奨) | 一時認証トークン・キー不要 | 低い |
OIDC では GitHub Actions の実行ごとに一時的な認証情報が発行され、AWS に長期キーを保存する必要がありません。
| リソース | 名前 / 値 | 備考 |
|---|---|---|
| IAM OIDC プロバイダー | token.actions.githubusercontent.com | GitHub Actions 用 |
| IAM ロール | github-actions-ecs-role | ECR・ECS 操作権限 |
| ECR リポジトリ | handson-app | Docker イメージ保管 |
| GitHub Actions ワークフロー | .github/workflows/deploy.yml | 自動デプロイ定義 |
ap-northeast-1(東京)以下の名前を事前にメモしておいてください。ワークフローファイルで使用します。
handson-ecs-cluster)handson-ecs-service)handson-task-def)handson-app)GitHub Actions が AWS に OIDC で認証できるよう、IAM に GitHub の OIDC プロバイダーを登録します。
AWS マネジメントコンソールで 「IAM」 を検索して開きます。左メニューの 「ID プロバイダー」→「プロバイダーを追加」 をクリックします。
「サムプリントを取得」をクリックします(自動入力されます)。
「プロバイダーを追加」をクリックします。
「ID プロバイダー」一覧に token.actions.githubusercontent.com が表示されれば成功です。
GitHub Actions が引き受ける IAM ロールを作成し、ECR・ECS の操作権限を付与します。
IAM コンソール → 「ロール」→「ロールを作成」 をクリックします。
「次へ」をクリックします。
以下のポリシーを検索してチェックします。
AmazonEC2ContainerRegistryPowerUser — ECR への push 権限AmazonECS_FullAccess — ECS タスク定義・サービス更新権限このハンズオンでは学習目的で AmazonECS_FullAccess を使いますが、本番環境では必要なアクション(ecs:UpdateService など)のみを許可するカスタムポリシーを作成してください。
「ロールを作成」をクリックします。作成後、ロールの ARN(arn:aws:iam::<account-id>:role/github-actions-ecs-role)をメモします。
作成したロール → 「信頼関係」タブ → 「信頼ポリシーを編集」で以下の形式になっていることを確認します。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::<account-id>:oidc-provider/token.actions.githubusercontent.com"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
},
"StringLike": {
"token.actions.githubusercontent.com:sub": "repo:<GitHubユーザー名>/handson-app:ref:refs/heads/main"
}
}
}
]
}
sub 条件でリポジトリとブランチを絞り込むことで、指定リポジトリ・ブランチからの実行のみが IAM ロールを引き受けられます。* ワイルドカードで全ブランチを許可することも可能です。
IAM ロール github-actions-ecs-role が作成され、信頼ポリシーに token.actions.githubusercontent.com が含まれていれば成功です。
AWS マネジメントコンソールで 「Elastic Container Registry」 を開き 「リポジトリを作成」 をクリックします。
「リポジトリを作成」をクリックします。
作成後、URI(<account-id>.dkr.ecr.ap-northeast-1.amazonaws.com/handson-app)をメモします。
GitHub Actions ワークフローが使用する変数と、サンプルアプリのファイルを設定します。
GitHub リポジトリ → 「Settings」→「Secrets and variables」→「Actions」→「Variables」タブ で以下の変数を追加します。
| Variable 名 | 値 |
|---|---|
| AWS_ACCOUNT_ID | (12 桁の AWS アカウント ID) |
| AWS_REGION | ap-northeast-1 |
| ECR_REPOSITORY | handson-app |
| ECS_CLUSTER | handson-ecs-cluster |
| ECS_SERVICE | handson-ecs-service |
| ECS_TASK_DEFINITION | handson-task-def |
| CONTAINER_NAME | handson-app |
| IAM_ROLE_ARN | arn:aws:iam::(アカウントID):role/github-actions-ecs-role |
パスワードや認証情報は Secrets(マスキングされる)、アカウント ID やリージョンなど非機密情報は Variables(ログに表示可能)を使います。IAM ロール ARN は Variables で問題ありません。
リポジトリに以下の 2 ファイルを追加します(CodePipeline ハンズオンと同じ内容を使用できます)。
const http = require('http');
const PORT = process.env.PORT || 3000;
const VERSION = process.env.APP_VERSION || 'v1.0';
const server = http.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end(`Hello from GitHub Actions CI/CD! Version: ${VERSION}\n`);
});
server.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
FROM node:20-alpine WORKDIR /app COPY app.js . EXPOSE 3000 CMD ["node", "app.js"]
リポジトリに .github/workflows/deploy.yml を作成します。これが CI/CD の本体です。
リポジトリに .github/workflows/deploy.yml を以下の内容で作成します。
name: Deploy to Amazon ECS
on:
push:
branches:
- main
env:
AWS_REGION: ${{ vars.AWS_REGION }}
ECR_REGISTRY: ${{ vars.AWS_ACCOUNT_ID }}.dkr.ecr.${{ vars.AWS_REGION }}.amazonaws.com
ECR_REPOSITORY: ${{ vars.ECR_REPOSITORY }}
ECS_CLUSTER: ${{ vars.ECS_CLUSTER }}
ECS_SERVICE: ${{ vars.ECS_SERVICE }}
ECS_TASK_DEFINITION: ${{ vars.ECS_TASK_DEFINITION }}
CONTAINER_NAME: ${{ vars.CONTAINER_NAME }}
jobs:
deploy:
name: Build and Deploy
runs-on: ubuntu-latest
# OIDC トークン発行に必要な permissions
permissions:
id-token: write # OIDC トークン取得に必要
contents: read # リポジトリ読み取りに必要
steps:
# 1. ソースコードを取得
- name: Checkout
uses: actions/checkout@v4
# 2. AWS 認証(OIDC)
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ vars.IAM_ROLE_ARN }}
aws-region: ${{ env.AWS_REGION }}
# 3. ECR にログイン
- name: Login to Amazon ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v2
# 4. Docker ビルド・タグ付け・ECR プッシュ
- name: Build, tag, and push image to ECR
id: build-image
run: |
IMAGE_URI=$ECR_REGISTRY/$ECR_REPOSITORY:${{ github.sha }}
docker build -t $IMAGE_URI .
docker push $IMAGE_URI
echo "image=$IMAGE_URI" >> $GITHUB_OUTPUT
# 5. 現在のタスク定義を JSON ファイルとして取得
- name: Download current task definition
run: |
aws ecs describe-task-definition \
--task-definition $ECS_TASK_DEFINITION \
--query taskDefinition > task-definition.json
# 6. タスク定義の image を新しいものに差し替え
- name: Render new task definition
id: task-def
uses: aws-actions/amazon-ecs-render-task-definition@v1
with:
task-definition: task-definition.json
container-name: ${{ env.CONTAINER_NAME }}
image: ${{ steps.build-image.outputs.image }}
# 7. ECS サービスを更新してデプロイ
- name: Deploy to ECS
uses: aws-actions/amazon-ecs-deploy-task-definition@v2
with:
task-definition: ${{ steps.task-def.outputs.task-definition }}
service: ${{ env.ECS_SERVICE }}
cluster: ${{ env.ECS_CLUSTER }}
wait-for-service-stability: true
| ステップ | 使用 Action | 処理内容 |
|---|---|---|
| Checkout | actions/checkout@v4 | ソースコードを Runner に取得 |
| AWS 認証 | aws-actions/configure-aws-credentials@v4 | OIDC トークンで IAM ロールを引き受け一時認証情報を取得 |
| ECR ログイン | aws-actions/amazon-ecr-login@v2 | Docker が ECR に push できるよう認証 |
| イメージビルド | (run: docker build/push) | commit SHA をタグにしてビルド・プッシュ |
| タスク定義取得 | (run: aws ecs describe-task-definition) | 現在のタスク定義を task-definition.json として保存 |
| タスク定義更新 | aws-actions/amazon-ecs-render-task-definition@v1 | 取得した JSON ファイルのイメージ URI を新しいものに置換 |
| ECS デプロイ | aws-actions/amazon-ecs-deploy-task-definition@v2 | 新タスク定義を登録しサービスを更新、安定するまで待機 |
amazon-ecs-render-task-definition Action の task-definition 入力は、ローカルの JSON ファイルパスを期待します(タスク定義名ではありません)。そのため直前に aws ecs describe-task-definition --query taskDefinition で現在のタスク定義を task-definition.json として書き出しておき、そのファイルパスを渡します。Action はこの JSON のうち指定コンテナのイメージ URI だけを差し替え、続く deploy Action が新リビジョンを登録します(登録時に読み取り専用フィールドは自動的に除去されます)。
ワークフローファイルを含めてリポジトリに push すると Actions が自動起動します。
git add . git commit -m "ci: add GitHub Actions deployment workflow" git push origin mainGitHub リポジトリ → 「Actions」タブ → 最新の実行をクリックします。各ステップのログが確認できます。
| ステップ | 正常時の出力例 |
|---|---|
| Configure AWS credentials | Assumed role arn:aws:iam::...:role/github-actions-ecs-role |
| Login to Amazon ECR | Login Succeeded |
| Build, tag, and push image | digest: sha256:... |
| Deploy to ECS | Service updated successfully |
app.js のバージョンを変更してプッシュし、ECS サービスに新しいバージョンが自動デプロイされることを確認します。
// v1.0 → v2.0 に変更 const VERSION = process.env.APP_VERSION || 'v2.0';git add app.js git commit -m "feat: update to v2.0" git push origin main
Actions が完了後、ALB の DNS 名にアクセスして Version: v2.0 が表示されれば成功です。
main ブランチへの push だけで、アクセスキー不要の安全な OIDC 認証により自動でビルド・デプロイが実行されます。
ECS Fargate(タスク稼働時間)・ECR(イメージ保管量)で課金が発生します。GitHub Actions 自体は無料枠(2000 分/月)内で完結します。
handson-app →「削除」github-actions-ecs-role →「削除」token.actions.githubusercontent.com →「削除」| 習得したスキル | 実践内容 |
|---|---|
| OIDC 認証 | アクセスキー不要の安全な GitHub → AWS 認証 |
| IAM ロール設計 | 信頼ポリシーの sub 条件でリポジトリ・ブランチを限定 |
| GitHub Actions ワークフロー | push トリガー・permissions・env・steps の組み合わせ |
| ECS 自動デプロイ | タスク定義の image 差し替え + サービス更新の自動化 |
| ECR 連携 | commit SHA タグによるイメージ管理 |