コンテナのビルドとデプロイを自動化しよう
このハンズオンでは、GitHub ActionsからAWS ECS/Fargateへのコンテナイメージの自動ビルドと自動デプロイを、実際にハンズオン形式で手を動かしながら体験します。
- サンプルリポジトリの準備
- OIDC連携によるAWSとの安全な接続設定
- ECRへのイメージビルド・プッシュの自動化
- ECS Fargateへの自動デプロイ設定
- 初回デプロイの動作確認と更新デプロイ
1. 事前準備
Gitの基礎知識を習得していることを前提とします。自信のない方は先に以下の講座を実施してください。
この講座のハンズオンでは、以下のツールやアカウントが必要です。まだ準備できていない場合は、リンク先の手順に沿って準備をお願いします。
また、CI/CDパイプラインの前段となる静的解析・自動テストの自動化については、静的解析と自動テストを自動化しようで扱っています。先にそちらを体験しておくと、CI/CDパイプラインの全体像がより掴みやすくなります。
2. ハンズオンの概要
ここでは、実際にコンテナをベースとした自動デプロイのパイプラインを構築するハンズオンを実施します。
まずはハンズオンの概要について説明します。今回は、Python(FastAPI)のアプリケーションをコンテナ化し、Amazon ECS/Fargateへ自動デプロイするCI/CDパイプラインを構築します。
処理の流れとして、開発者がPull Requestをmainブランチにマージすると、GitHub Actionsのワークフローが起動します。このワークフローでは、ビルド工程が最初に実行され、その後デプロイ工程が直列で実行されます。ビルドで作成した成果物(コンテナイメージ)をデプロイで使用するため、直列で行う必要があります。
また、デプロイ時には新しいタスクを起動してヘルスチェックが成功してから古いタスクを停止する仕組みになっているため、サービスを止めずに新しいバージョンへ切り替えることができます。これにより、利用者に影響を与えずに本番環境を更新できます。
合わせて、AWSとの接続はOIDC接続を利用することで、安全に接続を行います。
以上の流れを通して、自動ビルドと自動デプロイによってリリースを自動化するCI/CDパイプラインの全体像を体験できます。
2.1 AWS構成の確認

今回扱うAWSの構成を確認します。今回のハンズオンでは、AWSアカウント内に仮想ネットワークであるVPCとサブネットを作成し、その中にFargateで動作するECSを構築します。このECSが、GitHub Actionsからコンテナをデプロイする対象となります。
次に、インターネット経由でECSへアクセスできるようにするため、Internet Gatewayを用意します。これをVPCに関連付けることで、ユーザはインターネットゲートウェイを経由してECSにアクセスできるようになります。
さらに、コンテナイメージを格納するECRを作成します。GitHub ActionsからECRにイメージをアップロードし、そのイメージをもとにECSでコンテナが起動します。これが、今回の構成全体の流れになります。
3. リポジトリの準備
CI/CDパイプラインを構築するためには、まず対象となるアプリケーションのソースコードを管理するGitリポジトリが必要です。GitHub Actionsはリポジトリに格納されたコードに対して動作するため、ここでは自身のGitHubアカウントにリポジトリを用意し、ローカル環境でコードを編集できる状態を整えます。
3.1 GitHubで空のリポジトリを作成
今回のハンズオンではFastAPIで作成済みのサンプルAPIをCI/CDパイプラインの対象とします。ゼロからアプリケーションを作るとCI/CDの学習に集中できないため、準備済みのサンプルコードを使う形で進めます。
実務に近い形を体験するために、ご自身のGitHubアカウントで新しいリポジトリを作成し、そこにサンプルコードを配置する流れで進めます。自分が作ったリポジトリに対してCI/CDパイプラインを構築することで、実感が湧きやすい体験ができます。
GitHubにログインし、右上の+ボタンからNew repositoryをクリックします。
以下の設定でリポジトリを作成します。
| 設定項目 | 値 | 設定の基準 |
|---|---|---|
| Owner | ご自身のGitHubアカウント | 自分のアカウントで管理するため |
| Repository name | devopscamp-cicd-handson | ハンズオン用のリポジトリ名 |
| Visibility | Public | GitHub Actionsの無料実行時間を気にせず使えるため |
| 💡 ポイント |
|---|
リポジトリ名は任意ですが、後のOIDC設定でこのリポジトリ名を使うため、覚えておきやすい名前にしておきましょう。本ハンズオンではdevopscamp-cicd-handsonという名前で進めます。 |
Create repositoryボタンをクリックしてリポジトリを作成します。

