ADR-0023 · 2026-06-24 · Accepted
The CI/CD pipeline IAM roles were created manually in the AWS console during the initial setup. This worked until a change to the SAM template added CloudFront resource access that the role did not have — and the error only surfaced at runtime during the deploy.
The concrete symptom: adding AccessLogSettings to API Gateway caused CloudFormation to attempt an update on the CloudFront distribution, an operation that required permissions not present in the role. The error was discovered in production with automatic rollback, requiring two corrective deploys.
The underlying problem: without IaC, IAM permissions are invisible — no diff, no review, no history. The first successful deploy gives false confidence that permissions are correct; gaps only appear when a new category of AWS resources is touched.
IAM roles are codified in a separate CloudFormation stack and deployed via a dedicated workflow.
The app template is deployed with the IAM roles. If those roles were in the same stack, a misconfigured policy could break the very deploy that tries to fix it — a permissions dead-lock. A separate stack breaks that circular dependency.
cloudfront:* and not granular permissionsCloudFront permissions were expanded to cloudfront:* after two failed attempts with granular lists. CloudFormation requires different actions depending on the operation type, and the complete list is not exhaustively documented.
Risk analysis: the policy already includes s3:* and dynamodb:* without resource restriction, which are considerably more dangerous. Restricting only CloudFront would be false security: the floor of potential damage is set by S3 and DynamoDB. A full scope review is deferred until the project onboards external collaborators with merge permissions.
DeletionPolicy: RetainAll resources in the IAM stack carry DeletionPolicy: Retain. Accidentally deleting the stack would not delete the roles — which is critical because their absence would break all future deploys.
Any IAM permissions change goes through a Pull Request, is reviewable in the diff, and has a history in git. The IAM workflow has cancel-in-progress: false to prevent an app deploy from accidentally cancelling an in-progress role update.