Skip to content

feat(infra): デプロイ権限を CI ロールに集約し、push と rollout を分離する - #107

Merged
yook11 merged 4 commits into
mainfrom
feat/aws-human-deploy-role
Aug 4, 2026
Merged

feat(infra): デプロイ権限を CI ロールに集約し、push と rollout を分離する#107
yook11 merged 4 commits into
mainfrom
feat/aws-human-deploy-role

Conversation

@yook11

@yook11 yook11 commented Aug 3, 2026

Copy link
Copy Markdown
Owner

背景

Fly / Neon 退役後、本番への反映経路は手動の terraform applyaws ecs update-service だけになっている。この経路は WorkloadAdministrator で実行されており、CI 用に定義済みの最小権限ロールとその Deny 群を丸ごと迂回していた。

「デプロイで何ができるか」の定義は oidc.tf に既にある。人間用に別ポリシーを書くとその定義が 2 箇所になるため、人間も同じロールを通す方向で解決する。あわせて、image を焼く工程と本番を入れ替える工程を分離する。

1. 人間が CI ロールを assume できるようにする

CI ロールの trust policy に、IAM Identity Center の permission set 経由の経路を追加する。

  • data.aws_iam_policy_document.github_trustci_role_trust にリネーム (GitHub Actions 以外も入るため)
  • Sid = "DeployPermissionSet" を追加。permission set 名は deploy_permission_set 変数 (default VectorDeploy)

principals に ARN のワイルドカードは書けないため、入口をアカウントにして aws:PrincipalArnArnLike で絞る。permission set のロール名は割り当てを作り直すたびに接尾辞が変わるため完全一致では書けない。両者は AND で評価されるので、実効的に通るのは deploy permission set のロールだけになる。

2. push と rollout の role を分ける

app-deploy を 2 つに割る。

role 権限 assume 条件
app-push ECR への push のみ ref:refs/heads/main (承認不要)
app-rollout ECS の rollout と ECS 限定 PassRole のみ environment:production (承認必須)

docker build は Dockerfile と依存パッケージのコードが実際に走る工程で、外部から入るコードの実行点になる。ここに本番差し替えの権限を持たせると、依存 1 つの侵害が本番に直結する。分けると、build job が侵害されても ECR に image が置かれるだけで本番は動かない。

1 つの role に両方の sub を許す案もあるが、それだと承認を経ない job からも rollout でき、ゲートが「回避しない約束」に退化する。sub を分けると、承認済み job の外では rollout 用の token がそもそも発行されない。

3. rollout job を足す

aws-app-images.ymlenvironment: production の rollout job を追加する。

  • 対象は cluster に問い合わせて数える。workflow に段の一覧を持つと locals.tf と 2 箇所になり、段の追加が黙って漏れる。proxy だけ image も workflow も別系統なので除外する
  • family の最新 ACTIVE revision を土台に image tag だけ差し替える。service の現 revision ではなく family を見るので、Terraform 由来の env / secrets の変更もここで取り込まれる
  • describe が返す読み取り専用フィールドは落として register する。未知のフィールドが増えた場合は register が失敗して気づける
  • 7 段すべてを入れ替えたあと wait services-stable で 1 段でも安定しなければ失敗させる

検証

terraform fmt / validate、workflow の YAML パースを確認済み。

1 は apply 済み (0 added, 3 changed, 0 destroyed) で、実機で確認した:

確認内容 結果
3 ロールの trust に DeployPermissionSet が入ったか 入った
permission set から各 CI ロールへ chain できるか 3 経路とも成功
permission set 単体で ECS を触れるか AccessDenied
chain 後に secret の値を読めるか explicit deny

2 と 3 は未 apply。plan は 4 to add, 0 to change, 2 to destroy

merge 前に必要な手動作業

  1. terraform apply (bootstrap) — app-deploy が消えて app-push / app-rollout ができる
  2. GitHub repository variables: AWS_PUSH_ROLE_ARN / AWS_ROLLOUT_ROLE_ARN を追加し、AWS_DEPLOY_ROLE_ARN を削除
  3. production environment に required reviewer を設定 (現状は branch policy のみで、承認ゲートが存在しない)
  4. VectorDeploy permission set のインラインポリシーが参照する role ARN を新しい 2 つに更新

Non-goals

  • ManagementAdministrator の break-glass 分離
  • SSM parameter 書き込み用の permission set
  • proxy image の rollout 自動化

🤖 Generated with Claude Code

yook11 and others added 2 commits August 4, 2026 08:01
人間の手動デプロイが admin で CI のガードレールを迂回していた。CI ロールの
trust に permission set 経由の経路を足し、人間と CI が同じ権限定義を通るようにする。

principals に ARN のワイルドカードは書けないため、入口をアカウントにして
aws:PrincipalArn の ArnLike で絞る。permission set のロール名は割り当てを
作り直すたびに接尾辞が変わるため完全一致では書けない。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
build は Dockerfile と依存パッケージのコードが実際に走る工程なので、そこに本番を
差し替える権限を持たせない。app-deploy を app-push (ECR のみ) と app-rollout
(ECS のみ) に割り、後者の sub を environment 限定にする。承認されるまで job が
起動せず OIDC token が発行されないため、承認ゲートが経路の不在として成立する。

rollout job は cluster に service を問い合わせて対象を数える。workflow に段の
一覧を持つと locals.tf と 2 箇所になり、段の追加が黙って漏れるため。family の
最新 ACTIVE revision を土台に image tag だけ差し替えるので、Terraform 由来の
env / secrets の変更もそこで取り込まれる。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@yook11 yook11 changed the title feat(infra): CI ロールを deploy permission set からも assume 可能にする feat(infra): デプロイ権限を CI ロールに集約し、push と rollout を分離する Aug 3, 2026
yook11 and others added 2 commits August 4, 2026 08:51
レビュー指摘の反映。

- role ARN を vars から secrets へ。with: の値はステップ開始時にログへ出るため、
  run: 側の ::add-mask:: では間に合わない。mask-aws-account-id は action 自身の
  出力に対する補助として併用する。
- 空ガードが到達不能だった。grep -vx は該当なしで exit 1 を返し、set -e が先に
  効く。AWS 側の失敗を握り潰さないよう list-services を単独で実行し、|| true は
  grep だけに付ける。
- rollout を 2 パス化。全段の revision を登録してから入れ替えるので、途中失敗で
  一部だけ新 revision になる窓が短くなる。
- IMMUTABLE tag の焼き直しで push が落ち、rollout に到達しない問題。既に在る
  tag は skip する。batch-get-image は image 不在でも exit 0 なので images 配列の
  長さで判定する。
- waiter の上限は botocore 側の 15 秒 x 40 回 = 10 分で、job timeout より先に
  効く。コメントを事実に直し、1 回だけ待ち直す。
- 二重 dispatch で rollout が並走しないよう concurrency を追加する。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@yook11
yook11 merged commit 9cf559a into main Aug 4, 2026
14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant