From c2c1b80b600995997ae5ef81a14236c5c114e733 Mon Sep 17 00:00:00 2001 From: Adam Moussa Date: Tue, 28 Jul 2026 18:58:51 -0400 Subject: [PATCH] feat(iam): add scoped OIDC role for the meal-order-manager weekly-menu job --- oidc-deploy-roles.yaml | 78 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/oidc-deploy-roles.yaml b/oidc-deploy-roles.yaml index ad64e94..2ac35bf 100644 --- a/oidc-deploy-roles.yaml +++ b/oidc-deploy-roles.yaml @@ -7,6 +7,10 @@ Parameters: GitHubOrg: Type: String Default: Sea-Haven-Industries + # No glob metacharacters: this value is interpolated into StringLike trust + # conditions, where a '*' override would silently open every role's trust + # to any GitHub org with a same-named repo. + AllowedPattern: "^[A-Za-z0-9-]+$" CreateOIDCProvider: Type: String Default: "false" @@ -1376,6 +1380,78 @@ Resources: Resource: - !Sub arn:aws:iam::${AWS::AccountId}:role/cdk-hnb659fds-* + # Scoped runtime role for the meal-order-manager weekly-menu workflow + # (Monday scrape + order-form publish). Deliberately narrower than the + # repo's deploy role: the scheduled job reads stack outputs and app config, + # writes menu items and the published form, and invalidates the form's + # CloudFront path. It deploys nothing, so it gets no CloudFormation write + # actions, no PassRole, and no access outside the form bucket. + MealOrderManagerWeeklyMenuRole: + Type: AWS::IAM::Role + Properties: + RoleName: github-meal-order-manager-weekly-menu + AssumeRolePolicyDocument: + Version: "2012-10-17" + Statement: + - Effect: Allow + Principal: + Federated: !Sub arn:aws:iam::${AWS::AccountId}:oidc-provider/token.actions.githubusercontent.com + Action: sts:AssumeRoleWithWebIdentity + Condition: + # StringEquals (not the sibling roles' StringLike): no wildcard is + # intended, and job_workflow_ref pins this runtime role to the ONE + # workflow it serves — unlike the deploy roles, any main-branch + # workflow must NOT be able to mint these credentials. + StringEquals: + token.actions.githubusercontent.com:aud: sts.amazonaws.com + token.actions.githubusercontent.com:sub: !Sub repo:${GitHubOrg}/meal-order-manager:ref:refs/heads/main + token.actions.githubusercontent.com:job_workflow_ref: !Sub ${GitHubOrg}/meal-order-manager/.github/workflows/weekly-menu.yml@refs/heads/main + Policies: + - PolicyName: weekly-menu-publish + PolicyDocument: + Version: "2012-10-17" + Statement: + - Effect: Allow + Action: + - cloudformation:DescribeStacks + Resource: + - !Sub arn:aws:cloudformation:us-east-1:${AWS::AccountId}:stack/meal-order-manager/* + - Effect: Allow + Action: + - secretsmanager:GetSecretValue + # Secrets Manager appends a random 6-char suffix to every secret + # ARN, so a name-based match needs a glob — but exactly six '?' + # (one char each), NOT '-*', which would also match any future + # secret extending the name (e.g. form-api-key-backup). + Resource: + - !Sub arn:aws:secretsmanager:us-east-1:${AWS::AccountId}:secret:meal-order-manager/form-api-key-?????? + - !Sub arn:aws:secretsmanager:us-east-1:${AWS::AccountId}:secret:meal-order-manager/slack-bot-token-?????? + - Effect: Allow + Action: + - ssm:GetParameter + Resource: + - !Sub arn:aws:ssm:us-east-1:${AWS::AccountId}:parameter/meal-order-manager/google-client-id + - !Sub arn:aws:ssm:us-east-1:${AWS::AccountId}:parameter/meal-order-manager/slack-channel-id + - Effect: Allow + Action: + - dynamodb:GetItem + - dynamodb:PutItem + Resource: + - !Sub arn:aws:dynamodb:us-east-1:${AWS::AccountId}:table/meal-order-manager-orders + - Effect: Allow + Action: + - s3:PutObject + Resource: + - !Sub arn:aws:s3:::meal-order-manager-form-${AWS::AccountId}/index.html + - !Sub arn:aws:s3:::meal-order-manager-form-${AWS::AccountId}/archive/*.html + - Effect: Allow + Action: + - cloudfront:CreateInvalidation + # Distribution ID = the meal-order-manager stack's DistributionId + # output (stable for the life of the distribution). + Resource: + - !Sub arn:aws:cloudfront::${AWS::AccountId}:distribution/E314J1CJJ9ZTRA + Outputs: LambdaExecutionBoundaryArn: Value: !Ref LambdaExecutionBoundary @@ -1410,3 +1486,5 @@ Outputs: Value: !GetAtt ApmWoAnalysisDeployRole.Arn SeahavenAccountBaselineDeployRoleArn: Value: !GetAtt SeahavenAccountBaselineDeployRole.Arn + MealOrderManagerWeeklyMenuRoleArn: + Value: !GetAtt MealOrderManagerWeeklyMenuRole.Arn