はじめに
こんにちは、KUDs です。
シリーズの最終回です。
今回は、GitHub に保存したら、自動で AWS に反映される仕組みを作ります。
いわゆる CI/CD と呼ばれるもので、実際の開発現場で当たり前に使われている仕組みです。
所要時間は45分ほど、費用は数円程度です。
そして、この回でもうひとつ守りたいことがあります。
長期のアクセスキーを、最後まで1つも作らない。
このシリーズはステップ5から一貫して「有効期限のない認証情報を手元に残さない」方針で進めてきました。
GitHub Actions でも、その方針を貫きます。
この記事について
この記事は、AWS を初めて触る方向けのシリーズの最終回です。
シリーズ全体の地図と読む順番は、ロードマップにまとめてあります。

前回の「CDK でインフラをコード化する」まで終わっていることを前提にしています。

今回作るもの
流れはこうです。
ファイルを変更して GitHub に push する
→ GitHub Actions が自動で動き出す
→ OIDC という仕組みで AWS に一時的な認証を受ける
→ S3 にファイルを同期する
→ サイトが更新される
あなたが打つコマンドは git push だけになります。
なぜアクセスキーを使わないのか
GitHub Actions から AWS を操作する方法として、昔からよく紹介されているのがアクセスキーを GitHub に保存する方式です。
GitHub には Secrets という秘密情報の保管場所があるので、そこにアクセスキーを入れておく、という考え方です。
この方式には問題があります。
- 有効期限がありません。 一度置いたら、削除するまでずっと有効です
- 漏れたときの被害が大きい。 GitHub のアカウントが乗っ取られたら、そのまま AWS を操作されます
- 誰も気づけません。 キーは変わらないので、使われていても分かりません
OIDC という仕組み
そこで使うのが OIDC(OpenID Connect) です。
GitHub の公式ドキュメントは、こう説明しています。
OpenID Connect (OIDC) allows your GitHub Actions workflows to access resources in Amazon Web Services (AWS), without needing to store the AWS credentials as long-lived GitHub secrets.
(OIDC を使うと、GitHub Actions のワークフローが、AWS の認証情報を長期的な GitHub Secrets として保存することなく、AWS のリソースにアクセスできます)
仕組みを簡単に言うと、こうです。
- GitHub が「このワークフローは確かに、あなたのリポジトリの main ブランチで動いています」という証明書のようなものを発行する
- AWS が「その証明書が本物で、条件に合っていれば」一時的な権限を渡す
- その権限は数時間で失効する
保存されるものが何もありません。 これが決定的な違いです。
キーを渡すのではなく、その都度、身分証を提示して入館証をもらうイメージが近いと思います。