作成後、「Quick setup」の画面が表示されたら、ここでは何も操作せずに次のステップに進みます。
3.2 サンプルコードのダウンロード
リモートリポジトリの器ができたので、次はそこに配置するアプリケーションのソースコードを準備します。今回はFastAPIで作成済みのシンプルなAPIを利用するため、以下のボタンからサンプルコードのZIPファイルをダウンロードしてください。
ダウンロードしたZIPファイルを、任意の場所に解凍してください。解凍するとdevopscamp-cicd-handsonという名前のフォルダができます。このフォルダの中身は以下のようになっています。
devopscamp-cicd-handson/
├── Dockerfile
├── requirements.txt
└── main.py
各ファイルの役割を以下の表にまとめます。
| ファイル名 | 役割 |
|---|---|
main.py |
FastAPIアプリケーションのエントリーポイント。ヘルスチェック用のエンドポイントと、動作確認用のメッセージを返すエンドポイントが記述されている |
requirements.txt |
Pythonプロジェクトで利用するパッケージの一覧を記載したファイル。pip installでこれらをインストールする |
Dockerfile |
コンテナイメージを作成するための手順を記述した設定ファイル。自動ビルドの際にはこの内容に従ってビルドが実行される |
main.pyの中身は以下のとおりで、ヘルスチェック用の/healthと、動作確認用の/の2つのエンドポイントだけを持つシンプルな構成です。
from fastapi import FastAPI
app = FastAPI()
@app.get("/health")
def health():
return {"status": "ok"}
@app.get("/")
def root():
return {"message": "API呼び出しテストに成功しました"}
今回のハンズオンではCI/CDパイプラインの構築が主題のため、APIの中身はあえて最小限にしています。のちほどこのメッセージを書き換えてPushすることで、自動デプロイが動いてアプリが更新される様子を体験します。
3.3 Gitリポジトリとして初期化
ダウンロードしたサンプルコードは、まだただのフォルダであり、Gitの管理下に入っていません。このままではGitHubへPushできないため、devopscamp-cicd-handsonフォルダをGitリポジトリとして初期化し、最初のコミットを作成します。
Visual Studio Codeでdevopscamp-cicd-handsonフォルダを開き、ターミナルを開いて以下のコマンドを実行します。
まず、フォルダをGitリポジトリとして初期化します。
git init
以下のような実行結果が表示されます。
Initialized empty Git repository in /path/to/devopscamp-cicd-handson/.git/
次に、すべてのファイルをステージングします。
git add .
ステージングしたファイルをコミットします。
git commit -m "initial commit"
git logなどで初回コミットが作成されていれば、ここまでの操作は完了です。
3.4 リモートリポジトリの登録とPush
ローカルでコミットを作成しただけでは、GitHub上のリモートリポジトリにはまだ何も反映されていません。CI/CDパイプラインはGitHub上のコードに対して動作するため、ローカルリポジトリと先ほど作成したGitHubリポジトリを紐付け、Pushしてコードを反映させます。
まず、ローカルのブランチ名をmainに統一します。Gitのバージョンによっては初期ブランチ名がmasterになっていることがあるため、念のため明示的に変更します。
git branch -M main
次に、先ほど作成したGitHubリポジトリをリモートリポジトリとして登録します。<your-github-account-name>の部分はご自身のGitHubアカウント名に置き換えてください。
git remote add origin https://github.com/<your-github-account-name>/devopscamp-cicd-handson.git
| 📝 git remoteとは |
|---|
git remoteは、ローカルリポジトリと連携するリモートリポジトリを管理するコマンドです。originはリモートリポジトリに付ける慣習的な名前で、これ以降git push origin mainのようにoriginという名前でGitHubのリポジトリを指し示せるようになります。 |
最後に、コミットしたコードをリモートリポジトリにPushします。
git push -u origin main
-uオプションを付けることで、以降はgit pushだけで同じブランチにPushできるようになります。
GitHubのリポジトリページをブラウザで開き、main.pyやDockerfileなどのファイルが表示されていれば、ここまでの操作は完了です。

4. AWSリソースの作成
ソースコードの準備ができたので、次はデプロイ先となるAWSリソースを用意します。CI/CDパイプラインは「コードをビルドしてどこかにデプロイする」という流れなので、デプロイ先のインフラが存在しないと動作確認ができません。
今回利用するAWSリソース(VPC、ECR、ECSクラスタなど)はCloudFormationテンプレートにより一括で作成します。本講座の主軸はCI/CDパイプラインの構築であり、AWSインフラの構築自体は本質ではないためです。なお、CI/CDパイプラインで重要な「GitHub ActionsからAWSへ接続するためのIAMロール」は、後の自動デプロイのセクションで手動で作成します。IAMロールの信頼関係や権限の設定は今回のハンズオンの重要な学びの一つであるため、あえて手動で体験してもらう形にしています。
| 📝 CloudFormationとは |
|---|
| CloudFormationは、AWSのリソースをテンプレート(YAML/JSON)で定義し、自動的に作成・管理できるサービスです。手動でリソースを一つずつ作成する代わりに、テンプレートをアップロードするだけで必要なリソースをまとめて構築できます。なお、このハンズオンではCloudFormationテンプレートの内容を理解する必要はありません。テンプレートをそのままアップロードして環境を構築してください。 |
CloudFormationで作成されるリソースは以下のとおりです。
| リソース | 名前 | 説明 |
|---|---|---|
| VPC | cicd-handson-vpc | 10.0.0.0/16のアドレス範囲を持つ仮想ネットワーク |
| パブリックサブネット | cicd-handson-public-subnet | ECSタスクを配置するサブネット |
| インターネットゲートウェイ | cicd-handson-igw | パブリックサブネットからのインターネットアクセス用 |
| ルートテーブル | cicd-handson-rt | パブリックサブネット用のルーティング設定 |
| セキュリティグループ | cicd-handson-ecs-sg | ECSタスク用(HTTP:8000を許可) |
| ECRリポジトリ | test-repository | コンテナイメージを格納するリポジトリ |
| ECSクラスター | test-cluster | ECSサービスを管理する論理グループ |
| タスク実行ロール | ecsTaskExecutionRole-cicd-handson | ECSタスクがCloudWatch Logsなどを操作するためのIAMロール |
| タスク定義 | test-task | ECSタスクの定義(イメージURI、CPU/メモリなど) |
| ECSサービス | test-service | タスク定義を元にECSタスクを実行・管理するサービス |
| CloudWatch Logs | /ecs/test-task | ECSタスクのログ出力先 |
4.1 テンプレートファイルの準備
まず、CloudFormationにアップロードするテンプレートファイルを用意します。以下のボタンからテンプレートファイルをダウンロードしてください。
ファイルがダウンロードできたら、次のステップに進みます。
4.2 CloudFormationスタックの作成
AWSマネジメントコンソールでCloudFormationのダッシュボードを開きます。検索バーに「CloudFormation」と入力し、サービスを選択してください。
CloudFormationのダッシュボードが表示されたら、スタックの作成 > 新しいリソースを使用(標準)をクリックします。
ステップ1: テンプレートの指定
テンプレートの指定画面が表示されます。下記表に従い、設定を行ってください。
| 設定項目 | 値 | 設定の基準 |
|---|---|---|
| テンプレートの準備 | 既存のテンプレートを選択 | 事前に作成したテンプレートファイルを使用するため |
| テンプレートソース | テンプレートファイルのアップロード | ローカルのテンプレートファイルを直接アップロードするため |
| テンプレートファイルのアップロード | 先ほど保存したcicd-handson.yml |
ハンズオン環境を構築するテンプレートを指定するため |
設定後、次へをクリックしてください。

ステップ2: スタックの詳細を指定
スタックの詳細を指定する画面が表示されます。下記表に従い、設定を行ってください。
| 設定項目 | 値 | 設定の基準 |
|---|---|---|
| スタック名 | cicd-handson | スタックを識別するための名前 |
今回のテンプレートにはパラメータが定義されていないため、スタック名以外の入力はありません。設定後、次へをクリックしてください。

