diff --git a/lib/deploy-substrate/deploy-substrate.template.yaml b/lib/deploy-substrate/deploy-substrate.template.yaml index 8ba5426..8a13b89 100644 --- a/lib/deploy-substrate/deploy-substrate.template.yaml +++ b/lib/deploy-substrate/deploy-substrate.template.yaml @@ -507,11 +507,46 @@ Resources: - arn:aws:secretsmanager:us-east-1:011934824531:secret:afi-slack-webhook-T4oR3G - !Ref AWS::NoValue + # ── front-integrations (PLAT-72) — prod-only exact secret ARNs + DDB ─ + # Derived from live SAM template + secrets created in seahaven-prod + # 2026-08-05. No secret:front-integrations/* patterns. + - !If + - IsProdAccount + - Sid: FrontIntegrationsSecrets + Effect: Allow + Action: + - secretsmanager:GetSecretValue + Resource: + - arn:aws:secretsmanager:us-east-1:011934824531:secret:front-integrations/front-api-token-UXKv0U + - arn:aws:secretsmanager:us-east-1:011934824531:secret:front-integrations/slack-bot-token-giGfA7 + - arn:aws:secretsmanager:us-east-1:011934824531:secret:front-integrations/google-service-account-dqv3Bo + - !Ref AWS::NoValue + - !If + - IsProdAccount + - Sid: FrontIntegrationsDynamoDB + 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/front-sla-alerts" + - !Sub "arn:aws:dynamodb:us-east-1:${AWS::AccountId}:table/front-sla-alerts/index/*" + - !Ref AWS::NoValue + # ── FURTHER PER-WORKLOAD DATA-PLANE STATEMENTS ────────────────────── - # Floor above + afi-backup-monitor widening (PLAT-56). 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. + # Floor above + afi-backup-monitor (PLAT-56) + front-integrations + # (PLAT-72). 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. # # WHY the floor stayed empty of data-plane (decided 2026-07-30, Adam): # The security win of INFRA-186 comes from DELETION, not enumeration. diff --git a/lib/terraform-substrate/terraform-substrate.template.yaml b/lib/terraform-substrate/terraform-substrate.template.yaml index ece4fe6..2494263 100644 --- a/lib/terraform-substrate/terraform-substrate.template.yaml +++ b/lib/terraform-substrate/terraform-substrate.template.yaml @@ -545,3 +545,185 @@ Resources: Resource: - !Sub "arn:aws:s3:::afi-backup-monitor-artifacts-${AWS::AccountId}" - !Sub "arn:aws:s3:::afi-backup-monitor-artifacts-${AWS::AccountId}/*" + + # --------------------------------------------------------------------------- + # front-integrations (PLAT-72) — plan + apply roles for workspace + # front-integrations-prod. Copy shape from afi-backup-monitor above; extend + # for DynamoDB table front-sla-alerts, CloudWatch alarms, and site-alerts SNS. + # --------------------------------------------------------------------------- + HcptfFrontIntegrationsPlanRole: + Type: AWS::IAM::Role + Condition: IsProdAccount + Properties: + RoleName: hcptf-front-integrations-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:front-integrations-prod:run_phase:plan + ManagedPolicyArns: + - arn:aws:iam::aws:policy/job-function/ViewOnlyAccess + Policies: + - PolicyName: front-integrations-plan-refresh + PolicyDocument: + Version: "2012-10-17" + Statement: + - Sid: RefreshIamRoles + Effect: Allow + Action: + - iam:GetRole + - iam:GetRolePolicy + - iam:ListRolePolicies + - iam:ListAttachedRolePolicies + Resource: + - !Sub "arn:aws:iam::${AWS::AccountId}:role/tf-managed/front-*" + - 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/front-*" + - 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:front-*" + - Sid: RefreshArtifactsBucket + Effect: Allow + Action: + - s3:Get* + - s3:ListBucket + Resource: + - !Sub "arn:aws:s3:::front-integrations-artifacts-${AWS::AccountId}" + - !Sub "arn:aws:s3:::front-integrations-artifacts-${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/front-sla-alerts" + - Sid: RefreshCloudWatchAlarms + Effect: Allow + Action: + - cloudwatch:DescribeAlarms + - cloudwatch:ListTagsForResource + Resource: + - !Sub "arn:aws:cloudwatch:us-east-1:${AWS::AccountId}:alarm:front-*" + - Sid: RefreshLogs + Effect: Allow + Action: + - logs:DescribeLogGroups + - logs:ListTagsForResource + Resource: "*" + + HcptfFrontIntegrationsApplyRole: + Type: AWS::IAM::Role + Condition: IsProdAccount + Properties: + RoleName: hcptf-front-integrations + 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:front-integrations-prod:run_phase:apply + ManagedPolicyArns: + - !Ref HcptfIamManagementPolicy + Policies: + - PolicyName: front-integrations-services + PolicyDocument: + Version: "2012-10-17" + Statement: + - Sid: LambdaAll + Effect: Allow + Action: + - lambda:* + Resource: + - !Sub "arn:aws:lambda:us-east-1:${AWS::AccountId}:function:front-*" + - 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/front-*" + - Sid: CloudWatchLogs + Effect: Allow + Action: + - logs:CreateLogGroup + - logs:DeleteLogGroup + - logs:PutRetentionPolicy + - logs:DeleteRetentionPolicy + - logs:TagResource + - logs:UntagResource + - logs:ListTagsForResource + Resource: + - !Sub "arn:aws:logs:us-east-1:${AWS::AccountId}:log-group:/aws/lambda/front-*" + - Sid: CloudWatchLogsDescribe + Effect: Allow + Action: + - logs:DescribeLogGroups + Resource: "*" + - Sid: LambdaArtifactsBucket + Effect: Allow + Action: + - s3:* + Resource: + - !Sub "arn:aws:s3:::front-integrations-artifacts-${AWS::AccountId}" + - !Sub "arn:aws:s3:::front-integrations-artifacts-${AWS::AccountId}/*" + - Sid: DynamoDBTable + Effect: Allow + Action: + - dynamodb:* + Resource: + - !Sub "arn:aws:dynamodb:us-east-1:${AWS::AccountId}:table/front-sla-alerts" + - !Sub "arn:aws:dynamodb:us-east-1:${AWS::AccountId}:table/front-sla-alerts/index/*" + - Sid: DynamoDBList + Effect: Allow + Action: + - dynamodb:ListTables + Resource: "*" + - Sid: CloudWatchAlarms + Effect: Allow + Action: + - cloudwatch:* + Resource: + - !Sub "arn:aws:cloudwatch:us-east-1:${AWS::AccountId}:alarm:front-*" + - Sid: SiteAlertsSns + Effect: Allow + Action: + - sns:Publish + - sns:GetTopicAttributes + Resource: + - !Sub "arn:aws:sns:us-east-1:${AWS::AccountId}:site-alerts"