feat(iam): add hcptf roles and boundary widen for meal-order-manager (PLAT-70) (#97)

* feat(iam): add hcptf roles and boundary widen for meal-order-manager

Append plan/apply OIDC roles for meal-order-manager-prod and widen the lambda execution boundary with exact prod secret ARN and data-plane statements.

* fix(iam): make meal-order plan role Lambda refresh read-only

Replace plan-role lambda:* with Get*/List* so plan-phase credentials cannot mutate functions or layers.
This commit is contained in:
Adam Moussa 2026-08-07 19:36:20 -04:00 • committed by GitHub
parent a6f22880db
commit 44b672ae9a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 402 additions and 1 deletions

View file

@ -692,9 +692,96 @@ Resources:
- !Sub "arn:aws:cloudfront::${AWS::AccountId}:distribution/*"
- !Ref AWS::NoValue
# ── meal-order-manager (PLAT-70) — prod-only exact secret ARN + data plane ─
# Derived from meal-order-manager template.yaml + secrets created in
# seahaven-prod 2026-08-07. Exact ARN only (no secret:meal-order-* patterns).
- !If
- IsProdAccount
- Sid: MealOrderManagerSecrets
Effect: Allow
Action:
- secretsmanager:GetSecretValue
Resource:
- arn:aws:secretsmanager:us-east-1:011934824531:secret:meal-order-manager/slack-bot-token-ZGmSGw
- !Ref AWS::NoValue
- !If
- IsProdAccount
- Sid: MealOrderManagerDynamoDB
Effect: Allow
Action:
- dynamodb:GetItem
- dynamodb:PutItem
- dynamodb:UpdateItem
- dynamodb:DeleteItem
- dynamodb:Query
- dynamodb:Scan
- dynamodb:BatchGetItem
- dynamodb:BatchWriteItem
- dynamodb:DescribeTable
- dynamodb:ConditionCheckItem
Resource:
- !Sub "arn:aws:dynamodb:us-east-1:${AWS::AccountId}:table/meal-order-manager-orders"
- !Sub "arn:aws:dynamodb:us-east-1:${AWS::AccountId}:table/meal-order-manager-orders/index/*"
- !Ref AWS::NoValue
- !If
- IsProdAccount
- Sid: MealOrderManagerS3
Effect: Allow
Action:
- s3:GetObject*
- s3:GetBucket*
- s3:List*
- s3:PutObject*
- s3:DeleteObject*
- s3:AbortMultipartUpload
Resource:
- !Sub "arn:aws:s3:::meal-order-manager-form-${AWS::AccountId}"
- !Sub "arn:aws:s3:::meal-order-manager-form-${AWS::AccountId}/*"
- !Sub "arn:aws:s3:::meal-order-manager-reports-${AWS::AccountId}"
- !Sub "arn:aws:s3:::meal-order-manager-reports-${AWS::AccountId}/*"
- !Ref AWS::NoValue
- !If
- IsProdAccount
- Sid: MealOrderManagerSsm
Effect: Allow
Action:
- ssm:GetParameter
- ssm:GetParameters
Resource:
- !Sub "arn:aws:ssm:us-east-1:${AWS::AccountId}:parameter/meal-order-manager/*"
- !Ref AWS::NoValue
- !If
- IsProdAccount
- Sid: MealOrderManagerLambdaInvoke
Effect: Allow
Action:
- lambda:InvokeFunction
Resource:
- !Sub "arn:aws:lambda:us-east-1:${AWS::AccountId}:function:meal-order-manager-*"
- !Ref AWS::NoValue
- !If
- IsProdAccount
- Sid: MealOrderManagerSes
Effect: Allow
Action:
- ses:SendRawEmail
- ses:SendEmail
Resource: "*"
- !Ref AWS::NoValue
- !If
- IsProdAccount
- Sid: MealOrderManagerSns
Effect: Allow
Action:
- sns:Publish
Resource:
- !Sub "arn:aws:sns:us-east-1:${AWS::AccountId}:site-alerts"
- !Ref AWS::NoValue
# ── FURTHER PER-WORKLOAD DATA-PLANE STATEMENTS ──────────────────────
# Floor above + afi-backup-monitor (PLAT-56) + front-integrations
# (PLAT-72) + procurement-ingest (PLAT-86) + seahaven-site (PLAT-91).
# (PLAT-72) + procurement-ingest (PLAT-86) + seahaven-site (PLAT-91)
# + meal-order-manager (PLAT-70).
# Additional stacks add their own statements here, derived from THEIR
# OWN template, in their own PR, deployed to UPDATE_COMPLETE before
# first workload deploy. WIDENING PATH in the header still governs.

View file

@ -1585,3 +1585,317 @@ Resources:
- wafv2:ListWebACLs
- wafv2:ListResourcesForWebACL
Resource: "*"
# ---------------------------------------------------------------------------
# meal-order-manager-prod (PLAT-70) — HttpApi + 7 Lambdas + layer + DynamoDB
# + S3 form/reports/artifacts + CloudFront/ACM/WAF + EventBridge + alarms.
# Plan role: ViewOnly + plan-refresh sidecar. Apply role: HcptfIamManagement
# + prefix-scoped service wildcards (no enumerated Get* lists).
# ---------------------------------------------------------------------------
HcptfMealOrderManagerPlanRole:
Type: AWS::IAM::Role
Condition: IsProdAccount
Properties:
RoleName: hcptf-meal-order-manager-plan
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Federated: !Sub "arn:aws:iam::${AWS::AccountId}:oidc-provider/app.terraform.io"
Action: sts:AssumeRoleWithWebIdentity
Condition:
StringEquals:
"app.terraform.io:aud": aws.workload.identity
"app.terraform.io:sub": organization:seahaven:project:seahaven-prod:workspace:meal-order-manager-prod:run_phase:plan
ManagedPolicyArns:
- arn:aws:iam::aws:policy/job-function/ViewOnlyAccess
Policies:
- PolicyName: meal-order-manager-plan-refresh
PolicyDocument:
Version: "2012-10-17"
Statement:
- Sid: RefreshIamRoles
Effect: Allow
Action:
- iam:GetRole
- iam:GetRolePolicy
- iam:ListRolePolicies
- iam:ListAttachedRolePolicies
- iam:ListRoleTags
Resource:
- !Sub "arn:aws:iam::${AWS::AccountId}:role/tf-managed/meal-order-manager-*"
- !Sub "arn:aws:iam::${AWS::AccountId}:role/tf-managed/githubdeploy-meal-order-manager*"
- Sid: RefreshManagedPolicies
Effect: Allow
Action:
- iam:GetPolicy
- iam:GetPolicyVersion
Resource: "*"
- Sid: RefreshEventBridge
Effect: Allow
Action:
- events:DescribeRule
- events:ListTargetsByRule
- events:ListTagsForResource
Resource:
- !Sub "arn:aws:events:us-east-1:${AWS::AccountId}:rule/meal-order-manager-*"
- Sid: RefreshLambda
Effect: Allow
Action:
# Read-only refresh for plan; mutate APIs stay on the apply role.
- lambda:Get*
- lambda:List*
Resource:
- !Sub "arn:aws:lambda:us-east-1:${AWS::AccountId}:function:meal-order-manager-*"
- !Sub "arn:aws:lambda:us-east-1:${AWS::AccountId}:layer:meal-order-manager-*"
- Sid: RefreshBuckets
Effect: Allow
Action:
- s3:Get*
- s3:ListBucket
Resource:
- !Sub "arn:aws:s3:::meal-order-manager-artifacts-${AWS::AccountId}"
- !Sub "arn:aws:s3:::meal-order-manager-artifacts-${AWS::AccountId}/*"
- !Sub "arn:aws:s3:::meal-order-manager-form-${AWS::AccountId}"
- !Sub "arn:aws:s3:::meal-order-manager-form-${AWS::AccountId}/*"
- !Sub "arn:aws:s3:::meal-order-manager-reports-${AWS::AccountId}"
- !Sub "arn:aws:s3:::meal-order-manager-reports-${AWS::AccountId}/*"
- Sid: RefreshDynamoDB
Effect: Allow
Action:
- dynamodb:DescribeTable
- dynamodb:DescribeTimeToLive
- dynamodb:DescribeContinuousBackups
- dynamodb:ListTagsOfResource
Resource:
- !Sub "arn:aws:dynamodb:us-east-1:${AWS::AccountId}:table/meal-order-manager-orders"
- Sid: RefreshLogs
Effect: Allow
Action:
- logs:DescribeLogGroups
- logs:ListTagsForResource
Resource: "*"
- Sid: RefreshCloudFront
Effect: Allow
Action:
- cloudfront:Get*
- cloudfront:List*
Resource: "*"
- Sid: RefreshAcm
Effect: Allow
Action:
- acm:DescribeCertificate
- acm:ListCertificates
- acm:ListTagsForCertificate
- acm:GetCertificate
Resource: "*"
- Sid: RefreshAppWebAclSsm
Effect: Allow
Action:
- ssm:GetParameter
- ssm:GetParameters
Resource:
- !Sub "arn:aws:ssm:us-east-1:${AWS::AccountId}:parameter/seahaven/waf/app-web-acl-arn"
- !Sub "arn:aws:ssm:us-east-1:${AWS::AccountId}:parameter/meal-order-manager/*"
- Sid: RefreshWafWebAcl
Effect: Allow
Action:
- wafv2:GetWebACL
- wafv2:ListWebACLs
Resource: "*"
- Sid: RefreshHttpApi
Effect: Allow
Action:
- apigateway:GET
Resource:
- !Sub "arn:aws:apigateway:us-east-1::/apis/*"
- !Sub "arn:aws:apigateway:us-east-1::/tags/*"
- Sid: RefreshAlarms
Effect: Allow
Action:
- cloudwatch:DescribeAlarms
- cloudwatch:ListTagsForResource
Resource: "*"
HcptfMealOrderManagerApplyRole:
Type: AWS::IAM::Role
Condition: IsProdAccount
Properties:
RoleName: hcptf-meal-order-manager
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Federated: !Sub "arn:aws:iam::${AWS::AccountId}:oidc-provider/app.terraform.io"
Action: sts:AssumeRoleWithWebIdentity
Condition:
StringEquals:
"app.terraform.io:aud": aws.workload.identity
"app.terraform.io:sub": organization:seahaven:project:seahaven-prod:workspace:meal-order-manager-prod:run_phase:apply
ManagedPolicyArns:
- !Ref HcptfIamManagementPolicy
Policies:
- PolicyName: meal-order-manager-services
PolicyDocument:
Version: "2012-10-17"
Statement:
- Sid: LambdaAll
Effect: Allow
Action:
- lambda:*
Resource:
- !Sub "arn:aws:lambda:us-east-1:${AWS::AccountId}:function:meal-order-manager-*"
- !Sub "arn:aws:lambda:us-east-1:${AWS::AccountId}:layer:meal-order-manager-*"
- Sid: LambdaList
Effect: Allow
Action:
- lambda:ListFunctions
- lambda:ListLayers
- lambda:GetAccountSettings
Resource: "*"
- Sid: EventBridgeRules
Effect: Allow
Action:
- events:*
Resource:
- !Sub "arn:aws:events:us-east-1:${AWS::AccountId}:rule/meal-order-manager-*"
- Sid: CloudWatchLogs
Effect: Allow
Action:
- logs:CreateLogGroup
- logs:DeleteLogGroup
- logs:PutRetentionPolicy
- logs:DeleteRetentionPolicy
- logs:TagResource
- logs:UntagResource
- logs:ListTagsForResource
- logs:PutMetricFilter
- logs:DeleteMetricFilter
- logs:DescribeMetricFilters
Resource:
- !Sub "arn:aws:logs:us-east-1:${AWS::AccountId}:log-group:/aws/lambda/meal-order-manager-*"
- !Sub "arn:aws:logs:us-east-1:${AWS::AccountId}:log-group:/aws/apigateway/meal-order-manager*"
- Sid: CloudWatchLogsDescribe
Effect: Allow
Action:
- logs:DescribeLogGroups
Resource: "*"
- Sid: StackBuckets
Effect: Allow
Action:
- s3:*
Resource:
- !Sub "arn:aws:s3:::meal-order-manager-artifacts-${AWS::AccountId}"
- !Sub "arn:aws:s3:::meal-order-manager-artifacts-${AWS::AccountId}/*"
- !Sub "arn:aws:s3:::meal-order-manager-form-${AWS::AccountId}"
- !Sub "arn:aws:s3:::meal-order-manager-form-${AWS::AccountId}/*"
- !Sub "arn:aws:s3:::meal-order-manager-reports-${AWS::AccountId}"
- !Sub "arn:aws:s3:::meal-order-manager-reports-${AWS::AccountId}/*"
- Sid: DynamoDBTable
Effect: Allow
Action:
- dynamodb:*
Resource:
- !Sub "arn:aws:dynamodb:us-east-1:${AWS::AccountId}:table/meal-order-manager-orders"
- !Sub "arn:aws:dynamodb:us-east-1:${AWS::AccountId}:table/meal-order-manager-orders/*"
- Sid: DynamoDBList
Effect: Allow
Action:
- dynamodb:ListTables
Resource: "*"
- Sid: HttpApiManage
Effect: Allow
Action:
- apigateway:*
Resource:
- !Sub "arn:aws:apigateway:us-east-1::/apis"
- !Sub "arn:aws:apigateway:us-east-1::/apis/*"
- !Sub "arn:aws:apigateway:us-east-1::/tags/*"
- !Sub "arn:aws:apigateway:us-east-1::/vpclinks"
- !Sub "arn:aws:apigateway:us-east-1::/vpclinks/*"
- Sid: CloudFrontManage
Effect: Allow
Action:
- cloudfront:*
Resource: "*"
- Sid: AcmCreate
Effect: Allow
Action:
- acm:RequestCertificate
Resource: "*"
Condition:
StringEquals:
"aws:RequestTag/Project": meal-order-manager
- Sid: AcmList
Effect: Allow
Action:
- acm:ListCertificates
- acm:ListTagsForCertificate
Resource: "*"
- Sid: AcmManageTagged
Effect: Allow
Action:
- acm:DescribeCertificate
- acm:GetCertificate
- acm:DeleteCertificate
- acm:AddTagsToCertificate
- acm:RemoveTagsFromCertificate
- acm:RenewCertificate
Resource: "*"
Condition:
StringEquals:
"aws:ResourceTag/Project": meal-order-manager
- Sid: ReadAppWebAclSsm
Effect: Allow
Action:
- ssm:GetParameter
- ssm:GetParameters
Resource:
- !Sub "arn:aws:ssm:us-east-1:${AWS::AccountId}:parameter/seahaven/waf/app-web-acl-arn"
- Sid: MealOrderSsm
Effect: Allow
Action:
- ssm:GetParameter
- ssm:GetParameters
- ssm:PutParameter
- ssm:DeleteParameter
- ssm:AddTagsToResource
- ssm:RemoveTagsFromResource
- ssm:ListTagsForResource
Resource:
- !Sub "arn:aws:ssm:us-east-1:${AWS::AccountId}:parameter/meal-order-manager/*"
- Sid: ReadWafWebAcl
Effect: Allow
Action:
- wafv2:GetWebACL
- wafv2:GetWebACLForResource
- wafv2:ListWebACLs
- wafv2:ListResourcesForWebACL
Resource: "*"
- Sid: CloudWatchAlarms
Effect: Allow
Action:
- cloudwatch:PutMetricAlarm
- cloudwatch:DeleteAlarms
- cloudwatch:DescribeAlarms
- cloudwatch:TagResource
- cloudwatch:UntagResource
- cloudwatch:ListTagsForResource
Resource:
- !Sub "arn:aws:cloudwatch:us-east-1:${AWS::AccountId}:alarm:meal-order-manager-*"
- Sid: SnsPublishSiteAlerts
Effect: Allow
Action:
- sns:Publish
- sns:GetTopicAttributes
- sns:ListTagsForResource
Resource:
- !Sub "arn:aws:sns:us-east-1:${AWS::AccountId}:site-alerts"
- Sid: SesIdentityRead
Effect: Allow
Action:
- ses:GetIdentityVerificationAttributes
- ses:GetSendQuota
Resource: "*"