ステップ3: スタックオプションの設定
スタックオプションの設定画面が表示されます。基本的な設定はデフォルトのままで問題ありませんが、画面下部に「The following resource(s) require capabilities: [AWS::IAM::Role]」という警告が表示されます。これは、今回のテンプレートにIAMロール(ecsTaskExecutionRole-cicd-handson)が含まれているため、IAMリソースの作成を承認する必要があるというメッセージです。
「AWS CloudFormation によって IAM リソースがカスタム名で作成される場合があることを承認します。」にチェックを入れてから、次へをクリックしてください。

ステップ4: 確認と作成
確認画面が表示されます。設定内容を確認し、送信をクリックします。

スタックの作成完了を確認
スタックの作成が開始されます。画面にはステータスがCREATE_IN_PROGRESSと表示され、リソースが順番に作成されていきます。完了まで数分かかります。
ステータスがCREATE_COMPLETEに変わったら、環境構築は完了です。画面を更新してもCREATE_COMPLETEにならない場合は、もう少し待ってから再度更新してください。
4.3 リソースの確認
CloudFormationでスタックの作成が完了したら、実際にどのようなリソースが作成されたのかを確認しておきましょう。ここで各リソースの役割を把握しておくことで、この後のワークフロー作成時に「なぜこのターゲットグループやECRを指定するのか」が理解しやすくなります。
CloudFormationでは、スタックから作成されたリソースをまとめて確認できるリソースタブが用意されています。AWSマネジメントコンソールを個別に開いて回る必要がないため、まずはこの画面で作成されたリソースを一覧で確認します。
CloudFormationのスタック一覧でcicd-handsonスタックをクリックし、上部のリソースタブを選択してください。スタックから作成されたリソースが一覧で表示されます。
以下のリソースがすべて作成されていること、ステータスがCREATE_COMPLETEになっていることを確認してください。
| 論理ID | 物理ID | リソースタイプ |
|---|---|---|
| VPC | cicd-handson-vpc | AWS::EC2::VPC |
| PublicSubnet | cicd-handson-public-subnet | AWS::EC2::Subnet |
| InternetGateway | cicd-handson-igw | AWS::EC2::InternetGateway |
| RouteTable | cicd-handson-rt | AWS::EC2::RouteTable |
| SecurityGroup | cicd-handson-ecs-sg | AWS::EC2::SecurityGroup |
| ECRRepository | test-repository | AWS::ECR::Repository |
| ECSCluster | test-cluster | AWS::ECS::Cluster |
| TaskExecutionRole | ecsTaskExecutionRole-cicd-handson | AWS::IAM::Role |
| TaskDefinition | test-task | AWS::ECS::TaskDefinition |
| ECSService | test-service | AWS::ECS::Service |
| LogGroup | /ecs/test-task | AWS::Logs::LogGroup |
各リソースの物理IDをクリックすると、そのリソースの管理画面(VPCコンソールやECSコンソールなど)に直接遷移できます。気になるリソースがあれば、実際に遷移して中身を確認してみてください。
リソース一覧にすべてのリソースが表示され、全てCREATE_COMPLETEになっていれば、環境構築は正常に完了しています。

5. OIDC接続
AWSリソースの準備ができたので、次はGitHub ActionsからAWSへ接続するための設定を行います。ビルドしたコンテナイメージをECRにプッシュしたり、ECSにデプロイしたりするには、GitHub ActionsからAWSリソースへアクセスする必要があります。そのための前準備としてGitHub ActionsとAWSの接続設定を行います。
5.1 なぜOIDCを使うのか
GitHub ActionsからAWSに接続する方法はいくつかありますが、大きく分けると次の2通りになります。
- IAMユーザのアクセスキーID・シークレットアクセスキーをGitHubのSecretsに登録し、ワークフローから使用する
- OIDCで、GitHub Actionsが発行したIDトークンをAWSが検証し、一時的なクレデンシャルを発行してアクセスする
1の方法はシンプルですが、アクセスキーが万が一漏洩すると第三者がAWSリソースに恒久的にアクセスできてしまうというリスクがあります。また、定期的なローテーションも必要になります。
一方、2のOIDC(OpenID Connect)を使う方法では、アクセスキーを保存する必要がなく、毎回一時的な短命クレデンシャルが発行されます。漏洩リスクが大幅に軽減され、ローテーションも不要になるため、本番環境ではこちらが推奨される方法です。今回は実務で使える形を体験するために、OIDCでの接続を採用します。
| 📝 OIDCとは |
|---|
| OIDC(OpenID Connect)は、認証を行うための仕組みの一つです。GitHub ActionsとAWSの場合、ワークフロー実行時にGitHubが「このリポジトリ・ブランチから実行された」というIDトークンを発行し、AWSがそれを検証して一時的なクレデンシャルを発行します。信頼関係を事前にAWS側に登録しておくことで、「特定のリポジトリからのワークフロー実行時だけAWSへのアクセスを許可する」といった細かい制御ができます。 |
5.2 OIDCの設定に必要な作業
OIDCでGitHub ActionsとAWSを接続するには、AWS側で2つの設定を行う必要があります。
| 作業 | 内容 |
|---|---|
| IAM Identity Providerの作成 | GitHubのOIDCプロバイダ(token.actions.githubusercontent.com)をAWSに登録する |
| IAMロールの作成 | GitHub Actionsが引き受けるロールを作成し、信頼関係と権限を設定する |
これらを順番に作成していきます。
5.3 IAM Identity Providerの作成
まず、GitHubをOIDCプロバイダとしてAWSに登録します。これにより、GitHubから発行されたIDトークンをAWSが信頼できるようになります。
AWSマネジメントコンソールでIAMのダッシュボードを開きます。検索バーに「IAM」と入力し、サービスを選択してください。
左メニューからIDプロバイダを選択し、プロバイダを追加をクリックします。

以下の設定でプロバイダを追加します。
| 設定項目 | 値 | 設定の基準 |
|---|---|---|
| プロバイダのタイプ | OpenID Connect | OIDCでGitHubと接続するため |
| プロバイダのURL | https://token.actions.githubusercontent.com |
GitHub ActionsのOIDCエンドポイント |
| 対象者 | sts.amazonaws.com |
AWS STSをターゲットとするため |
入力後、プロバイダを追加をクリックします。

IDプロバイダの一覧にtoken.actions.githubusercontent.comが追加されていれば、ここまでの操作は完了です。