手順1: デプロイ先を復活させる
前回の最後で cdk destroy して、バケットを消しました。
今回はそこにデプロイするので、もう一度作ります。
前回と同じフォルダで、次を実行してください。
cd ~/aws-claude-practice/infra
cdk deploy
コードが残っているので、これだけで戻ってきます。
前回「同じ環境をいつでも作り直せる」と書いたことが、さっそく役に立ちました。
手作業で作っていたら、また4つのコマンドを思い出すところでした。
出力された バケット名 と URL を控えておいてください(aws s3 ls でも確認できます)。
今回もフォルダは1つだけです
念のため確認しておきます。今回使うのは、ステップ4からずっと使っている ~/aws-claude-practice です。
| 場所 | 中身 |
|---|---|
~/aws-claude-practice/ | GitHub リポジトリ本体 |
~/aws-claude-practice/infra/ | CDK のコード(前回作成) |
~/aws-claude-practice/site/ | 公開するファイル(前回 Claude が作成) |
~/aws-claude-practice/.github/workflows/ | 今回作る自動デプロイの設定 |
新しいフォルダは作りません。 今回作るのは、この中の .github/workflows/deploy.yml 1つだけです。
手順2: IAM の権限を足す(ルートユーザーで)
今回は IAM のロールと ID プロバイダを作ります。
ところが、作業用ユーザー my-dev-user に付けた PowerUserAccess は、IAM をほとんど操作できません。
前回 cdk bootstrap でつまずいたのと、まったく同じ理由です。
そこで、ルートユーザーでログインして、my-dev-user にインラインポリシーをもう1つ足します。
IAM →「ユーザー」→ my-dev-user →「許可を追加」→「インラインポリシーを作成」→ JSON で次を貼り付けます。
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "GitHubActionsRole",
"Effect": "Allow",
"Action": [
"iam:CreateRole",
"iam:GetRole",
"iam:DeleteRole",
"iam:TagRole",
"iam:PutRolePolicy",
"iam:GetRolePolicy",
"iam:DeleteRolePolicy",
"iam:ListRolePolicies",
"iam:UpdateAssumeRolePolicy"
],
"Resource": "arn:aws:iam::*:role/github-actions-*"
},
{
"Sid": "GitHubOidcProvider",
"Effect": "Allow",
"Action": [
"iam:CreateOpenIDConnectProvider",
"iam:GetOpenIDConnectProvider",
"iam:ListOpenIDConnectProviders",
"iam:DeleteOpenIDConnectProvider"
],
"Resource": "*"
}
]
}
ロールのほうは github-actions-* という名前に絞っています。
これで、今回作るロール以外には手を出せません。
ID プロバイダのほうは * になっていますが、これはアカウント全体で1つしか作らないものなので、実害はほぼありません。
気になる方は、この記事の最後の後片付けで、このインラインポリシーごと削除してください。
「IAMFullAccess を付ける」はやらないでください
権限エラーが出たときに、IAMFullAccess を付けて解決する解説をよく見ます。
それをやると、この作業用ユーザーは自分の権限を書き換えられるようになります。
つまり、ステップ2で AdministratorAccess を避けた意味がなくなります。
面倒でも、必要な操作だけを、必要な名前に絞って足す。これが正しい対処です。
手順3: Claude Code に設定を作ってもらう
ここからが本題です。
AWS に「GitHub を信頼する」設定を作り、専用のロールを1つ作ります。
マネジメントコンソールでも作れますが、この記事では Claude Code にやってもらいます。
理由は後で説明しますが、コンソールでやると、ほぼ確実に1回失敗するからです。
先に、settings.json の1行を外します
ステップ6で、settings.json の deny にこう書きました。
"Bash(aws iam *)",
このままだと Claude は aws iam を実行できません。この1行を、いったん削除してください。
「安全設定を外すのか」と思ったかもしれません。そこを説明させてください。
本当の境界線は、さきほどのインラインポリシーです
いま my-dev-user にできる IAM 操作は、手順2で足した分だけです。
- ロールは
github-actions-*という名前のものだけ - あとは OIDC プロバイダだけ
つまり、Claude が何をしようとしても、それ以上のことは AWS 側が拒否します。
settings.json の deny は、その外側にもう1枚あるだけの柵です。
AWS 側で範囲を絞ったなら、外側の柵は一時的に開けてよい、というのが今回の判断です。
そしてもうひとつ。
ステップ6で defaultMode を default にしているので、deny を外しても、Claude は実行前に必ず承認を求めます。
あなたが中身を読んで承認するという点は、まったく変わりません。
やってはいけないのは、こういう外し方です
- エラーが出たから、とりあえず
denyを空にする IAMFullAccessを付けて、AWS 側の制限ごと無くす
順番が逆です。
正しいのは、AWS 側で「何ができるか」を必要最小限に絞ってから、Claude 側の柵を開けるという順番です。
今回は手順2でそれを済ませたので、ここで外せます。
そして、使い終わったら両方とも元に戻します。 後片付けの章でやります。
Claude に指示する
VS Code の Claude Code パネルで、こう指示してください。
GitHub Actions から AWS にデプロイするための OIDC 設定を作ってください。
1. gh コマンドで、このリポジトリのオーナーID・リポジトリID・作成日を調べる
2. その結果から、信頼ポリシーに書くべき sub の値を組み立てる
3. 信頼ポリシーと、S3 へのデプロイ権限だけを持つポリシーを ~/oidc-setup/ に作る
4. OIDC プロバイダと github-actions-deploy ロールを作成する
権限は、前回 CDK で作ったバケットに対する
アップロード・削除・一覧だけに絞ってください。
承認を求められたら、コマンドの中身を読んでから承認してください。
sub の値がいちばん間違えやすい
Claude に調べさせているのは、sub という値です。
AWS 側で「どのリポジトリからのアクセスを許すか」を指定するとき、この値で判定します。
そして 2026年7月15日に、この値の形式が変わりました。
| リポジトリの作成時期 | sub の形式 |
|---|---|
| 2026年7月15日より前 | repo:オーナー名/リポジトリ名:ref:refs/heads/main |
| 2026年7月15日以降 | repo:オーナー名@オーナーID/リポジトリ名@リポジトリID:ref:refs/heads/main |
新しい形式には数値のIDが入ります。
このシリーズを順に進めてきた方は、前回リポジトリを作ったばかりなので、新しい形式に該当します。
Claude が実行するのは、こういうコマンドです。
gh api repos/オーナー名/リポジトリ名 --jq '{created_at: .created_at, owner_id: .owner.id, repo_id: .id}'
ほとんどの日本語記事は古い形式で書かれています。 そのままコピーすると、必ず認証に失敗します。
そして、マネジメントコンソールのウィザードも古い形式を作ります。
組織名・リポジトリ名・ブランチ名を入力する画面が出ますが、あそこで作られるのは ID の入らない形式です。
コンソールで作ると、作ったあとに信頼ポリシーを手で直すことになります。 これが「ほぼ確実に1回失敗する」理由です。
Claude が実行する3つのコマンド
承認を求められるのは、次のようなコマンドです。
何をしようとしているかだけ、押さえておいてください。
aws iam create-open-id-connect-provider \
--url https://token.actions.githubusercontent.com \
--client-id-list sts.amazonaws.com
aws iam create-role \
--role-name github-actions-deploy \
--assume-role-policy-document file://~/oidc-setup/trust-policy.json
aws iam put-role-policy \
--role-name github-actions-deploy \
--policy-name s3-deploy \
--policy-document file://~/oidc-setup/s3-policy.json
指紋(サムプリント)の入力は不要です。
古い記事には 6938fd4d... のような長い文字列を入力する手順が載っていますが、現在は不要です。
AWS の公式ドキュメントにも、こう書かれています。
Prior versions of this documentation gave instructions for specifying the certificate fingerprint, but this is no longer necessary. The thumbprint, if specified, will be ignored.
(以前のドキュメントでは証明書のフィンガープリントを指定する手順を案内していましたが、現在は不要です。指定しても無視されます)
作られた中身を確認する
信頼ポリシー(trust-policy.json)は、こういう形になっているはずです。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::アカウントID:oidc-provider/token.actions.githubusercontent.com"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"token.actions.githubusercontent.com:aud": "sts.amazonaws.com",
"token.actions.githubusercontent.com:sub": "repo:オーナー名@12345678/リポジトリ名@987654321:ref:refs/heads/main"
}
}
}
]
}
sub の条件は省略できません。
AWS は、GitHub の OIDC を使う場合に sub の条件が書かれていることを必須にしています。
書かなかったり、* だけにしたりすると、保存の時点で拒否されます。
これは「他人のリポジトリからあなたの AWS を操作できてしまう」事故を防ぐための仕様です。
面倒に見えますが、ここが唯一の防波堤です。
権限のほう(s3-policy.json)は、こうなっているはずです。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:DeleteObject",
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::バケット名",
"arn:aws:s3:::バケット名/*"
]
}
]
}
このバケットにファイルを置く・消す・一覧するだけの権限です。
このロールは自動で動くものなので、権限を広く付けると、乗っ取られたときの被害がそのまま広がります。
最後に、ロールの ARN(arn:aws:iam::...:role/github-actions-deploy)を控えておいてください。
aws iam create-role の出力に含まれています。Claude に聞けば教えてくれます。
手順4: ワークフローを書く
GitHub 側の設定です。これも Claude Code に頼めます。
.github/workflows/deploy.yml を作ってください。
- main に push されたときに動く
- OIDC で さきほど作った github-actions-deploy ロールを引き受ける
- site/ フォルダの中身だけを S3 バケットに同期する
でき上がるのは、こういうファイルです。
name: Deploy to S3
on:
push:
branches:
- main
permissions:
id-token: write # OIDC のトークン発行に必要
contents: read # リポジトリの取得に必要
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: リポジトリを取得
uses: actions/checkout@v7
- name: AWS の認証情報を設定
uses: aws-actions/configure-aws-credentials@v6
with:
role-to-assume: arn:aws:iam::アカウントID:role/github-actions-deploy
aws-region: ap-northeast-1
- name: S3 に同期
run: aws s3 sync ./site s3://バケット名/ --delete
各部分の意味
| 部分 | 意味 |
|---|---|
on: push: branches: [main] | main に push されたときだけ動く |
permissions: id-token: write | OIDC を使うために必須。これがないと動きません |
contents: read | リポジトリの中身を読むために必要 |
role-to-assume | 手順3で作ったロールの ARN |
aws s3 sync ./site | site/ の中身だけをバケットに反映する |
--delete | ローカルから消えたファイルを、S3 からも消す |
permissions の2行は、両方書いてください。
permissions を書くと、書かなかった権限はすべて無効になります。
id-token: write だけ書くと contents: read が失われ、リポジトリの取得に失敗します。
【重要】同期するのは site/ だけです
aws s3 sync . s3://バケット名/ と書いてはいけません。
リポジトリの直下には、infra/(CDK のコード)や cdk.out/(生成された設計図)が入っています。
. を指定すると、それらが丸ごと、誰でも読めるバケットに公開されます。
公開バケットに置いていいのは、公開するつもりで作ったファイルだけです。
site/ というフォルダを分けておいたのは、このためでもあります。
CDK と GitHub Actions の役割分担
ひとつ補足します。
前回、CDK 側にも「site/ の中身をアップロードする」設定を入れました。
今回、同じ仕事を GitHub Actions もするようになります。
これは矛盾ではなく、役割が移ったと考えてください。
| 誰が | 何を担当するか |
|---|---|
| CDK | バケットそのものを作る(インフラ) |
| GitHub Actions | 中身を更新する(コンテンツ) |
今後、中身の更新は push だけで済みます。
cdk deploy を実行するのは、バケットの設定そのものを変えたいときだけです。
(cdk deploy を再実行すると、CDK が持っている site/ の内容で上書きされます。
どちらも同じリポジトリの site/ を見ているので、内容がずれることはありません)
手順5: push して動かす
cd ~/aws-claude-practice
git add .
git commit -m "自動デプロイの設定を追加"
git push
GitHub のリポジトリページを開き、「Actions」タブを見てください。
ワークフローが自動的に動き始めています。
緑のチェックが付いたら成功です。
サイトの URL を開いて、内容が反映されているか確認してください。
初回は失敗することが多いです。 ほとんどが sub の書き間違いなので、次の章を見てください。
手順6: 自動化を実感する
せっかくなので、変更してみましょう。
site/index.html の文章を少し書き換えて、保存し、push します。
git add .
git commit -m "文章を修正"
git push
それだけです。
AWS のコンソールを開くことも、aws コマンドを打つこともありません。
数十秒後には、サイトが更新されています。
これが CI/CD です。
【必須】後片付け
最後まで、作ったものは消しましょう。
1. S3 のリソースを消す
cd ~/aws-claude-practice/infra
cdk destroy
2. IAM ロールを削除する
aws iam delete-role-policy --role-name github-actions-deploy --policy-name s3-deploy
aws iam delete-role --role-name github-actions-deploy
インラインポリシーを先に消さないと、ロールは削除できません。
3. ID プロバイダを削除する
今後も GitHub Actions を使う予定なら、残しておいて構いません(料金はかかりません)。
消す場合は、IAM の「IDプロバイダ」から token.actions.githubusercontent.com を削除します。
4. settings.json の deny を戻す
手順3で外した1行を、deny に書き戻します。
"Bash(aws iam *)",
5. 手順2で足したインラインポリシーを消す
ここを忘れないでください。
ルートユーザーでログインし、my-dev-user から GitHubActionsRole のインラインポリシーを削除します。
(前回足した CdkBootstrapIamRoles のほうは、今後も CDK を使うなら残して構いません)
4と5は必ずセットです。
一時的に開けた柵は、使い終わったら閉める。 これが習慣にできると、事故がぐっと減ります。
「あのとき何のために開けたか分からない穴」が積み上がるのが、いちばん危ない状態です。
6. CDK の bootstrap について
前回書いたとおり、残しておいて構いません。料金はほぼ発生せず、AWS も削除して作り直すことを推奨していません。
うまくいかないときは
Q. Not authorized to perform sts:AssumeRoleWithWebIdentity と出ます
A. 9割はこれです。 信頼ポリシーの sub が、実際の値と一致していません。
リポジトリの作成日を確認して、新しい形式(IDが入る)か古い形式かを確かめてください。手順3の gh api コマンドで調べられます。
Q. Credentials could not be loaded と出ます
A. ワークフローに permissions: id-token: write が抜けています。
Q. actions/checkout のところで失敗します
A. permissions に contents: read が抜けています。permissions を書くと、書いていない権限は無効になります。
Q. プルリクエストからだと失敗します
A. sub の値が変わるためです。 ブランチへの push は ...:ref:refs/heads/main ですが、プルリクエストでは ...:pull_request になります。
今回のワークフローは main への push だけを対象にしているので、通常は起きません。
Q. Access Denied が出ます
A. ロールに付けた権限(インラインポリシー)のバケット名を確認してください。arn:aws:s3:::バケット名 と arn:aws:s3:::バケット名/* の2行とも必要です。
Q. サイトが更新されません
A. ワークフローは成功していますか? 成功しているのに反映されない場合は、aws s3 sync の対象パスを確認してください。
Q. 余計なファイルまで S3 に上がってしまいました
A. 同期の対象が .(リポジトリ全体)になっています。 aws s3 sync ./site のように、site/ だけを指定してください。
すでに上がってしまった場合は、すぐに cdk destroy でバケットごと消してください。 公開バケットに置かれたファイルは、誰でも読めます。
Q. aws iam create-role が AccessDenied になります
A. 手順2のインラインポリシーが足りていません。追加はルートユーザーで行う必要があります(my-dev-user は自分に権限を足せません)。
Q. Claude が「設定により実行できません」と言って aws iam を実行しません
A. settings.json の deny に Bash(aws iam *) が残っています。 手順3のとおり、いったん削除してください。
編集後は、Claude Code を開き直すか /permissions で反映されているか確認してください。
古い情報に注意
この分野は特に、日本語の記事が古くなっています。
| よく見る記述 | 2026年8月時点の実際 |
|---|---|
| 指紋(サムプリント)を取得して入力する | 不要。指定しても無視される |
sub は repo:オーナー/リポジトリ:ref:... | 2026年7月15日以降に作ったリポジトリはIDが入る新形式 |
信頼ポリシーに aud だけ書けばよい | sub の条件は必須。無いと保存できない |
| アクセスキーを GitHub Secrets に置く | OIDC を使えば不要。GitHub・AWS 双方が推奨 |
| コンソールのウィザードで作れば正しい信頼ポリシーができる | ウィザードは古い形式の sub を作る。新形式のリポジトリでは手直しが必要 |
よくある質問
Q. private リポジトリでも使えますか?
A. 使えます。GitHub Actions は無料プランの private リポジトリでも一定時間まで無料で使えます。
Q. 他の AWS サービスにもデプロイできますか?
A. できます。ロールに付ける権限を変えれば、Lambda でも ECS でも同じ仕組みが使えます。
Q. CDK も自動で実行できますか?
A. できますが、難易度が上がります。CDK は bootstrap で作られた別のロールを経由して動くため、そのロールを引き受ける権限も追加で必要になります。
この記事では、まず「OIDC で認証する」ことに集中するため、S3 への直接デプロイにしています。
Q. 安全設定を外すのに抵抗があります
A. その感覚は正しいです。ただ、外す前に AWS 側で範囲を絞っているのがポイントです。
my-dev-user は github-actions-* という名前のロールと OIDC プロバイダしか触れないので、Claude が何をしようとしても、それ以上は AWS が拒否します。
「AWS 側で絞ってから、Claude 側を開ける」という順番さえ守れば、危険ではありません。逆順は危険です。
Q. deny を外したままにしてはいけませんか?
A. 戻してください。 今後ずっと aws iam を使うわけではないので、開けっぱなしにする理由がありません。
インラインポリシーのほうも一緒に消します。この2つはセットで戻すと覚えてください。
Q. 複数人で使う場合は?
A. 考え方は同じですが、sub の条件をブランチ単位や環境単位で分けるなど、設計が必要になります。
Q. GitHub Actions の料金は?
A. 個人の無料プランでも、private リポジトリで毎月一定時間まで無料で使えます。public リポジトリは無料です。今回の規模なら、無料の範囲に十分収まります。
Q. 手元から aws コマンドで直接デプロイするのと、どちらがいいですか?
A. 一人で練習する分には、手元からでも構いません。
自動デプロイの価値は、手順が人に依存しなくなることにあります。「あの人しかデプロイできない」という状態を避けられます。
さいごに
GitHub に push したら自動で AWS に反映される仕組みを、アクセスキーを1つも作らずに構築しました。
やったことをまとめます。
- CDK でデプロイ先を復活させる(コードがあるから作り直せる)
- ルートユーザーで IAM の権限を、名前を絞って足す
- AWS 側で絞ってから Claude 側の
denyを開け、Claude に設定を作らせる(subの形式に注意) - ワークフローを書く(
permissionsの2行と、同期対象はsite/だけ) - push して自動デプロイを確認する
- 後片付けをする(開けた柵は
denyもインラインポリシーも戻す)
シリーズを終えて
これでシリーズは完結です。 ここまでお付き合いいただき、ありがとうございました。
振り返ると、こんな道のりでした。
- AWS が何かを知り、安全装置を先に付けた
- ツールを入れ、AI に AWS を操作させる環境を作った
- 自分の Web サイトを公開した
- コードを GitHub で管理し、インフラをコード化し、デプロイを自動化した
最初は「AWS は難しそう」だったところから、実際の開発現場で使われている流れを一通り経験したことになります。
そして、このシリーズを通して一貫していたことが2つあります。
1つは、安全装置を先に付けること。
予算アラート、IAM の権限、Claude Code の禁止ルール。何かを作る前に、必ず上限を作ってきました。
2つめは、長期のアクセスキーを作らなかったこと。
aws login、そして今回の OIDC。最後まで、有効期限のない認証情報を1つも作らずに済みました。
どちらも「面倒だから」と飛ばされがちなところですが、事故が起きてから学ぶには代償が大きすぎる部分です。
ここから先へ
次に何をするかは自由ですが、いくつか方向性を挙げておきます。
- Lambda で動くものを作る(サーバーレス。使わなければほぼ無料)
- Amazon Bedrock で生成AIを組み込む
- AWS 認定資格に挑戦する(手を動かした経験があると、用語の定着が段違いです)
もう、何を作るにも困らない土台ができています。
あとは作りたいものを、Claude Code に相談しながら進めてみてください。
シリーズの全体像は、ロードマップから確認できます。

KUDs のサービス
KUDs では、実際に AWS 上で動く Web サービスを公開しています。
「個人でもこのくらいのものは作れる」という参考にしていただければ嬉しいです。


ご質問やご指摘は contact@kuds.jp までお寄せください。
「ここで詰まった」という報告は、記事を良くするうえでいちばんありがたいフィードバックです。
コメント