GitHub OIDC
.github/workflows/deploy.yml in the product repo assumes an IAM role via OIDC — no long-lived AWS access keys. Safe for a public repository if secrets stay in GitHub and the role trust is scoped. Deploy also supports workflow_dispatch for a manual OIDC smoke without a code push.
CI shape
Section titled “CI shape”| Workflow | When | What |
|---|---|---|
ci.yml | Pull requests | Root tests/build, web/ build, cdk synth -c enableDynamo=true; optional cdk-diff when ENABLE_CDK_DIFF=true |
deploy.yml | Push to main / workflow_dispatch | Job deploy (CDK) then deploy-amplify (zip) — separate so failures are obvious |
pr-ready.yml | PR open/sync | Warn-only coach comment (PR Comments) |
| Dependabot | Weekly | github-actions + npm |

1. Create the GitHub OIDC identity provider
Section titled “1. Create the GitHub OIDC identity provider”Once per AWS account, if missing:
- Provider URL:
https://token.actions.githubusercontent.com - Audience:
sts.amazonaws.com
2. Create an IAM role
Section titled “2. Create an IAM role”Example name github-pr-readiness-coach-deploy with trust limited to this repository and main (do not use *):
{ "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:OWNER/REPO:ref:refs/heads/main" } } } ]}Minimum deploy permissions (prefer over AdministratorAccess)
Section titled “Minimum deploy permissions (prefer over AdministratorAccess)”Scope the role to cdk deploy / cdk destroy of PrReadinessCoachStack with DynamoDB + Cognito + Amplify zip deploy. Assumes the account is already bootstrapped (CDKToolkit + cdk-hnb659fds-assets-*).
Full JSON (copy-paste ready, with ACCOUNT_ID / REGION placeholders): Deploy IAM Policy.
Sid overview:
| Sid | Covers |
|---|---|
CdkBootstrapLookup | ssm:GetParameter, CFN describe/list, sts:GetCallerIdentity, AZ describe |
CdkAssetsBucket | S3 on cdk-hnb659fds-assets-ACCOUNT_ID-REGION (+ /*) |
CloudFormationAppStack | Create/update/delete/change-sets for PrReadinessCoachStack/* and CDKToolkit/* |
IamForLambdaExecutionRole | Create/tag/pass/attach policies on role/PrReadinessCoachStack-* |
PassRoleToLambdaService | iam:PassRole to lambda.amazonaws.com for stack roles |
PassRoleCdkCfnExec | Pass cdk-hnb659fds-cfn-exec-role-ACCOUNT_ID-REGION to CloudFormation |
AssumeCdkBootstrapRoles | Assume CDK deploy / file-publishing / image-publishing / lookup roles |
LambdaFunction | CRUD + invoke/tags on function:PrReadinessCoachStack-* |
ApiGateway | REST APIs, API keys, usage plans, tags in the region |
DynamoDbOptionalRunHistory | Table + index under PrReadinessCoachStack-* |
CognitoOwnerUi | User pool / client / admin user / tags |
AmplifyHostingZipDeploy | App, branch, create/start deployment, jobs, tags |
CloudWatchLogsForLambda | Log groups under /aws/lambda/PrReadinessCoachStack-* |
Policy notes:
- This role deploys infra; it does not need
bedrock:*. Bedrock invoke stays on the Lambda execution role. - First-time
cdk bootstrapis a separate, wider one-time admin action (not this CI role). - Public repo logs: Deploy uses quiet OIDC (
scripts/configure-aws-oidc.sh) andredact-deploy-log.js. FetchApiUrl/ API key locally when setting secrets.
3. GitHub repository settings
Section titled “3. GitHub repository settings”| Name | Type | Value |
|---|---|---|
AWS_ROLE_ARN | Secret | IAM role ARN from step 2 |
AWS_REGION | Variable (optional) | e.g. ap-southeast-2 |
PR_READY_OWNER_EMAIL | Secret (preferred) or Variable | Cognito owner invite — Deploy reads secret first, then the Actions variable |
After deploy, also set PR_READY_API_URL and PR_READY_API_KEY for PR Comments. Do not commit these values. Fork PRs on a public repo do not receive Actions secrets.
4. Prove deploy CI
Section titled “4. Prove deploy CI”Push or merge to main → Actions → Deploy green (jobs deploy then deploy-amplify; outputs are masked — fetch ApiUrl / key value locally). You can also run Deploy via workflow_dispatch.