| 💡 ポイント |
|---|
| 以前はOIDCプロバイダを作成する際に「サムプリント(指紋)」の登録が必要でしたが、現在のAWSでは自動的に検証される仕組みになっているため、この手順は不要です。プロバイダのURLと対象者を指定するだけで登録できます。 |
5.4 IAMロールの作成
次に、GitHub Actionsが引き受けるIAMロールを作成します。このロールには、「どのGitHubリポジトリからのアクセスを許可するか」という信頼関係と、「そのロールがAWSで何をできるか」という権限の2つを設定します。
IAMのダッシュボードの左メニューからロールを選択し、ロールを作成をクリックします。

信頼されたエンティティの選択
ロール作成画面で、信頼されたエンティティのタイプを選択します。
| 設定項目 | 値 | 設定の基準 |
|---|---|---|
| 信頼されたエンティティタイプ | ウェブアイデンティティ | OIDCプロバイダを信頼するため |
| アイデンティティプロバイダー | token.actions.githubusercontent.com |
先ほど作成したOIDCプロバイダ |
| Audience | sts.amazonaws.com |
AWS STSをターゲットとするため |
| GitHub組織 | ご自身のGitHubユーザ名 | リポジトリのオーナー名 |
| GitHubリポジトリ | devopscamp-cicd-handson |
対象のリポジトリ名 |
| GitHubブランチ | main |
mainブランチからのアクセスだけを許可するため。feature/xxxブランチ等からAWSを操作できないようにする |
| 💡 ポイント |
|---|
GitHub組織には、ご自身のGitHubユーザ名(個人アカウントの場合)または組織名を指定します。GitHubリポジトリには、このハンズオンで作成したリポジトリ名(devopscamp-cicd-handson)を指定してください。リポジトリ名を変えている場合は、その名前を指定します。 |
| 💡 ポイント |
|---|
ブランチを空欄にすると、リポジトリ内のあらゆるブランチからAWSへアクセスできてしまいます。今回のワークフローは mainへのpush でのみ動作する前提なので、mainブランチのみを信頼する設定にしておくとセキュリティが高まります。feature/xxxブランチ等から意図せずAWSリソースが操作される事故を防げます。将来、ステージング環境を追加する場合は staging ブランチを許可リストに追加する、という形で拡張できます。 |
設定後、次へをクリックします。

許可ポリシーの追加
このロールには、あとのステップでインラインポリシー(特定のリソース・アクションに絞ったカスタムポリシー)として必要な権限を追加します。このステップではAWS管理ポリシーは何も選択せず、そのまま次へをクリックしてください。
| 💡 ポイント |
|---|
AWSが用意する管理ポリシー(例: AmazonEC2ContainerRegistryPowerUser)は広く使いやすい反面、リソースやアクションが広めに許可されがちです。本ハンズオンでは「最小権限の原則」を体感してもらうため、必要なECR・ECSのアクションだけを、対象リソースも絞ったインラインポリシーで付与します。 |

名前、確認、および作成
最後にロールの名前と説明を入力します。
| 設定項目 | 値 | 設定の基準 |
|---|---|---|
| ロール名 | github-actions-deploy-role | GitHub Actionsからのデプロイに使うロール |
| 説明 | Role for deployment from GitHub Actions | ロールの目的を明確にするため |

入力後、画面下部のロールを作成をクリックします。

| ⚠️ 「ロールの説明が無効です」エラーが出る場合 |
|---|
AWSのIAMロール説明欄は日本語を受け付けません。英数字と記号のみで入力してください。使用可能な文字は A-Za-z0-9、タブ、改行、および `_+=,.@-/[{}]!#$%^*():;"'`` の記号です。説明欄はオプションなので、空欄のままでも構いません。 |
ロール一覧にgithub-actions-deploy-roleが表示されていれば、ここまでの操作は完了です。

デプロイ権限の追加(インラインポリシー)
作成したロールにはまだ権限が何もついていません。ECRへのイメージプッシュとECSへのデプロイを行うために、インラインポリシーとして必要な権限を設定します。
先ほど作成したgithub-actions-deploy-roleの詳細画面を開きます。
許可タブから許可を追加 > インラインポリシーを作成をクリックします。

JSONタブを選択し、以下の内容を入力します。
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "ECRAuth",
"Effect": "Allow",
"Action": "ecr:GetAuthorizationToken",
"Resource": "*"
},
{
"Sid": "ECRPush",
"Effect": "Allow",
"Action": [
"ecr:BatchCheckLayerAvailability",
"ecr:InitiateLayerUpload",
"ecr:UploadLayerPart",
"ecr:CompleteLayerUpload",
"ecr:PutImage",
"ecr:BatchGetImage"
],
"Resource": "arn:aws:ecr:*:*:repository/test-repository"
},
{
"Sid": "ECSDeploy",
"Effect": "Allow",
"Action": [
"ecs:UpdateService",
"ecs:DescribeServices",
"ecs:DescribeTaskDefinition",
"ecs:RegisterTaskDefinition",
"ecs:DeregisterTaskDefinition",
"ecs:ListTasks",
"ecs:DescribeTasks"
],
"Resource": "*"
},
{
"Sid": "PassExecutionRole",
"Effect": "Allow",
"Action": "iam:PassRole",
"Resource": "arn:aws:iam::*:role/ecsTaskExecutionRole-cicd-handson"
}
]
}

ポリシーは4ブロックに分かれています。各ブロックが「何に」「何の権限を」「なぜ」与えているかを以下の表で解説します。
| Sid | 対象リソース | 与える権限 | なぜ必要か |
|---|---|---|---|
ECRAuth |
全ECR(*) |
ecr:GetAuthorizationToken |
docker login 時にECRから一時認証トークンを取得する必要がある。このAPIはアカウント単位の操作のため、リポジトリでリソースを絞れない仕様 |
ECRPush |
test-repository リポジトリのみ |
ecr:BatchCheckLayerAvailability / ecr:InitiateLayerUpload / ecr:UploadLayerPart / ecr:CompleteLayerUpload / ecr:PutImage / ecr:BatchGetImage |
docker push でイメージをアップロードするために必要な一連の操作。レイヤーの確認・アップロード開始・転送・完了・登録までをカバーする。リソースを絞ることで他のECRリポジトリには手出しできないようにしている |
ECSDeploy |
全ECS(*) |
ecs:UpdateService / ecs:DescribeServices / ecs:DescribeTaskDefinition / ecs:RegisterTaskDefinition / ecs:DeregisterTaskDefinition / ecs:ListTasks / ecs:DescribeTasks |
タスク定義を新しいリビジョンとして登録し、ECSサービスを更新してデプロイするために必要。RegisterTaskDefinition はリソース絞り込みが利かない仕様のため、本ハンズオンでは * としている |
PassExecutionRole |
ecsTaskExecutionRole-cicd-handson ロールのみ |
iam:PassRole |
ECSがタスクを起動する際、コンテナにCloudWatch Logsへの書き込み権限などを与える「タスク実行ロール」をECS側に渡す必要がある。CloudFormationで作成した特定ロールだけに絞ることで、任意のIAMロールを勝手に渡せないようにしている |
次へをクリックし、ポリシー名にDeployPolicyと入力してポリシーの作成をクリックします。

github-actions-deploy-roleの許可一覧にDeployPolicyが追加されていれば、ここまでの操作は完了です。

5.5 ロールARNの確認
作成したIAMロールのARNは、後ほどGitHub Actionsのワークフローから参照します。github-actions-deploy-roleの詳細画面の上部に表示されているARNを確認しておきましょう。
ARNは以下のような形式になっています。
arn:aws:iam::123456789012:role/github-actions-deploy-role

この中の123456789012の部分がAWSアカウントIDです。このアカウントIDはGitHub Actionsワークフローから参照するため、次のステップでGitHubのシークレットに登録します。
5.6 GitHubシークレットの登録
GitHub Actionsのワークフローからロールを参照する際、AWSアカウントIDをコードにハードコードすると、リポジトリを見た人にアカウントIDが知られてしまいます。アカウントID自体は機密情報ではありませんが、安全な運用としてはGitHubのシークレット機能に登録し、ワークフローから動的に参照する形が推奨されます。
ここでは、AWSアカウントIDをGitHubシークレットに登録します。
GitHubの対象リポジトリ(devopscamp-cicd-handson)のページを開きます。
Settingsタブをクリックし、左メニューからSecrets and variables > Actionsを選択します。
New repository secretボタンをクリックし、以下の内容でシークレットを登録します。

| 設定項目 | 値 | 設定の基準 |
|---|---|---|
| Name | AWS_ACCOUNT_ID | ワークフローから参照する際の名前 |
| Secret | 先ほど確認したAWSアカウントID(例: 123456789012) |
対象のAWSアカウント |
Add secretボタンをクリックすると、シークレットが登録されます。

シークレット一覧にAWS_ACCOUNT_IDが表示されていれば、ここまでの操作は完了です。

6. 自動ビルドとデプロイ
OIDC接続の準備が整ったので、いよいよGitHub Actionsを使った自動ビルドと自動デプロイのワークフローを構築していきます。
手動でデプロイを行う場合、Pull Requestをマージしたあとに「コンテナイメージの作成」「ECRへのプッシュ」「ECSへのデプロイ」といった作業を開発者が実行する必要があります。これらの一連の作業を自動化することで、mainブランチにマージしただけで本番環境に反映されるという、真のCI/CDパイプラインが完成します。
自動ビルドと自動デプロイは別のジョブとして定義することもできますが、今回は同一のジョブで実行する流れとします。別々のジョブにすると、ジョブ間でビルドしたイメージの情報を受け渡す必要があり、複雑になるためです。
6.1 作業用ブランチの作成
ワークフローファイルを追加する前に、作業用のブランチを作成します。ブランチを分けずにmainブランチに直接コミットしてしまうと、「Pull Requestを作成してマージする」という実務的な流れを体験できません。CI/CDパイプラインは「レビュー後のマージ」をトリガーにデプロイする前提で組むため、ここでしっかりと作業用ブランチを分けておきましょう。
Visual Studio Codeでdevopscamp-cicd-handsonフォルダを開いたうえで、ターミナルを開きます。以下のgit switchコマンドで、feature/add-cicd-pipelineというブランチを作ります。-cオプションは新しいブランチを作成して切り替えるという意味です。
git switch -c feature/add-cicd-pipeline
Switched to a new branch 'feature/add-cicd-pipeline'と表示されていれば、ここまでの操作は完了です。
6.2 自動ビルド&デプロイの流れ
自動ビルドとデプロイの流れを説明します。FastAPIで作成したAPIをコンテナ化し、Amazon ECSにデプロイするまでの流れを自動化しています。
トリガーはmainブランチへのPushです。これは、直接Pushした場合だけでなく、Pull RequestのマージによるPushにも対応しています。
処理の流れは以下のとおりです。
- GitHub Actionsでソースコードをチェックアウトする
- 先ほど作成したOIDCの仕組みを使って、AWSへの認証を行う
- Amazon ECRへログインする
- ソースコードからコンテナイメージを作成し、ECRにPushする(自動ビルド)
- タスク定義ファイルのプレースホルダをシークレット値で置換する
- 置換済みタスク定義を元に、Amazon ECSへデプロイする(自動デプロイ)
これらの一連の流れを、GitHub Actionsのワークフローとして設定します。
6.3 ワークフローの作成
まず、ワークフローファイルを作成して全体の流れを定義します。
ファイルの作成
ビルドとデプロイの処理は、mainブランチへのマージ(Push)時に動作するワークフローとして構築します。GitHub Actionsは.github/workflowsフォルダ配下のYAMLファイルをワークフローとして自動的に認識するため、このルールに沿ってファイルを配置します。今回はdeploy.ymlというファイル名で作成します。
devopscamp-cicd-handson/
└── .github/
└── workflows/
└── deploy.yml ← このファイルを作成
作成したファイルに以下の内容を記述して保存します。
name: Build and Deploy to ECS
on:
push:
branches:
- main # mainブランチへのpushをトリガーに実行
permissions:
id-token: write # OIDCトークン発行に必要
contents: read # リポジトリ内容を取得するために必要
env:
AWS_REGION: ap-northeast-1
ECR_REPOSITORY: test-repository # ECRリポジトリ名
ECS_CLUSTER: test-cluster # ECSクラスター名
ECS_SERVICE: test-service # ECSサービス名
jobs:
build-and-deploy:
name: Build and Deploy
runs-on: ubuntu-latest
steps:
# ソースコードをチェックアウト
- name: Checkout source
uses: actions/checkout@v5
# AWS認証情報を設定 (OIDCを利用)
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v5
with:
role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/github-actions-deploy-role
aws-region: ${{ env.AWS_REGION }}
# ECRへログイン
- name: Login to Amazon ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v2
# Dockerイメージをビルド&プッシュ
- name: Build and Push Docker image
run: |
IMAGE_REPO=${{ steps.login-ecr.outputs.registry }}/${{ env.ECR_REPOSITORY }}
docker build -t $IMAGE_REPO:latest .
docker push $IMAGE_REPO:latest
# タスク定義のプレースホルダをシークレットの値で置換
- name: Render task definition
run: |
sed -i "s|<AWS_ACCOUNT_ID>|${{ secrets.AWS_ACCOUNT_ID }}|g" ecs-taskdef.json
# ECSへデプロイ
- name: Deploy to ECS
uses: aws-actions/amazon-ecs-deploy-task-definition@v2
with:
task-definition: ecs-taskdef.json
service: ${{ env.ECS_SERVICE }}
cluster: ${{ env.ECS_CLUSTER }}
wait-for-service-stability: true
6.4 ワークフローの説明
ワークフローの説明を行います。
名称
name: Build and Deploy to ECS
ワークフローの名称として、Build and Deploy to ECSという名前をつけています。
トリガー
on:
push:
branches:
- main
トリガーとして、mainブランチに対してコードが変更された時に起動するように設定しています。
permissions:
id-token: write # OIDCトークン発行に必要
contents: read # リポジトリ内容を取得するために必要
Permissionsにより、GitHubが操作を行う上での権限を設定します。
id-token: writeは、GitHub Actions が OIDCトークンを発行するための権限です。今回はOIDCトークンを利用してAWSへアクセスするため、この権限が必要になります。
contents: readは、リポジトリのコードを読み取るための権限です。actions/checkoutがソースを取得するのに必要となります。
ジョブ
jobs:
build-and-deploy:
name: Build and Deploy
runs-on: ubuntu-latest
ジョブとして、build-and-deployというジョブを定義しています。名前にBuild and Deploy、ランナーとしてubuntu-latestを指定しているため、ubuntuの最新のランナー環境で動作します。
ステップ1:コードをチェックアウト
- name: Checkout source
uses: actions/checkout@v5
mainブランチへのpushをトリガーとして実行されるため、このステップではmainブランチの最新のコードがチェックアウトされます。
ステップ2:AWS認証情報を設定
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v5
with:
role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/github-actions-deploy-role
aws-region: ${{ env.AWS_REGION }}
aws-actions/configure-aws-credentialsは、AWSにログインするための認証情報を設定するアクションです。role-to-assumeにはAWSに接続するときに使用するIAMロールを、aws-regionには操作対象のリージョンを指定します。
aws-actions/configure-aws-credentials
ステップ3:ECRへのログイン
- name: Login to Amazon ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v2
aws-actions/amazon-ecr-loginで ECR へのログインを行います。このアクションを使うためには、事前にステップ2でAWSの認証情報を設定しておく必要があります。
ECRへの認証は、内部的には認可トークンを取得して docker login に渡す仕組みになっています。Private registry authentication in Amazon ECR(AWS公式ドキュメント)の原文(英語表示時)では以下のように述べられています。
An authentication token is used to access any Amazon ECR registry that your IAM principal has access to and is valid for 12 hours.
「認可トークンはIAMプリンシパルがアクセスできる任意のECRレジストリに対して有効で、12時間で失効する」と読み取れます。aws-actions/amazon-ecr-login はこのトークン取得と docker login を裏側でまとめて実行してくれるアクションです。なお、AWSドキュメントは画面右上の言語セレクタから日本語表示に切り替えられます。
ステップ4:Dockerイメージをビルド&プッシュ
- name: Build and Push Docker image
run: |
IMAGE_REPO=${{ steps.login-ecr.outputs.registry }}/${{ env.ECR_REPOSITORY }}
docker build -t $IMAGE_REPO:latest .
docker push $IMAGE_REPO:latest
Dockerコマンドを使って、イメージのビルドとプッシュを行います。docker build -t $IMAGE_REPO:latest .は、カレントディレクトリにあるDockerfileを使ってDockerイメージを作成するコマンドです。今回はFastAPIのコードがリポジトリのルート直下に配置されているため、ビルドコンテキストとして.(カレントディレクトリ)を指定しています。
docker pushは、作成したイメージをECRのリポジトリにアップロードするコマンドです。
ステップ5:タスク定義のプレースホルダを置換
- name: Render task definition
run: |
sed -i "s|<AWS_ACCOUNT_ID>|${{ secrets.AWS_ACCOUNT_ID }}|g" ecs-taskdef.json
ecs-taskdef.jsonに書かれた<AWS_ACCOUNT_ID>というプレースホルダを、GitHubシークレットに登録したAWSアカウントIDの値で置換します。これによりリポジトリには常にプレースホルダだけが残り、アカウントIDが直接コミットされない状態を保てます。
sed -iはファイルを直接書き換えるオプションで、s|置換前|置換後|gという構文で全出現箇所を一括置換します。
ステップ6:ECSにデプロイ
- name: Deploy to ECS
uses: aws-actions/amazon-ecs-deploy-task-definition@v2
with:
task-definition: ecs-taskdef.json
service: ${{ env.ECS_SERVICE }}
cluster: ${{ env.ECS_CLUSTER }}
wait-for-service-stability: true
aws-actions/amazon-ecs-deploy-task-definitionで ECS へのデプロイを行います。task-definitionには直前のステップで置換済みのecs-taskdef.jsonを、serviceとclusterには対象のECSサービス名とクラスタ名を指定します。
wait-for-service-stability: trueを指定すると、デプロイ後に新しいタスクが正常に稼働し、サービスが安定状態になるまで待機します。これを指定しないと、タスクが起動しきっていない状態でワークフローが終了してしまい、不具合に気づきにくくなる可能性があります。
6.5 タスク定義ファイルの作成
ワークフローのステップ6でtask-definition: ecs-taskdef.jsonを参照していました。このファイルは、ECSに対して「どのようなコンテナを・どのようなリソース設定で動かすのか」を伝えるためのタスク定義ファイルです。ワークフロー実行時にこのファイルを読み込み、新しいタスク定義をECSに登録してサービスを更新します。
リポジトリのルート(devopscamp-cicd-handson/直下)にecs-taskdef.jsonというファイルを作成します。
devopscamp-cicd-handson/
├── ecs-taskdef.json ← このファイルを作成
├── Dockerfile
├── main.py
└── ...
作成したファイルに以下の内容をそのまま記述して保存します。<AWS_ACCOUNT_ID>はプレースホルダで、ワークフロー実行時にGitHubシークレットの値で自動置換するため、ここではそのままで構いません。
{
"family": "test-task",
"networkMode": "awsvpc",
"requiresCompatibilities": ["FARGATE"],
"cpu": "256",
"memory": "512",
"executionRoleArn": "arn:aws:iam::<AWS_ACCOUNT_ID>:role/ecsTaskExecutionRole-cicd-handson",
"containerDefinitions": [
{
"name": "app",
"image": "<AWS_ACCOUNT_ID>.dkr.ecr.ap-northeast-1.amazonaws.com/test-repository:latest",
"portMappings": [
{
"containerPort": 8000,
"protocol": "tcp"
}
],
"essential": true,
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "/ecs/test-task",
"awslogs-region": "ap-northeast-1",
"awslogs-stream-prefix": "ecs"
}
}
}
]
}
コードを解説します。
| 項目 | 説明 |
|---|---|
family |
タスク定義の名前。CloudFormationで作成したタスク定義と同じtest-taskを指定 |
networkMode |
Fargateではawsvpcを指定する必要がある |
requiresCompatibilities |
FARGATEを指定してFargate互換のタスク定義にする |
cpu, memory |
タスクのCPU・メモリサイズ |
executionRoleArn |
ECSタスクがCloudWatch Logsへの書き込みなどを行うために必要なロール |
containerDefinitions.image |
ECRにPushされたイメージのURI |
containerDefinitions.portMappings |
コンテナが公開するポート(FastAPIはデフォルトで8000) |
containerDefinitions.logConfiguration |
ログをCloudWatch Logsに送る設定 |
networkMode: awsvpc がFargateで必須である点は、Amazon ECS task definition parameters for Fargate(AWS公式ドキュメント)に明記されています。原文(英語表示時)では以下のように述べられています。
For Amazon ECS tasks hosted on Fargate, the
awsvpcnetwork mode is required.
「Fargate上のECSタスクでは awsvpc ネットワークモードが必須である」と読み取れます。各パラメータの取りうる値や cpu / memory の組み合わせ表も同ページにまとまっています。なお、AWSドキュメントは画面右上の言語セレクタから日本語表示に切り替えられます。
| 💡 ポイント |
|---|
imageは:latestを指定していますが、GitHub Actionsのワークフロー実行時に、この:latestタグのイメージがECRに最新のイメージとしてPushされた状態で、ECSに新しいタスク定義として登録されます。つまり、デプロイのたびに常に最新のイメージが使われる仕組みです。 |
| 💡 ポイント |
|---|
アカウントIDをこのJSONに直接書いてしまうと、GitHubシークレットに登録した意味がなくなります(リポジトリにそのまま残るため)。そこで本ハンズオンでは<AWS_ACCOUNT_ID>というプレースホルダで書いておき、ワークフロー実行時にシークレットの値で置換してから使用します。置換は次のセクションで追加するsedコマンドのステップで行います。AWSアカウントIDは厳密には機密情報ではありません(AWS公式も「秘密ではない」と明言)。ただし一度シークレットで扱うと決めたなら、成果物にもハードコードしないのが一貫性ある設計です。この考え方は、今後他の準機密情報(リージョン依存のリソースIDなど)を扱う際にも応用できます。 |
6.6 実行してみる
ワークフローの作成が完了しましたので、実際にデプロイが動くかを確認します。今回のワークフローはmainブランチへのPushで起動するため、作業ブランチ(feature/add-cicd-pipeline)の内容をPull Requestでマージすれば、mainブランチへの変更が発生してデプロイが自動実行されます。
GitHubにPushする
まず、作業ブランチで行った変更(ecs-taskdef.jsonとdeploy.ymlの追加)をGitHubにPushします。Visual Studio Codeのターミナルで、以下のコマンドを1つずつ実行します。
ステージングします。
git add .
コミットします。
git commit -m "add deploy workflow"
Pushします。
git push origin feature/add-cicd-pipeline
GitHubのリポジトリページを開いて、Pushしたファイルが反映されていることを確認します。
Pull Requestの作成
Pushが完了したので、feature/add-cicd-pipelineブランチからmainブランチに対してPull Requestを作成します。ビルドとデプロイのワークフローはmainブランチへのマージをトリガーとして実行されるため、まずPull Requestを作成し、そこからマージする流れを取ります。
GitHubのリポジトリページで、Pull requestsタブをクリックします。
New pull requestのボタンをクリックします。
ブランチとして、左側のbaseにmain、右側のcompareにfeature/add-cicd-pipelineを選択した上で、Create pull requestをクリックします。
タイトルと説明欄はそのままで問題ありません。さらにCreate pull requestをクリックすると、Pull Requestが作成されます。

Pull Requestのマージ
Pull Requestが作成できたら、そのままマージします。ビルドとデプロイのワークフローはマージをトリガーとして実行されるため、ここでマージすることで初めてデプロイが動き出します。
Pull Requestの画面で、Merge pull requestのボタンをクリックします。

続いて、確認画面でConfirm mergeボタンをクリックすると、マージが行われます。

マージが完了すると、mainブランチに対してPushが発生したことになり、Build and Deploy to ECSワークフローが自動的に起動します。GitHubのActionsタブを開くと、ワークフローの実行状況を確認できます。

しばらくするとデプロイのワークフローが正常終了します。

6.7 リソースの確認
ワークフローが正常終了したので、実際にAWS側にデプロイの結果が反映されているかを確認します。ワークフローが「成功」と表示されていても、何らかの理由で想定どおりにリソースが更新されていない可能性もあるため、AWSコンソールで直接確認しておくことが重要です。
まずは、ECRのリポジトリに、新しいイメージが作成されていることを確認します。イメージタグがlatestとなっているイメージが作成されていて、プッシュされた日時が、先ほどマージしたタイミングと同じであれば問題ありません。

続いて、ECSへのデプロイが行われていることを確認します。
ECSのクラスターからtest-serviceを選択し、デプロイタブを開くと、新しいリビジョンでのデプロイが完了していることが確認できます。

6.8 タスクの起動(DesiredCountの変更)
ECSサービスにはデプロイは反映されましたが、実はまだタスクは起動していません。CloudFormationで作成した時点でDesiredCount(実行するタスク数)を0に設定していたためです。
DesiredCountを最初から1にしなかった理由は、初回のCloudFormationスタック作成時にはECRリポジトリにイメージがまだ存在しないため、ECSがタスクを起動しようとしても失敗を繰り返してしまうためです。CI/CDパイプラインで初回のデプロイが完了し、ECRにイメージがPushされた今のタイミングで、手動でタスク数を1に変更します。
ECSのクラスターtest-clusterのサービスタブで、test-serviceを選択し、更新ボタンをクリックします。

サービス更新画面で、以下の設定を行います。
| 設定項目 | 値 | 設定の基準 |
|---|---|---|
| 必要なタスク | 1 | タスクを1つ起動させるため |

その他の項目はデフォルトのままで、更新をクリックします。

数十秒〜1分程度で、新しいタスクが起動します。ECSのクラスターtest-clusterのタスクタブを開くと、タスクが1件起動していれば、ここまでの操作は完了です。

6.9 動作確認
ECS側でタスクが起動したことを確認できたら、実際にAPIを呼び出して「本当に正常に動作しているか」を確かめます。デプロイが完了してもアプリケーション側のバグや設定ミスで動作していないケースもあるため、最後にAPIを叩いて期待通りのレスポンスが返ってくることを確認するのがベストプラクティスです。
今回はECSタスクをパブリックサブネットに配置しているため、ECSタスクのパブリックIPアドレスを使ってAPIを呼び出します。
まずは、タスクの詳細を開きます。
その後、ネットワーキングのタブを開き、パブリックIPをコピーします。

Windowsであればコマンドプロンプト、Macであればターミナルを開き、CURLコマンドでAPIのヘルスチェックエンドポイントを呼び出してみます。
curl http://<パブリックIP>:8000/health
以下のようにヘルスチェックのレスポンスが返ってくれば、コンテナが起動していることが確認できます。
{"status":"ok"}
続いて、動作確認用のエンドポイントも呼び出してみます。
curl http://<パブリックIP>:8000/
以下のようにメッセージのレスポンスが返ってくれば、アプリケーションが正常に動作しています。
{"message":"API呼び出しテストに成功しました"}
{"status":"ok"}と{"message":"API呼び出しテストに成功しました"}の両方が表示されていれば、ここまでの操作は完了です。
6.10 更新してみる
初回デプロイは成功しましたが、CI/CDパイプラインの本当の価値は「繰り返し使えること」にあります。コードを変更するたびに自動でビルド・デプロイされ、常に最新のコードが本番環境に反映されることを体験するために、APIのレスポンスメッセージを少し変更してから再度デプロイしてみます。
コードの変更
main.pyのファイルを開き、root関数のレスポンスメッセージに(ver2)を追加します。
@app.get("/")
def root():
return {"message": "API呼び出しテストに成功しました(ver2)"}
その後、変更をGitコマンドでGitHubにプッシュします。
まず、前回の作業ブランチ(feature/add-cicd-pipeline)からmainブランチに切り替え、最新のmainの状態をローカルに取り込みます。
git switch main
git pull origin main
続いて、今回の変更用に新しいブランチを作成します。
git switch -c feature/update-message
変更をステージングしてコミットします。
git add .
git commit -m "update message to ver2"
Pushします。
git push origin feature/update-message
マージ
続いて、GitHubの画面にてPull Requestを作成し、マージを行います。マージを行うと、GitHub Actionsのワークフローが正常終了することを確認します。

動作確認
CURLコマンドを実行して、変更の反映を確認します。なお、デプロイすることでECSタスクが新しくなるため、パブリックIPアドレスは変更されているのでご注意ください。
curl http://<パブリックIP>:8000/
実行結果のメッセージに(ver2)が含まれていればOKです。
{"message":"API呼び出しテストに成功しました(ver2)"}
レスポンスに(ver2)が含まれていれば、コードの変更が自動デプロイを通じて正しく反映されていることが確認できます。コードを書き換えてPushするだけで本番環境が更新されることが確認できました。これがCI/CDパイプラインの最大のメリットです。
7. リソースの削除
最後に、AWSのリソースを削除します。AWSのリソースは起動している間ずっと料金が発生し続けているため、必ず削除しておきましょう。
7.1 ECRリポジトリのイメージ削除
CloudFormationでスタックを削除する際、ECRリポジトリにイメージファイルが存在すると削除に失敗します。そのため、先にECRリポジトリ内のイメージを削除しておく必要があります。
AWSマネジメントコンソールでECRのダッシュボードを開きます。検索バーに「ECR」と入力し、「Elastic Container Registry」を選択してください。
リポジトリ一覧からtest-repositoryをクリックし、表示されているイメージをすべて選択します。選択後、削除をクリックしてください。
確認画面が表示されるため、確認文字列を入力し、削除をクリックします。

イメージの一覧が空になっていれば、ここまでの操作は成功です。
7.2 CloudFormationスタックの削除
AWSマネジメントコンソールでCloudFormationのダッシュボードを開きます。
スタック一覧からcicd-handsonを選択し、削除をクリックします。
確認画面が表示されるので、削除をクリックします。

スタックの削除が開始され、ステータスがDELETE_IN_PROGRESSに変わります。削除完了まで数分かかります。
ステータスがDELETE_COMPLETEになれば、CloudFormationで作成したリソースがすべて削除されています。スタック一覧からcicd-handsonが消えていることを確認してください。
7.3 IAMリソースの削除
このハンズオンで手動作成したIAMリソースは、CloudFormationスタックには含まれていないため、別途削除する必要があります。
AWSマネジメントコンソールでIAMのダッシュボードを開きます。
まず、左メニューからロールを選択し、github-actions-deploy-roleを検索します。該当のロールを選択し、削除をクリックしてください。確認画面でロール名を入力し、削除を実行します。

次に、左メニューからIDプロバイダを選択し、token.actions.githubusercontent.comを選択します。削除をクリックし、確認画面で削除を実行してください。

IAMのロール一覧とIDプロバイダ一覧から、それぞれのリソースが消えていれば、ここまでの操作は完了です。
8. まとめ
このハンズオンでは、GitHub ActionsとAWS ECS/Fargateを使ったFastAPIアプリケーションの自動ビルドと自動デプロイを体験しました。
- OIDCを利用してGitHub ActionsからAWSに安全に接続できる
- 自動ビルドでDockerイメージを作成しECRにプッシュできる
- 自動デプロイでECS Fargateにコンテナをデプロイできる
- Pull Requestのマージをトリガーに、デプロイまで自動実行できる
- ECSのデプロイ機能により、ダウンタイムなしで新バージョンへ切り替えられる