2026-07-30 16:31:34 -04:00
|
|
|
AWSTemplateFormatVersion: "2010-09-09"
|
|
|
|
|
Description: >-
|
|
|
|
|
Per-account HCP Terraform deploy substrate for Sea Haven Industries:
|
|
|
|
|
the app.terraform.io OIDC identity provider and the shared boundary-gated
|
|
|
|
|
IAM guardrail policy that every per-workspace Terraform APPLY role attaches.
|
|
|
|
|
Per-workspace hcptf-* roles are NOT pre-provisioned — they are appended to
|
|
|
|
|
this template at each stack's migration time.
|
|
|
|
|
|
|
|
|
|
# PROVENANCE / DESIGN SOURCE
|
|
|
|
|
# Authored fresh 2026-07-30 (the mgmt Terraform POC's CLI-created provider and
|
|
|
|
|
# hcptf-* roles were rolled back the same day, so there is no deployed source
|
2026-07-30 16:55:45 -04:00
|
|
|
# to vendor). HcptfIamManagementPolicy DERIVES FROM the reviewed
|
|
|
|
|
# seahaven-cfn-exec-iam-management pattern in
|
2026-07-30 16:31:34 -04:00
|
|
|
# lib/deploy-substrate/deploy-substrate.template.yaml (boundary-gated
|
|
|
|
|
# CreateRole/AttachRolePolicy/PutRolePolicy/PutRolePermissionsBoundary +
|
2026-07-30 16:55:45 -04:00
|
|
|
# DenyBoundaryTampering / DenyBoundaryPolicyEdit / DenySelfMutation) but is
|
|
|
|
|
# DELIBERATELY STRICTER — it is NOT a byte-identical mirror. Do not "reconcile"
|
|
|
|
|
# the two by copying this file's statements back, or vice versa; the divergences
|
|
|
|
|
# below are load-bearing and were required by the 2026-07-30 security review
|
|
|
|
|
# (findings C1-C5, one confirmed critical + one high):
|
|
|
|
|
#
|
|
|
|
|
# 1. ROLE PATH SCOPING (review finding C2). The SAM copy's Resource
|
|
|
|
|
# `role/*` on the boundary-gated statements is justified there by SAM
|
|
|
|
|
# auto-generating execution roles at path / with no settable RolePath —
|
|
|
|
|
# a path condition would break every SAM deploy. THAT RATIONALE DOES NOT
|
|
|
|
|
# TRANSFER: Terraform's aws_iam_role supports `path` and `name_prefix`.
|
|
|
|
|
# So every role-WRITE statement here is scoped to the Terraform-owned path
|
|
|
|
|
# `role/tf-managed/*`. Terraform configs MUST set path = "/tf-managed/" on
|
|
|
|
|
# every role they create; a role created anywhere else is denied. The path
|
|
|
|
|
# is deliberately NOT `hcptf-*`, which would collide with the substrate's
|
|
|
|
|
# own hcptf-* apply/plan roles under DenySelfMutation's wildcard.
|
|
|
|
|
# 2. READ AND WRITE SPLIT (review findings C1, C3, C4). The SAM copy's
|
|
|
|
|
# IAMRoleReadAndDelete grants iam:UpdateAssumeRolePolicy / DeleteRole /
|
|
|
|
|
# DetachRolePolicy / DeleteRolePolicy / UpdateRole on Resource "*"
|
|
|
|
|
# unconditioned — a confirmed privilege-escalation primitive (repoint the
|
|
|
|
|
# AdministratorAccess CDK bootstrap role's trust policy, then assume it
|
|
|
|
|
# cross-account) that DenySelfMutation's three name patterns do not cover.
|
|
|
|
|
# Here those actions are split: reads stay on "*" (Terraform data sources
|
|
|
|
|
# need them), every destructive/mutating action is confined to
|
|
|
|
|
# `role/tf-managed/*`. This closes the escalation at the root instead of
|
|
|
|
|
# chasing it with a denylist.
|
|
|
|
|
# 3. PASSROLE SCOPING (review finding C5). The SAM copy passes any role to
|
|
|
|
|
# Lambda (its comment claims SAM-role scoping the Resource does not
|
|
|
|
|
# express). Here PassRole is confined to `role/tf-managed/*`, so one
|
|
|
|
|
# workspace cannot attach another workspace's execution role to a function
|
|
|
|
|
# it controls — that path performs no IAM write and would otherwise evade
|
|
|
|
|
# every boundary gate and Deny in this document.
|
|
|
|
|
# 4. DENYSELFMUTATION SCOPE. Extended beyond the substrate's own principals to
|
|
|
|
|
# cdk-hnb659fds-* (AdministratorAccess bootstrap roles),
|
|
|
|
|
# OrganizationAccountAccessRole, and seahaven-* (detective-control roles
|
|
|
|
|
# such as the Config recorder role, which no SCP on prod/nonprod protects
|
|
|
|
|
# from iam:DeleteRole). Defense in depth behind the path scoping above.
|
|
|
|
|
#
|
|
|
|
|
# The SAM copy retains its adjudicated accepted risks because SAM's constraints
|
|
|
|
|
# are real; this file has no such excuse. KNOWN OPEN ITEM (pre-existing, not
|
|
|
|
|
# introduced here): the org's ProtectPrivilegedRoles SCP encodes exactly the
|
|
|
|
|
# protection in (4) but is attached ONLY to the security OU — extending it to
|
|
|
|
|
# prod/nonprod is the durable org-level fix and is tracked separately.
|
2026-07-30 16:31:34 -04:00
|
|
|
#
|
|
|
|
|
# COUPLING: the boundary ARN referenced in the Conditions below is
|
|
|
|
|
# seahaven-lambda-execution-boundary, created by the seahaven-deploy-substrate
|
|
|
|
|
# stack in the same account. The reference is a literal !Sub string inside
|
|
|
|
|
# Condition values, so CloudFormation infers NO ordering edge from it —
|
|
|
|
|
# bin/app.ts carries an explicit addStackDependency on the same-account
|
|
|
|
|
# deploy-substrate stack instead. The coupling is by NAME: if the boundary
|
|
|
|
|
# policy is ever renamed or replaced, every Condition below (and the
|
|
|
|
|
# deploy-substrate copy) must change in the same piece of work. INFRA-186
|
docs(iam): resolve confirmed review findings from both INFRA-186 gates
Cross-family round 1 plus the /sh-security-review verifier confirmed 11
findings on the floor reduction, all documentation defects; no policy
statement changes. The one HIGH: the Terraform migration checklist never
widened the boundary, so a Lambda-bearing Terraform migration would deploy
green and lose every data-plane call at first invoke. Checklist step 2 now
carries the widening requirement, step 3 verifies deployed boundary content,
and the terraform-substrate header no longer reads as 'Terraform path
unaffected'. Also corrected: Description is a REPLACEMENT property (a
Description edit wedges the custom-named policy and CFN's remedy is the
forbidden rename), the sanctioned-source contradiction, the false
AWSLambdaVPCAccessExecutionRole parity claim, the KMS log-group category
error, stale size numbers (691/5,453), the same-PR widening contradiction,
per-workload residue text, a LoggingConfig silent-log-loss note, the
us-east-1 region pin rationale, and ENI DoS deferral now tracked as
INFRA-200.
2026-07-31 13:44:21 -04:00
|
|
|
# (boundary reduced to a fleet-wide floor; per-workload boundaries are
|
|
|
|
|
# INFRA-187) changed the boundary's CONTENT, not its ARN, so this file is
|
|
|
|
|
# textually untouched — but the Terraform path IS affected: the Conditions
|
|
|
|
|
# below FORCE every role a Terraform apply creates onto that boundary, and
|
|
|
|
|
# the floor carries zero data-plane permissions. A migrating stack that
|
|
|
|
|
# creates Lambda execution roles must widen the boundary per the WIDENING
|
|
|
|
|
# PATH in lib/deploy-substrate/deploy-substrate.template.yaml, deployed
|
|
|
|
|
# before its first apply (README migration checklist step 2).
|
2026-07-30 16:31:34 -04:00
|
|
|
#
|
|
|
|
|
# SIZE BUDGET: an attached managed policy document is capped at 6,144
|
|
|
|
|
# characters (whitespace excluded). The statement set below is ~2.5 KB.
|
|
|
|
|
# Measure before adding statements — len(json.dumps(doc,separators=(',',':')))
|
|
|
|
|
# on the synthesized PolicyDocument — the same wall the role INLINE limit
|
|
|
|
|
# (10,240 bytes) put the first deploy-substrate deploy into on 2026-07-27.
|
|
|
|
|
#
|
|
|
|
|
# PER-WORKSPACE ROLE ACCUMULATOR
|
|
|
|
|
# At each stack's migration, a PR appends to this template:
|
|
|
|
|
# - hcptf-<stack>-plan: read-only (ViewOnlyAccess-class), trust sub
|
|
|
|
|
# organization:seahaven:project:seahaven-<env>:workspace:<workspace>:run_phase:plan
|
|
|
|
|
# - hcptf-<stack>: apply role attaching HcptfIamManagementPolicy plus
|
|
|
|
|
# stack-scoped service statements, trust sub ...run_phase:apply
|
|
|
|
|
# All subs are exact StringEquals (never StringLike, never a wildcarded
|
|
|
|
|
# run_phase — a speculative PR plan must never hold write credentials);
|
|
|
|
|
# audience is aws.workload.identity. IAM role additions here are a mandatory
|
|
|
|
|
# GPT-4.1 cross-review + /sh-security-review trigger. See the README
|
|
|
|
|
# "Terraform substrate" section for the full migration checklist and the
|
|
|
|
|
# rollback runbook.
|
|
|
|
|
#
|
|
|
|
|
# This template is deployed via lib/terraform-substrate-stack.ts
|
|
|
|
|
# (cloudformation-include) as stack seahaven-terraform-substrate, once per
|
|
|
|
|
# member account that hosts Terraform-managed workloads (currently
|
|
|
|
|
# seahaven-prod 011934824531 and seahaven-dev 710827005802; NEVER mgmt —
|
|
|
|
|
# mgmt stays SAM until its stacks migrate out).
|
|
|
|
|
|
2026-07-30 16:55:45 -04:00
|
|
|
Parameters:
|
|
|
|
|
CreateOIDCProvider:
|
|
|
|
|
Type: String
|
|
|
|
|
Default: "true"
|
|
|
|
|
AllowedValues: ["true", "false"]
|
|
|
|
|
Description: >-
|
|
|
|
|
Set to false if the app.terraform.io OIDC provider already exists in this
|
|
|
|
|
account. An account holds exactly ONE provider per URL, so an unconditional
|
|
|
|
|
create collides. Because the provider is Retain, a FIRST-create rollback
|
|
|
|
|
(caused by any other resource in this stack failing) leaves the provider
|
|
|
|
|
behind as an orphan and the stack in ROLLBACK_COMPLETE — which cannot be
|
|
|
|
|
updated. Recovery: delete the stack, then either
|
|
|
|
|
`aws iam delete-open-id-connect-provider --open-id-connect-provider-arn
|
|
|
|
|
arn:aws:iam::<acct>:oidc-provider/app.terraform.io` before retrying, or
|
|
|
|
|
redeploy with this parameter false. Same idempotency affordance the sibling
|
|
|
|
|
deploy-substrate template carries for the GitHub provider.
|
|
|
|
|
|
|
|
|
|
Conditions:
|
|
|
|
|
ShouldCreateOIDCProvider: !Equals [!Ref CreateOIDCProvider, "true"]
|
2026-08-05 12:44:52 -04:00
|
|
|
# Per-workspace hcptf-* roles for afi-backup-monitor-prod (PLAT-56) must only
|
|
|
|
|
# exist in seahaven-prod. The same template deploys to seahaven-dev; creating
|
|
|
|
|
# prod-workspace trust there would leave dead credentials in the wrong account.
|
|
|
|
|
IsProdAccount: !Equals [!Ref "AWS::AccountId", "011934824531"]
|
2026-07-30 16:55:45 -04:00
|
|
|
|
2026-07-30 16:31:34 -04:00
|
|
|
Resources:
|
|
|
|
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
# HCP Terraform OIDC provider
|
|
|
|
|
#
|
2026-07-30 16:55:45 -04:00
|
|
|
# Created by default: Phase-0 checks (2026-07-30) confirmed neither prod nor
|
|
|
|
|
# dev has an app.terraform.io provider (the mgmt POC's copy was deleted in the
|
2026-07-30 16:31:34 -04:00
|
|
|
# same-day rollback and never existed in the member accounts). An account
|
2026-07-30 16:55:45 -04:00
|
|
|
# holds exactly ONE provider per URL — see the parameter above for the
|
|
|
|
|
# first-create rollback trap this condition exists to make recoverable.
|
2026-07-30 16:31:34 -04:00
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
TerraformCloudOIDCProvider:
|
|
|
|
|
Type: AWS::IAM::OIDCProvider
|
2026-07-30 16:55:45 -04:00
|
|
|
Condition: ShouldCreateOIDCProvider
|
2026-07-30 16:31:34 -04:00
|
|
|
Properties:
|
|
|
|
|
Url: https://app.terraform.io
|
|
|
|
|
ClientIdList:
|
|
|
|
|
# Default audience of HCP Terraform dynamic provider credentials
|
|
|
|
|
# (TFC_AWS_WORKLOAD_IDENTITY_AUDIENCE). Trust policies pin this via
|
|
|
|
|
# StringEquals on app.terraform.io:aud.
|
|
|
|
|
- aws.workload.identity
|
|
|
|
|
ThumbprintList:
|
|
|
|
|
# AWS ignores thumbprints for issuers signed by a trusted root CA
|
|
|
|
|
# (app.terraform.io qualifies) and secures trust via the CA bundle;
|
|
|
|
|
# the property is populated because CloudFormation requires a value.
|
|
|
|
|
# This is the thumbprint HashiCorp's own AWS setup documentation uses.
|
|
|
|
|
- 9e99a48a9960b14926bb7f3b02e22da2b0ab7280
|
|
|
|
|
# Every future hcptf-* role trusts this provider. Retain so deleting the
|
|
|
|
|
# stack can never delete the account's Terraform federation anchor out
|
|
|
|
|
# from under live workspaces.
|
|
|
|
|
DeletionPolicy: Retain
|
|
|
|
|
UpdateReplacePolicy: Retain
|
|
|
|
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
# Shared boundary-gated IAM guardrail policy (attached managed policy)
|
|
|
|
|
#
|
|
|
|
|
# Attached by every per-workspace Terraform APPLY role (hcptf-<stack>);
|
|
|
|
|
# NEVER by plan roles (hcptf-<stack>-plan are read-only and hold no IAM
|
|
|
|
|
# writes at all). Defined once here so all apply roles carry the identical
|
|
|
|
|
# reviewed escalation control instead of per-role copies that can drift.
|
|
|
|
|
#
|
|
|
|
|
# PRIMARY ESCALATION CONTROL (same design as INFRA-97 on the SAM side):
|
|
|
|
|
# every iam:CreateRole / AttachRolePolicy / PutRolePolicy is conditioned on
|
|
|
|
|
# the target role carrying seahaven-lambda-execution-boundary, so a role
|
|
|
|
|
# created by a Terraform apply can never exceed the boundary ceiling. The
|
|
|
|
|
# POC security review confirmed the unconditioned alternative is critical:
|
|
|
|
|
# iam:PutRolePolicy on Lambda exec roles + lambda:UpdateFunctionCode reads
|
|
|
|
|
# every secret in the account.
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
HcptfIamManagementPolicy:
|
|
|
|
|
Type: AWS::IAM::ManagedPolicy
|
|
|
|
|
Properties:
|
|
|
|
|
# Fixed name: future hcptf-* roles reference it by ARN, and a rename
|
|
|
|
|
# would detach-and-replace mid-update. Treat a rename as a coordinated
|
|
|
|
|
# migration, not an edit.
|
|
|
|
|
ManagedPolicyName: seahaven-hcptf-iam-management
|
|
|
|
|
Description: >-
|
|
|
|
|
Boundary-gated IAM role lifecycle for per-workspace Terraform apply
|
|
|
|
|
roles (hcptf-*), plus the explicit Deny backstops that keep the
|
|
|
|
|
permissions boundary from being detached or rewritten and the deploy
|
|
|
|
|
substrates' own principals from being mutated. Mirrors
|
|
|
|
|
seahaven-cfn-exec-iam-management; reconcile changes across both.
|
|
|
|
|
PolicyDocument:
|
|
|
|
|
Version: "2012-10-17"
|
|
|
|
|
Statement:
|
2026-07-30 16:55:45 -04:00
|
|
|
# Create role — MUST attach boundary AND land on the Terraform-owned
|
|
|
|
|
# path. Two independent gates: the boundary caps what the role can do,
|
|
|
|
|
# the path caps which roles this policy can touch at all. Terraform
|
|
|
|
|
# configs set path = "/tf-managed/" on every aws_iam_role.
|
2026-07-30 16:31:34 -04:00
|
|
|
- Sid: IAMCreateRoleWithBoundary
|
|
|
|
|
Effect: Allow
|
|
|
|
|
Action:
|
|
|
|
|
- iam:CreateRole
|
|
|
|
|
Resource:
|
2026-07-30 16:55:45 -04:00
|
|
|
- !Sub "arn:aws:iam::${AWS::AccountId}:role/tf-managed/*"
|
2026-07-30 16:31:34 -04:00
|
|
|
Condition:
|
|
|
|
|
StringEquals:
|
|
|
|
|
"iam:PermissionsBoundary": !Sub "arn:aws:iam::${AWS::AccountId}:policy/seahaven-lambda-execution-boundary"
|
|
|
|
|
|
|
|
|
|
# Attach managed policies — MUST have boundary already on role
|
|
|
|
|
- Sid: IAMAttachPolicyWithBoundary
|
|
|
|
|
Effect: Allow
|
|
|
|
|
Action:
|
|
|
|
|
- iam:AttachRolePolicy
|
|
|
|
|
Resource:
|
2026-07-30 16:55:45 -04:00
|
|
|
- !Sub "arn:aws:iam::${AWS::AccountId}:role/tf-managed/*"
|
2026-07-30 16:31:34 -04:00
|
|
|
Condition:
|
|
|
|
|
StringEquals:
|
|
|
|
|
"iam:PermissionsBoundary": !Sub "arn:aws:iam::${AWS::AccountId}:policy/seahaven-lambda-execution-boundary"
|
|
|
|
|
|
|
|
|
|
# Put inline policy — MUST have boundary already on role
|
|
|
|
|
- Sid: IAMPutRolePolicyWithBoundary
|
|
|
|
|
Effect: Allow
|
|
|
|
|
Action:
|
|
|
|
|
- iam:PutRolePolicy
|
|
|
|
|
Resource:
|
2026-07-30 16:55:45 -04:00
|
|
|
- !Sub "arn:aws:iam::${AWS::AccountId}:role/tf-managed/*"
|
2026-07-30 16:31:34 -04:00
|
|
|
Condition:
|
|
|
|
|
StringEquals:
|
|
|
|
|
"iam:PermissionsBoundary": !Sub "arn:aws:iam::${AWS::AccountId}:policy/seahaven-lambda-execution-boundary"
|
|
|
|
|
|
|
|
|
|
# Boundary management — SET only, never DELETE. For a delete, the
|
|
|
|
|
# iam:PermissionsBoundary condition key resolves to the boundary
|
|
|
|
|
# CURRENTLY on the target role, so a StringEquals grant would match
|
|
|
|
|
# exactly the roles the gate protects and self-defeat it (verified
|
|
|
|
|
# live against the mgmt SAM copy 2026-07-27). Terraform never needs
|
|
|
|
|
# the delete: it SETS the boundary on roles it creates, and destroy
|
|
|
|
|
# calls DeleteRole.
|
2026-07-30 16:55:45 -04:00
|
|
|
# Path-scoped as well as boundary-pinned: the condition constrains WHICH
|
|
|
|
|
# boundary may be set, not WHICH role receives it. Unscoped (as in the
|
|
|
|
|
# SAM copy) this is a one-way denial-of-service — applying the Lambda
|
|
|
|
|
# runtime boundary to the CDK bootstrap execution role collapses its
|
|
|
|
|
# permissions, and DenyBoundaryTampering below then blocks removal by
|
|
|
|
|
# this same principal (2026-07-30 review finding C3).
|
2026-07-30 16:31:34 -04:00
|
|
|
- Sid: IAMPutPermissionsBoundary
|
|
|
|
|
Effect: Allow
|
|
|
|
|
Action:
|
|
|
|
|
- iam:PutRolePermissionsBoundary
|
|
|
|
|
Resource:
|
2026-07-30 16:55:45 -04:00
|
|
|
- !Sub "arn:aws:iam::${AWS::AccountId}:role/tf-managed/*"
|
2026-07-30 16:31:34 -04:00
|
|
|
Condition:
|
|
|
|
|
StringEquals:
|
|
|
|
|
"iam:PermissionsBoundary": !Sub "arn:aws:iam::${AWS::AccountId}:policy/seahaven-lambda-execution-boundary"
|
|
|
|
|
|
|
|
|
|
# Explicit Deny backstop (AWS's NoBoundaryPolicyEdit/NoBoundaryDelete
|
|
|
|
|
# delegation pattern). A Deny is required, not merely omitting the
|
|
|
|
|
# Allow — any future Allow added to an apply role silently reopens
|
|
|
|
|
# the escalation otherwise.
|
|
|
|
|
- Sid: DenyBoundaryTampering
|
|
|
|
|
Effect: Deny
|
|
|
|
|
Action:
|
|
|
|
|
- iam:DeleteRolePermissionsBoundary
|
|
|
|
|
- iam:DeleteUserPermissionsBoundary
|
|
|
|
|
Resource:
|
|
|
|
|
- !Sub "arn:aws:iam::${AWS::AccountId}:role/*"
|
|
|
|
|
- !Sub "arn:aws:iam::${AWS::AccountId}:user/*"
|
|
|
|
|
|
|
|
|
|
# Whole seahaven-* policy family: this policy carries the Denies, so
|
|
|
|
|
# it is a higher-value target than the boundary it protects. Safe to
|
|
|
|
|
# scope broadly — no Terraform stack manages a seahaven-* managed
|
|
|
|
|
# policy, and apply roles hold no iam:CreatePolicy.
|
|
|
|
|
- Sid: DenyBoundaryPolicyEdit
|
|
|
|
|
Effect: Deny
|
|
|
|
|
Action:
|
|
|
|
|
- iam:CreatePolicyVersion
|
|
|
|
|
- iam:SetDefaultPolicyVersion
|
|
|
|
|
- iam:DeletePolicyVersion
|
|
|
|
|
- iam:DeletePolicy
|
|
|
|
|
Resource:
|
|
|
|
|
- !Sub "arn:aws:iam::${AWS::AccountId}:policy/seahaven-*"
|
|
|
|
|
|
|
|
|
|
# Self-protection for BOTH deploy substrates' principals. Without
|
|
|
|
|
# this the control is one API call from being undone —
|
|
|
|
|
# IAMRoleReadAndDelete below grants iam:DetachRolePolicy on
|
|
|
|
|
# Resource "*" unconditioned, so an apply role could detach this
|
|
|
|
|
# very policy from itself. Scope covers the Terraform substrate's
|
|
|
|
|
# own roles (hcptf-*) AND the GitHub Actions substrate's
|
|
|
|
|
# (github-cfn-execution-role, githubdeploy-*): a Terraform apply
|
|
|
|
|
# never legitimately manages any of them — hcptf-* roles are
|
|
|
|
|
# managed by THIS stack via the CDK bootstrap execution role, the
|
|
|
|
|
# GitHub-side roles by their own substrate/onboarding — so the Deny
|
|
|
|
|
# costs nothing operationally and closes the same
|
|
|
|
|
# UpdateAssumeRolePolicy-on-* repoint risk the SAM-side review
|
|
|
|
|
# flagged, for every substrate principal reachable from this path.
|
|
|
|
|
- Sid: DenySelfMutation
|
|
|
|
|
Effect: Deny
|
|
|
|
|
Action:
|
|
|
|
|
- iam:AttachRolePolicy
|
|
|
|
|
- iam:DeleteRole
|
|
|
|
|
- iam:DeleteRolePolicy
|
|
|
|
|
- iam:DeleteRolePermissionsBoundary
|
|
|
|
|
- iam:DetachRolePolicy
|
|
|
|
|
- iam:PutRolePolicy
|
|
|
|
|
- iam:PutRolePermissionsBoundary
|
|
|
|
|
- iam:UpdateAssumeRolePolicy
|
|
|
|
|
- iam:UpdateRole
|
|
|
|
|
- iam:UpdateRoleDescription
|
|
|
|
|
Resource:
|
|
|
|
|
- !Sub "arn:aws:iam::${AWS::AccountId}:role/hcptf-*"
|
|
|
|
|
- !Sub "arn:aws:iam::${AWS::AccountId}:role/github-cfn-execution-role"
|
|
|
|
|
- !Sub "arn:aws:iam::${AWS::AccountId}:role/githubdeploy-*"
|
2026-07-30 16:55:45 -04:00
|
|
|
# Extended beyond the SAM copy's three patterns (2026-07-30 review
|
|
|
|
|
# findings C1/C3/C4). cdk-hnb659fds-* carries AdministratorAccess
|
|
|
|
|
# and deploys this very stack; OrganizationAccountAccessRole is the
|
|
|
|
|
# org break-glass path; seahaven-* covers detective-control roles
|
|
|
|
|
# (e.g. the Config recorder role) that the protect-security-baseline
|
|
|
|
|
# SCP does NOT shield from iam:DeleteRole. Defense in depth — the
|
|
|
|
|
# path scoping on the write statements is the primary control.
|
|
|
|
|
- !Sub "arn:aws:iam::${AWS::AccountId}:role/cdk-hnb659fds-*"
|
|
|
|
|
- !Sub "arn:aws:iam::${AWS::AccountId}:role/OrganizationAccountAccessRole"
|
|
|
|
|
- !Sub "arn:aws:iam::${AWS::AccountId}:role/seahaven-*"
|
2026-07-30 16:31:34 -04:00
|
|
|
|
2026-07-30 16:55:45 -04:00
|
|
|
# READ-ONLY on every role/policy in the account. Terraform data sources
|
|
|
|
|
# and refresh legitimately need to read arbitrary roles; none of these
|
|
|
|
|
# actions can modify anything, so Resource "*" is safe here.
|
|
|
|
|
- Sid: IAMReadOnly
|
2026-07-30 16:31:34 -04:00
|
|
|
Effect: Allow
|
|
|
|
|
Action:
|
|
|
|
|
- iam:GetRole
|
|
|
|
|
- iam:GetRolePolicy
|
|
|
|
|
- iam:ListAttachedRolePolicies
|
|
|
|
|
- iam:ListRolePolicies
|
|
|
|
|
- iam:ListRoles
|
|
|
|
|
- iam:GetPolicy
|
|
|
|
|
- iam:GetPolicyVersion
|
|
|
|
|
- iam:ListPolicies
|
|
|
|
|
- iam:ListPolicyVersions
|
|
|
|
|
Resource: "*"
|
|
|
|
|
|
2026-07-30 16:55:45 -04:00
|
|
|
# DESTRUCTIVE / MUTATING role actions — confined to the Terraform-owned
|
|
|
|
|
# path. The SAM copy grants these on Resource "*" unconditioned, which
|
|
|
|
|
# the 2026-07-30 review confirmed as a critical escalation primitive
|
|
|
|
|
# (finding C1): iam:UpdateAssumeRolePolicy on "*" lets the principal
|
|
|
|
|
# repoint the AdministratorAccess CDK bootstrap role's trust policy to
|
|
|
|
|
# an external account and assume it. Path scoping closes that at the
|
|
|
|
|
# root rather than enumerating protected names.
|
|
|
|
|
- Sid: IAMRoleWriteScoped
|
|
|
|
|
Effect: Allow
|
|
|
|
|
Action:
|
|
|
|
|
- iam:DeleteRole
|
|
|
|
|
- iam:DeleteRolePolicy
|
|
|
|
|
- iam:DetachRolePolicy
|
|
|
|
|
- iam:TagRole
|
|
|
|
|
- iam:UntagRole
|
|
|
|
|
- iam:UpdateRole
|
|
|
|
|
- iam:UpdateRoleDescription
|
|
|
|
|
- iam:UpdateAssumeRolePolicy
|
|
|
|
|
Resource:
|
|
|
|
|
- !Sub "arn:aws:iam::${AWS::AccountId}:role/tf-managed/*"
|
|
|
|
|
|
|
|
|
|
# PassRole — Terraform passes the execution roles it created (which are
|
|
|
|
|
# on the tf-managed path, boundary-gated above) to the Lambda service.
|
|
|
|
|
# Path-scoped, not role/*: unscoped, one workspace's apply role could
|
|
|
|
|
# attach ANOTHER workspace's or a SAM stack's execution role to a
|
|
|
|
|
# function it controls and run arbitrary code as that identity — a path
|
|
|
|
|
# that performs no IAM write and so evades every boundary gate and Deny
|
|
|
|
|
# in this document (2026-07-30 review finding C5). Other target services
|
|
|
|
|
# (scheduler, apigateway, ...) are NOT granted: a stack that needs one
|
|
|
|
|
# adds a scoped PassRole statement to its own apply role at migration.
|
2026-07-30 16:31:34 -04:00
|
|
|
- Sid: IAMPassRole
|
|
|
|
|
Effect: Allow
|
|
|
|
|
Action:
|
|
|
|
|
- iam:PassRole
|
|
|
|
|
Resource:
|
2026-07-30 16:55:45 -04:00
|
|
|
- !Sub "arn:aws:iam::${AWS::AccountId}:role/tf-managed/*"
|
2026-07-30 16:31:34 -04:00
|
|
|
Condition:
|
|
|
|
|
StringEquals:
|
|
|
|
|
"iam:PassedToService": "lambda.amazonaws.com"
|
2026-08-05 12:44:52 -04:00
|
|
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
2026-08-05 16:14:16 -04:00
|
|
|
# Per-workspace role pattern (required for every future hcptf-* append)
|
|
|
|
|
# and first workload: afi-backup-monitor-prod (PLAT-56).
|
2026-08-05 12:44:52 -04:00
|
|
|
#
|
2026-08-05 16:14:16 -04:00
|
|
|
# Copy this shape — do not invent enumerated Get* allow-lists.
|
|
|
|
|
#
|
|
|
|
|
# Plan role (every stack):
|
|
|
|
|
# - Managed: ViewOnlyAccess (never ReadOnlyAccess — it grants
|
|
|
|
|
# secretsmanager:GetSecretValue / s3:GetObject / kms:Decrypt to
|
|
|
|
|
# speculative PR plans).
|
|
|
|
|
# - PLUS a stack-scoped plan-refresh sidecar. ViewOnly alone omits
|
|
|
|
|
# iam:GetRole, events:DescribeRule, and provider Lambda/S3 reads
|
|
|
|
|
# needed after a partial first apply.
|
|
|
|
|
#
|
|
|
|
|
# Apply role (Lambda / EventBridge stacks):
|
|
|
|
|
# - Attach seahaven-hcptf-iam-management.
|
|
|
|
|
# - Service grants: prefix-scoped lambda:* on function:<prefix>-* and
|
|
|
|
|
# layer:<prefix>-*, events:* on rule/<prefix>-*, and bucket-scoped
|
|
|
|
|
# s3:* on the stack artifact bucket. Enumerating provider Get*
|
|
|
|
|
# (GetFunctionCodeSigningConfig, GetBucketAcl, …) lags and fails
|
|
|
|
|
# first apply (PLAT-56).
|
|
|
|
|
#
|
|
|
|
|
# Trust: exact StringEquals on organization/project/workspace/run_phase —
|
|
|
|
|
# never StringLike, never a wildcarded run_phase. Prod-only for this
|
|
|
|
|
# pair (IsProdAccount). See README "Migration checklist".
|
2026-08-05 12:44:52 -04:00
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
HcptfAfiBackupMonitorPlanRole:
|
|
|
|
|
Type: AWS::IAM::Role
|
|
|
|
|
Condition: IsProdAccount
|
|
|
|
|
Properties:
|
|
|
|
|
RoleName: hcptf-afi-backup-monitor-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:afi-backup-monitor-prod:run_phase:plan
|
|
|
|
|
ManagedPolicyArns:
|
|
|
|
|
- arn:aws:iam::aws:policy/job-function/ViewOnlyAccess
|
2026-08-05 12:57:23 -04:00
|
|
|
Policies:
|
|
|
|
|
- PolicyName: afi-backup-monitor-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/afi-*"
|
|
|
|
|
- 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/afi-*"
|
|
|
|
|
- Sid: RefreshLambda
|
|
|
|
|
Effect: Allow
|
|
|
|
|
Action:
|
2026-08-05 12:59:19 -04:00
|
|
|
- lambda:*
|
2026-08-05 12:57:23 -04:00
|
|
|
Resource:
|
|
|
|
|
- !Sub "arn:aws:lambda:us-east-1:${AWS::AccountId}:function:afi-*"
|
|
|
|
|
- !Sub "arn:aws:lambda:us-east-1:${AWS::AccountId}:layer:afi-shared*"
|
|
|
|
|
- Sid: RefreshArtifactsBucket
|
|
|
|
|
Effect: Allow
|
|
|
|
|
Action:
|
|
|
|
|
- s3:Get*
|
|
|
|
|
- s3:ListBucket
|
|
|
|
|
Resource:
|
|
|
|
|
- !Sub "arn:aws:s3:::afi-backup-monitor-artifacts-${AWS::AccountId}"
|
|
|
|
|
- !Sub "arn:aws:s3:::afi-backup-monitor-artifacts-${AWS::AccountId}/*"
|
|
|
|
|
- Sid: RefreshLogs
|
|
|
|
|
Effect: Allow
|
|
|
|
|
Action:
|
|
|
|
|
- logs:DescribeLogGroups
|
|
|
|
|
- logs:ListTagsForResource
|
|
|
|
|
Resource: "*"
|
2026-08-05 12:44:52 -04:00
|
|
|
|
|
|
|
|
HcptfAfiBackupMonitorApplyRole:
|
|
|
|
|
Type: AWS::IAM::Role
|
|
|
|
|
Condition: IsProdAccount
|
|
|
|
|
Properties:
|
|
|
|
|
RoleName: hcptf-afi-backup-monitor
|
|
|
|
|
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:afi-backup-monitor-prod:run_phase:apply
|
|
|
|
|
ManagedPolicyArns:
|
|
|
|
|
- !Ref HcptfIamManagementPolicy
|
|
|
|
|
Policies:
|
|
|
|
|
- PolicyName: afi-backup-monitor-services
|
|
|
|
|
PolicyDocument:
|
|
|
|
|
Version: "2012-10-17"
|
|
|
|
|
Statement:
|
2026-08-05 12:59:19 -04:00
|
|
|
# lambda:*/events:* on stack prefixes — AWS provider reads many
|
|
|
|
|
# Get* attributes (e.g. GetFunctionCodeSigningConfig) that lag any
|
|
|
|
|
# enumerated allow-list (PLAT-56 first-apply misses).
|
|
|
|
|
- Sid: LambdaAll
|
2026-08-05 12:44:52 -04:00
|
|
|
Effect: Allow
|
|
|
|
|
Action:
|
2026-08-05 12:59:19 -04:00
|
|
|
- lambda:*
|
2026-08-05 12:44:52 -04:00
|
|
|
Resource:
|
|
|
|
|
- !Sub "arn:aws:lambda:us-east-1:${AWS::AccountId}:function:afi-*"
|
|
|
|
|
- !Sub "arn:aws:lambda:us-east-1:${AWS::AccountId}:layer:afi-shared*"
|
|
|
|
|
- Sid: LambdaList
|
|
|
|
|
Effect: Allow
|
|
|
|
|
Action:
|
|
|
|
|
- lambda:ListFunctions
|
|
|
|
|
- lambda:ListLayers
|
|
|
|
|
- lambda:GetAccountSettings
|
|
|
|
|
Resource: "*"
|
|
|
|
|
- Sid: EventBridgeRules
|
|
|
|
|
Effect: Allow
|
|
|
|
|
Action:
|
2026-08-05 12:59:19 -04:00
|
|
|
- events:*
|
2026-08-05 12:44:52 -04:00
|
|
|
Resource:
|
|
|
|
|
- !Sub "arn:aws:events:us-east-1:${AWS::AccountId}:rule/afi-*"
|
|
|
|
|
- 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/afi-*"
|
|
|
|
|
# logs:DescribeLogGroups is a collection action — AWS authorises it
|
|
|
|
|
# against "*" only. Scoping it to a log-group ARN is a silent no-op
|
|
|
|
|
# grant (same pitfall documented on LambdaExecutionBoundary).
|
|
|
|
|
- Sid: CloudWatchLogsDescribe
|
|
|
|
|
Effect: Allow
|
|
|
|
|
Action:
|
|
|
|
|
- logs:DescribeLogGroups
|
|
|
|
|
Resource: "*"
|
|
|
|
|
# Artifact bucket for HCP plan/apply split: zip bytes travel in the
|
|
|
|
|
# plan via aws_s3_object content_base64 (local archive_file paths
|
2026-08-05 12:56:24 -04:00
|
|
|
# from the plan worker are not on the apply worker). Action set is
|
|
|
|
|
# s3:* on this bucket only — the AWS provider reads many GetBucket*
|
|
|
|
|
# attributes (e.g. GetBucketAcl) after CreateBucket; enumerating
|
|
|
|
|
# them lags provider upgrades (PLAT-56 first-apply miss).
|
2026-08-05 12:44:52 -04:00
|
|
|
- Sid: LambdaArtifactsBucket
|
|
|
|
|
Effect: Allow
|
|
|
|
|
Action:
|
2026-08-05 12:56:24 -04:00
|
|
|
- s3:*
|
2026-08-05 12:44:52 -04:00
|
|
|
Resource:
|
|
|
|
|
- !Sub "arn:aws:s3:::afi-backup-monitor-artifacts-${AWS::AccountId}"
|
|
|
|
|
- !Sub "arn:aws:s3:::afi-backup-monitor-artifacts-${AWS::AccountId}/*"
|
2026-08-05 18:33:31 -04:00
|
|
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
# 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"
|
2026-08-05 18:46:32 -04:00
|
|
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
# Per-workspace hcptf-* roles for sh-openswe-traces-prod (PLAT-73).
|
|
|
|
|
#
|
|
|
|
|
# Storage / IAM-user stack — NOT Lambda/EventBridge. Deviations from the
|
|
|
|
|
# Lambda apply-role pattern (documented on PLAT-73):
|
|
|
|
|
# - No seahaven-lambda-execution-boundary widen (no Lambda exec roles).
|
|
|
|
|
# - No lambda:*/events:*/artifact-bucket statements.
|
|
|
|
|
# - Explicit IAM user CRUD (seahaven-hcptf-iam-management is role-path-only).
|
|
|
|
|
# - Stack-scoped s3:* on account-suffixed data + log buckets.
|
|
|
|
|
# - KMS manage for alias/sh-openswe-traces CMK.
|
|
|
|
|
# - Secrets Manager shell lifecycle on exact secret name (no Get/Put value).
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
HcptfShOpensweTracesPlanRole:
|
|
|
|
|
Type: AWS::IAM::Role
|
|
|
|
|
Condition: IsProdAccount
|
|
|
|
|
Properties:
|
|
|
|
|
RoleName: hcptf-sh-openswe-traces-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:sh-openswe-traces-prod:run_phase:plan
|
|
|
|
|
ManagedPolicyArns:
|
|
|
|
|
- arn:aws:iam::aws:policy/job-function/ViewOnlyAccess
|
|
|
|
|
Policies:
|
|
|
|
|
- PolicyName: sh-openswe-traces-plan-refresh
|
|
|
|
|
PolicyDocument:
|
|
|
|
|
Version: "2012-10-17"
|
|
|
|
|
Statement:
|
|
|
|
|
- Sid: RefreshIamUser
|
|
|
|
|
Effect: Allow
|
|
|
|
|
Action:
|
|
|
|
|
- iam:GetUser
|
|
|
|
|
- iam:GetUserPolicy
|
|
|
|
|
- iam:ListUserPolicies
|
|
|
|
|
- iam:ListAttachedUserPolicies
|
|
|
|
|
- iam:ListUserTags
|
|
|
|
|
- iam:GetAccessKeyLastUsed
|
|
|
|
|
- iam:ListAccessKeys
|
|
|
|
|
Resource:
|
|
|
|
|
- !Sub "arn:aws:iam::${AWS::AccountId}:user/sh-openswe-langsmith-export"
|
|
|
|
|
- Sid: RefreshManagedPolicies
|
|
|
|
|
Effect: Allow
|
|
|
|
|
Action:
|
|
|
|
|
- iam:GetPolicy
|
|
|
|
|
- iam:GetPolicyVersion
|
|
|
|
|
Resource: "*"
|
|
|
|
|
- Sid: RefreshBuckets
|
|
|
|
|
Effect: Allow
|
|
|
|
|
Action:
|
|
|
|
|
- s3:Get*
|
|
|
|
|
- s3:ListBucket
|
|
|
|
|
Resource:
|
|
|
|
|
- !Sub "arn:aws:s3:::sh-openswe-traces-${AWS::AccountId}"
|
|
|
|
|
- !Sub "arn:aws:s3:::sh-openswe-traces-${AWS::AccountId}/*"
|
|
|
|
|
- !Sub "arn:aws:s3:::sh-openswe-traces-logs-${AWS::AccountId}"
|
|
|
|
|
- !Sub "arn:aws:s3:::sh-openswe-traces-logs-${AWS::AccountId}/*"
|
|
|
|
|
- Sid: RefreshKms
|
|
|
|
|
Effect: Allow
|
|
|
|
|
Action:
|
|
|
|
|
- kms:Describe*
|
|
|
|
|
- kms:GetKeyPolicy
|
|
|
|
|
- kms:GetKeyRotationStatus
|
|
|
|
|
- kms:ListResourceTags
|
|
|
|
|
- kms:ListAliases
|
|
|
|
|
Resource: "*"
|
|
|
|
|
- Sid: RefreshSecret
|
|
|
|
|
Effect: Allow
|
|
|
|
|
Action:
|
|
|
|
|
- secretsmanager:DescribeSecret
|
|
|
|
|
- secretsmanager:GetResourcePolicy
|
|
|
|
|
- secretsmanager:ListSecretVersionIds
|
|
|
|
|
Resource:
|
|
|
|
|
- !Sub "arn:aws:secretsmanager:us-east-1:${AWS::AccountId}:secret:sh-openswe/langsmith-export-s3-*"
|
|
|
|
|
|
|
|
|
|
HcptfShOpensweTracesApplyRole:
|
|
|
|
|
Type: AWS::IAM::Role
|
|
|
|
|
Condition: IsProdAccount
|
|
|
|
|
Properties:
|
|
|
|
|
RoleName: hcptf-sh-openswe-traces
|
|
|
|
|
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:sh-openswe-traces-prod:run_phase:apply
|
|
|
|
|
ManagedPolicyArns:
|
|
|
|
|
- !Ref HcptfIamManagementPolicy
|
|
|
|
|
Policies:
|
|
|
|
|
- PolicyName: sh-openswe-traces-services
|
|
|
|
|
PolicyDocument:
|
|
|
|
|
Version: "2012-10-17"
|
|
|
|
|
Statement:
|
|
|
|
|
- Sid: TracesBuckets
|
|
|
|
|
Effect: Allow
|
|
|
|
|
Action:
|
|
|
|
|
- s3:*
|
|
|
|
|
Resource:
|
|
|
|
|
- !Sub "arn:aws:s3:::sh-openswe-traces-${AWS::AccountId}"
|
|
|
|
|
- !Sub "arn:aws:s3:::sh-openswe-traces-${AWS::AccountId}/*"
|
|
|
|
|
- !Sub "arn:aws:s3:::sh-openswe-traces-logs-${AWS::AccountId}"
|
|
|
|
|
- !Sub "arn:aws:s3:::sh-openswe-traces-logs-${AWS::AccountId}/*"
|
|
|
|
|
# CreateKey is account-level; pin via RequestTag matching the
|
|
|
|
|
# app provider default_tags (Project=sh-openswe-traces). Key
|
|
|
|
|
# admin after create requires the same ResourceTag — no
|
|
|
|
|
# unconstrained PutKeyPolicy/DisableKey on unrelated CMKs.
|
|
|
|
|
- Sid: TracesKmsCreate
|
|
|
|
|
Effect: Allow
|
|
|
|
|
Action:
|
|
|
|
|
- kms:CreateKey
|
|
|
|
|
Resource: "*"
|
|
|
|
|
Condition:
|
|
|
|
|
StringEquals:
|
|
|
|
|
"aws:RequestTag/Project": sh-openswe-traces
|
|
|
|
|
- Sid: TracesKmsList
|
|
|
|
|
Effect: Allow
|
|
|
|
|
Action:
|
|
|
|
|
- kms:ListAliases
|
|
|
|
|
Resource: "*"
|
|
|
|
|
- Sid: TracesKmsAlias
|
|
|
|
|
Effect: Allow
|
|
|
|
|
Action:
|
|
|
|
|
- kms:CreateAlias
|
|
|
|
|
- kms:UpdateAlias
|
|
|
|
|
- kms:DeleteAlias
|
|
|
|
|
Resource:
|
|
|
|
|
- !Sub "arn:aws:kms:us-east-1:${AWS::AccountId}:alias/sh-openswe-traces"
|
|
|
|
|
- Sid: TracesKmsKey
|
|
|
|
|
Effect: Allow
|
|
|
|
|
Action:
|
|
|
|
|
- kms:TagResource
|
|
|
|
|
- kms:UntagResource
|
|
|
|
|
- kms:ScheduleKeyDeletion
|
|
|
|
|
- kms:CancelKeyDeletion
|
|
|
|
|
- kms:EnableKeyRotation
|
|
|
|
|
- kms:DisableKeyRotation
|
|
|
|
|
- kms:PutKeyPolicy
|
|
|
|
|
- kms:DescribeKey
|
|
|
|
|
- kms:GetKeyPolicy
|
|
|
|
|
- kms:GetKeyRotationStatus
|
|
|
|
|
- kms:ListResourceTags
|
|
|
|
|
- kms:EnableKey
|
|
|
|
|
- kms:DisableKey
|
|
|
|
|
# Alias attach/detach also authorizes against the key ARN.
|
|
|
|
|
- kms:CreateAlias
|
|
|
|
|
- kms:UpdateAlias
|
|
|
|
|
- kms:DeleteAlias
|
|
|
|
|
Resource: "*"
|
|
|
|
|
Condition:
|
|
|
|
|
StringEquals:
|
|
|
|
|
"aws:ResourceTag/Project": sh-openswe-traces
|
|
|
|
|
- Sid: ExportIamUser
|
|
|
|
|
Effect: Allow
|
|
|
|
|
Action:
|
|
|
|
|
- iam:CreateUser
|
|
|
|
|
- iam:DeleteUser
|
|
|
|
|
- iam:GetUser
|
|
|
|
|
- iam:TagUser
|
|
|
|
|
- iam:UntagUser
|
|
|
|
|
- iam:UpdateUser
|
|
|
|
|
- iam:PutUserPolicy
|
|
|
|
|
- iam:DeleteUserPolicy
|
|
|
|
|
- iam:GetUserPolicy
|
|
|
|
|
- iam:ListUserPolicies
|
|
|
|
|
- iam:ListAttachedUserPolicies
|
|
|
|
|
- iam:ListUserTags
|
|
|
|
|
- iam:ListAccessKeys
|
|
|
|
|
Resource:
|
|
|
|
|
- !Sub "arn:aws:iam::${AWS::AccountId}:user/sh-openswe-langsmith-export"
|
|
|
|
|
# CreateUser is authorized against the user ARN that will exist;
|
|
|
|
|
# ListUsers is a collection action on "*".
|
|
|
|
|
- Sid: ExportIamUserList
|
|
|
|
|
Effect: Allow
|
|
|
|
|
Action:
|
|
|
|
|
- iam:ListUsers
|
|
|
|
|
- iam:GetAccountSummary
|
|
|
|
|
Resource: "*"
|
|
|
|
|
# Shell lifecycle only — no GetSecretValue / PutSecretValue /
|
|
|
|
|
# UpdateSecret so apply never renders or overwrites key material
|
|
|
|
|
# in HCP state or run logs. CreateSecret is only on
|
|
|
|
|
# ExportSecretCreate with an exact Name pin (not this ARN
|
|
|
|
|
# prefix, which would also match longer secret names).
|
|
|
|
|
- Sid: ExportSecretShell
|
|
|
|
|
Effect: Allow
|
|
|
|
|
Action:
|
|
|
|
|
- secretsmanager:DeleteSecret
|
|
|
|
|
- secretsmanager:DescribeSecret
|
|
|
|
|
- secretsmanager:GetResourcePolicy
|
|
|
|
|
- secretsmanager:PutResourcePolicy
|
|
|
|
|
- secretsmanager:DeleteResourcePolicy
|
|
|
|
|
- secretsmanager:TagResource
|
|
|
|
|
- secretsmanager:UntagResource
|
|
|
|
|
Resource:
|
|
|
|
|
- !Sub "arn:aws:secretsmanager:us-east-1:${AWS::AccountId}:secret:sh-openswe/langsmith-export-s3-*"
|
|
|
|
|
- Sid: ExportSecretCreate
|
|
|
|
|
Effect: Allow
|
|
|
|
|
Action:
|
|
|
|
|
- secretsmanager:CreateSecret
|
|
|
|
|
Resource: "*"
|
|
|
|
|
Condition:
|
|
|
|
|
StringEquals:
|
|
|
|
|
"secretsmanager:Name": sh-openswe/langsmith-export-s3
|
2026-08-07 10:41:12 -04:00
|
|
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
# procurement-ingest (PLAT-86) — plan + apply roles for workspace
|
|
|
|
|
# procurement-ingest-prod. Import-in-place of three former CDK stacks
|
|
|
|
|
# (po-ingest, WorkorderIngestStack, procurement-api). Copy shape from
|
|
|
|
|
# front-integrations; extend for S3 email buckets, SQS, SES receipt rules,
|
|
|
|
|
# API Gateway, KMS (SHOC + DynamoDB CMK manage), Secrets Manager shell/
|
|
|
|
|
# rotation, DynamoDB streams, and prefix-scoped Lambda/alarms/log groups.
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
HcptfProcurementIngestPlanRole:
|
|
|
|
|
Type: AWS::IAM::Role
|
|
|
|
|
Condition: IsProdAccount
|
|
|
|
|
Properties:
|
|
|
|
|
RoleName: hcptf-procurement-ingest-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:procurement-ingest-prod:run_phase:plan
|
|
|
|
|
ManagedPolicyArns:
|
|
|
|
|
- arn:aws:iam::aws:policy/job-function/ViewOnlyAccess
|
|
|
|
|
Policies:
|
|
|
|
|
- PolicyName: procurement-ingest-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/po-*"
|
|
|
|
|
- !Sub "arn:aws:iam::${AWS::AccountId}:role/tf-managed/workorder-*"
|
|
|
|
|
- !Sub "arn:aws:iam::${AWS::AccountId}:role/tf-managed/procurement-api"
|
|
|
|
|
- Sid: RefreshManagedPolicies
|
|
|
|
|
Effect: Allow
|
|
|
|
|
Action:
|
|
|
|
|
- iam:GetPolicy
|
|
|
|
|
- iam:GetPolicyVersion
|
|
|
|
|
Resource: "*"
|
|
|
|
|
- Sid: RefreshLambda
|
|
|
|
|
Effect: Allow
|
|
|
|
|
Action:
|
|
|
|
|
- lambda:Get*
|
|
|
|
|
Resource:
|
|
|
|
|
- !Sub "arn:aws:lambda:us-east-1:${AWS::AccountId}:function:po-*"
|
|
|
|
|
- !Sub "arn:aws:lambda:us-east-1:${AWS::AccountId}:function:workorder-*"
|
|
|
|
|
- !Sub "arn:aws:lambda:us-east-1:${AWS::AccountId}:function:procurement-api"
|
|
|
|
|
# Collection/list APIs authorize only against Resource "*".
|
2026-08-07 11:00:49 -04:00
|
|
|
# GetEventSourceMapping is authorized on the UUID mapping ARN
|
|
|
|
|
# (no FunctionArn in the request context), so it cannot share
|
|
|
|
|
# the apply-role FunctionArn condition.
|
2026-08-07 10:41:12 -04:00
|
|
|
- Sid: RefreshLambdaList
|
|
|
|
|
Effect: Allow
|
|
|
|
|
Action:
|
|
|
|
|
- lambda:ListFunctions
|
|
|
|
|
- lambda:ListEventSourceMappings
|
2026-08-07 11:00:49 -04:00
|
|
|
- lambda:GetEventSourceMapping
|
2026-08-07 10:41:12 -04:00
|
|
|
- lambda:GetAccountSettings
|
|
|
|
|
Resource: "*"
|
|
|
|
|
- Sid: RefreshArtifactsBucket
|
|
|
|
|
Effect: Allow
|
|
|
|
|
Action:
|
|
|
|
|
- s3:Get*
|
|
|
|
|
- s3:ListBucket
|
|
|
|
|
Resource:
|
|
|
|
|
- !Sub "arn:aws:s3:::procurement-ingest-artifacts-${AWS::AccountId}"
|
|
|
|
|
- !Sub "arn:aws:s3:::procurement-ingest-artifacts-${AWS::AccountId}/*"
|
|
|
|
|
- Sid: RefreshEmailBuckets
|
|
|
|
|
Effect: Allow
|
|
|
|
|
Action:
|
|
|
|
|
- s3:Get*
|
|
|
|
|
- s3:ListBucket
|
|
|
|
|
- s3:GetBucketNotification
|
|
|
|
|
- s3:GetBucketPolicy
|
|
|
|
|
- s3:GetEncryptionConfiguration
|
|
|
|
|
- s3:GetBucketTagging
|
|
|
|
|
- s3:GetBucketVersioning
|
|
|
|
|
- s3:GetBucketPublicAccessBlock
|
|
|
|
|
Resource:
|
|
|
|
|
- !Sub "arn:aws:s3:::po-ingest-emails-${AWS::AccountId}"
|
|
|
|
|
- !Sub "arn:aws:s3:::po-ingest-emails-${AWS::AccountId}/*"
|
|
|
|
|
- !Sub "arn:aws:s3:::workorder-ingest-emails-${AWS::AccountId}"
|
|
|
|
|
- !Sub "arn:aws:s3:::workorder-ingest-emails-${AWS::AccountId}/*"
|
|
|
|
|
- Sid: RefreshDynamoDB
|
|
|
|
|
Effect: Allow
|
|
|
|
|
Action:
|
|
|
|
|
- dynamodb:DescribeTable
|
|
|
|
|
- dynamodb:DescribeTimeToLive
|
|
|
|
|
- dynamodb:DescribeContinuousBackups
|
|
|
|
|
- dynamodb:DescribeStream
|
|
|
|
|
- dynamodb:ListTagsOfResource
|
|
|
|
|
Resource:
|
|
|
|
|
- !Sub "arn:aws:dynamodb:us-east-1:${AWS::AccountId}:table/purchase-orders"
|
|
|
|
|
- !Sub "arn:aws:dynamodb:us-east-1:${AWS::AccountId}:table/purchase-orders/*"
|
|
|
|
|
- !Sub "arn:aws:dynamodb:us-east-1:${AWS::AccountId}:table/verified-sites"
|
|
|
|
|
- !Sub "arn:aws:dynamodb:us-east-1:${AWS::AccountId}:table/pending-site-review"
|
|
|
|
|
- !Sub "arn:aws:dynamodb:us-east-1:${AWS::AccountId}:table/WorkOrders"
|
|
|
|
|
- !Sub "arn:aws:dynamodb:us-east-1:${AWS::AccountId}:table/WorkOrders/*"
|
|
|
|
|
- !Sub "arn:aws:dynamodb:us-east-1:${AWS::AccountId}:table/WorkOrderComments"
|
|
|
|
|
- !Sub "arn:aws:dynamodb:us-east-1:${AWS::AccountId}:table/WorkOrderComments/*"
|
2026-08-07 13:53:42 -04:00
|
|
|
- !Sub "arn:aws:dynamodb:us-east-1:${AWS::AccountId}:table/work-orders"
|
|
|
|
|
- !Sub "arn:aws:dynamodb:us-east-1:${AWS::AccountId}:table/work-orders/*"
|
|
|
|
|
- !Sub "arn:aws:dynamodb:us-east-1:${AWS::AccountId}:table/work-order-comments"
|
|
|
|
|
- !Sub "arn:aws:dynamodb:us-east-1:${AWS::AccountId}:table/work-order-comments/*"
|
2026-08-07 10:41:12 -04:00
|
|
|
- Sid: RefreshDynamoDBList
|
|
|
|
|
Effect: Allow
|
|
|
|
|
Action:
|
|
|
|
|
- dynamodb:ListStreams
|
|
|
|
|
- dynamodb:ListTables
|
|
|
|
|
Resource: "*"
|
|
|
|
|
- Sid: RefreshSqs
|
|
|
|
|
Effect: Allow
|
|
|
|
|
Action:
|
|
|
|
|
- sqs:GetQueueAttributes
|
|
|
|
|
- sqs:GetQueueUrl
|
|
|
|
|
- sqs:ListQueueTags
|
|
|
|
|
Resource:
|
|
|
|
|
- !Sub "arn:aws:sqs:us-east-1:${AWS::AccountId}:po-ingest-*"
|
|
|
|
|
- !Sub "arn:aws:sqs:us-east-1:${AWS::AccountId}:WorkorderIngestStack-*"
|
|
|
|
|
- !Sub "arn:aws:sqs:us-east-1:${AWS::AccountId}:workorder-shoc-emitter-*"
|
|
|
|
|
- Sid: RefreshCloudWatchAlarms
|
|
|
|
|
Effect: Allow
|
|
|
|
|
Action:
|
|
|
|
|
- cloudwatch:DescribeAlarms
|
|
|
|
|
- cloudwatch:ListTagsForResource
|
|
|
|
|
Resource:
|
|
|
|
|
- !Sub "arn:aws:cloudwatch:us-east-1:${AWS::AccountId}:alarm:po-*"
|
|
|
|
|
- !Sub "arn:aws:cloudwatch:us-east-1:${AWS::AccountId}:alarm:workorder-*"
|
|
|
|
|
- !Sub "arn:aws:cloudwatch:us-east-1:${AWS::AccountId}:alarm:procurement-api-*"
|
|
|
|
|
- !Sub "arn:aws:cloudwatch:us-east-1:${AWS::AccountId}:alarm:purchase-orders-*"
|
|
|
|
|
- !Sub "arn:aws:cloudwatch:us-east-1:${AWS::AccountId}:alarm:verified-sites-*"
|
|
|
|
|
- !Sub "arn:aws:cloudwatch:us-east-1:${AWS::AccountId}:alarm:pending-site-review-*"
|
|
|
|
|
- !Sub "arn:aws:cloudwatch:us-east-1:${AWS::AccountId}:alarm:WorkOrders-*"
|
|
|
|
|
- !Sub "arn:aws:cloudwatch:us-east-1:${AWS::AccountId}:alarm:WorkOrderComments-*"
|
2026-08-07 13:53:42 -04:00
|
|
|
- !Sub "arn:aws:cloudwatch:us-east-1:${AWS::AccountId}:alarm:work-orders-*"
|
|
|
|
|
- !Sub "arn:aws:cloudwatch:us-east-1:${AWS::AccountId}:alarm:work-order-comments-*"
|
2026-08-07 10:41:12 -04:00
|
|
|
- Sid: RefreshApiGateway
|
|
|
|
|
Effect: Allow
|
|
|
|
|
Action:
|
|
|
|
|
- apigateway:GET
|
|
|
|
|
Resource:
|
|
|
|
|
- !Sub "arn:aws:apigateway:us-east-1::/restapis/mvul1efda2"
|
|
|
|
|
- !Sub "arn:aws:apigateway:us-east-1::/restapis/mvul1efda2/*"
|
|
|
|
|
- arn:aws:apigateway:us-east-1::/domainnames/procurement-api.seahaven.com
|
|
|
|
|
- arn:aws:apigateway:us-east-1::/domainnames/procurement-api.seahaven.com/*
|
|
|
|
|
- Sid: RefreshKms
|
|
|
|
|
Effect: Allow
|
|
|
|
|
Action:
|
|
|
|
|
- kms:DescribeKey
|
|
|
|
|
- kms:GetKeyPolicy
|
|
|
|
|
- kms:GetKeyRotationStatus
|
|
|
|
|
- kms:ListResourceTags
|
|
|
|
|
Resource:
|
|
|
|
|
- !Sub "arn:aws:kms:us-east-1:${AWS::AccountId}:key/*"
|
|
|
|
|
- !Sub "arn:aws:kms:us-east-1:${AWS::AccountId}:alias/workorder-ingest-shoc-webhook-kms"
|
2026-08-07 11:00:49 -04:00
|
|
|
# ListAliases/ListKeys are collection APIs (Resource "*").
|
|
|
|
|
- Sid: RefreshKmsList
|
|
|
|
|
Effect: Allow
|
|
|
|
|
Action:
|
|
|
|
|
- kms:ListAliases
|
|
|
|
|
- kms:ListKeys
|
|
|
|
|
Resource: "*"
|
2026-08-07 10:41:12 -04:00
|
|
|
- Sid: RefreshSecrets
|
|
|
|
|
Effect: Allow
|
|
|
|
|
Action:
|
|
|
|
|
- secretsmanager:DescribeSecret
|
|
|
|
|
- secretsmanager:GetResourcePolicy
|
|
|
|
|
- secretsmanager:ListSecretVersionIds
|
|
|
|
|
Resource:
|
|
|
|
|
- !Sub "arn:aws:secretsmanager:us-east-1:${AWS::AccountId}:secret:workorder-ingest/shoc-webhook-hmac-*"
|
|
|
|
|
- !Sub "arn:aws:secretsmanager:us-east-1:${AWS::AccountId}:secret:procurement-ingest/web-ui-auth-token-*"
|
2026-08-07 11:00:49 -04:00
|
|
|
# OOB SSM pins used by data.aws_ssm_parameter (not in ViewOnlyAccess).
|
|
|
|
|
- Sid: RefreshSsm
|
|
|
|
|
Effect: Allow
|
|
|
|
|
Action:
|
|
|
|
|
- ssm:GetParameter
|
|
|
|
|
- ssm:GetParameters
|
|
|
|
|
Resource:
|
|
|
|
|
- !Sub "arn:aws:ssm:us-east-1:${AWS::AccountId}:parameter/seahaven/dynamodb/cmk-arn"
|
|
|
|
|
- !Sub "arn:aws:ssm:us-east-1:${AWS::AccountId}:parameter/procurement-api/custom-domain/certificate-arn"
|
2026-08-07 10:41:12 -04:00
|
|
|
- Sid: RefreshSes
|
|
|
|
|
Effect: Allow
|
|
|
|
|
Action:
|
|
|
|
|
- ses:DescribeReceiptRule
|
|
|
|
|
- ses:DescribeReceiptRuleSet
|
|
|
|
|
Resource: "*"
|
|
|
|
|
- Sid: RefreshLogs
|
|
|
|
|
Effect: Allow
|
|
|
|
|
Action:
|
|
|
|
|
- logs:DescribeLogGroups
|
|
|
|
|
- logs:DescribeMetricFilters
|
|
|
|
|
- logs:ListTagsForResource
|
|
|
|
|
Resource: "*"
|
|
|
|
|
|
|
|
|
|
HcptfProcurementIngestApplyRole:
|
|
|
|
|
Type: AWS::IAM::Role
|
|
|
|
|
Condition: IsProdAccount
|
|
|
|
|
Properties:
|
|
|
|
|
RoleName: hcptf-procurement-ingest
|
|
|
|
|
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:procurement-ingest-prod:run_phase:apply
|
|
|
|
|
ManagedPolicyArns:
|
|
|
|
|
- !Ref HcptfIamManagementPolicy
|
|
|
|
|
Policies:
|
|
|
|
|
- PolicyName: procurement-ingest-services
|
|
|
|
|
PolicyDocument:
|
|
|
|
|
Version: "2012-10-17"
|
|
|
|
|
Statement:
|
|
|
|
|
- Sid: LambdaAll
|
|
|
|
|
Effect: Allow
|
|
|
|
|
Action:
|
|
|
|
|
- lambda:*
|
|
|
|
|
Resource:
|
|
|
|
|
- !Sub "arn:aws:lambda:us-east-1:${AWS::AccountId}:function:po-*"
|
|
|
|
|
- !Sub "arn:aws:lambda:us-east-1:${AWS::AccountId}:function:workorder-*"
|
|
|
|
|
- !Sub "arn:aws:lambda:us-east-1:${AWS::AccountId}:function:procurement-api"
|
|
|
|
|
# Event source mapping ARNs are UUID-keyed; AWS authorises Create against
|
2026-08-07 11:00:49 -04:00
|
|
|
# FunctionArn. Mutating Get/Update/Delete also take the mapping ARN.
|
|
|
|
|
# GetEventSourceMapping by UUID does not carry FunctionArn in the
|
|
|
|
|
# request context, so read is unconditioned on "*"; mutate stays
|
|
|
|
|
# FunctionArn-constrained.
|
|
|
|
|
- Sid: LambdaEventSourceMappingRead
|
|
|
|
|
Effect: Allow
|
|
|
|
|
Action:
|
|
|
|
|
- lambda:GetEventSourceMapping
|
|
|
|
|
- lambda:ListTags
|
2026-08-07 11:09:06 -04:00
|
|
|
# Tag/Untag on ESM UUID ARNs do not carry FunctionArn in the
|
|
|
|
|
# request context (provider default_tags on import).
|
|
|
|
|
- lambda:TagResource
|
|
|
|
|
- lambda:UntagResource
|
2026-08-07 11:00:49 -04:00
|
|
|
Resource: "*"
|
2026-08-07 10:41:12 -04:00
|
|
|
- Sid: LambdaEventSourceMappings
|
|
|
|
|
Effect: Allow
|
|
|
|
|
Action:
|
|
|
|
|
- lambda:CreateEventSourceMapping
|
|
|
|
|
- lambda:DeleteEventSourceMapping
|
|
|
|
|
- lambda:UpdateEventSourceMapping
|
|
|
|
|
Resource: "*"
|
|
|
|
|
Condition:
|
|
|
|
|
"ForAnyValue:StringLike":
|
|
|
|
|
"lambda:FunctionArn":
|
|
|
|
|
- !Sub "arn:aws:lambda:us-east-1:${AWS::AccountId}:function:po-*"
|
|
|
|
|
- !Sub "arn:aws:lambda:us-east-1:${AWS::AccountId}:function:workorder-*"
|
|
|
|
|
- !Sub "arn:aws:lambda:us-east-1:${AWS::AccountId}:function:procurement-api"
|
|
|
|
|
- Sid: LambdaList
|
|
|
|
|
Effect: Allow
|
|
|
|
|
Action:
|
|
|
|
|
- lambda:ListFunctions
|
|
|
|
|
- lambda:ListEventSourceMappings
|
|
|
|
|
- lambda:GetAccountSettings
|
|
|
|
|
Resource: "*"
|
2026-08-07 11:00:49 -04:00
|
|
|
- Sid: SsmRead
|
|
|
|
|
Effect: Allow
|
|
|
|
|
Action:
|
|
|
|
|
- ssm:GetParameter
|
|
|
|
|
- ssm:GetParameters
|
|
|
|
|
Resource:
|
|
|
|
|
- !Sub "arn:aws:ssm:us-east-1:${AWS::AccountId}:parameter/seahaven/dynamodb/cmk-arn"
|
|
|
|
|
- !Sub "arn:aws:ssm:us-east-1:${AWS::AccountId}:parameter/procurement-api/custom-domain/certificate-arn"
|
2026-08-07 10:41:12 -04:00
|
|
|
- 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/po-*"
|
|
|
|
|
- !Sub "arn:aws:logs:us-east-1:${AWS::AccountId}:log-group:/aws/lambda/workorder-*"
|
|
|
|
|
- !Sub "arn:aws:logs:us-east-1:${AWS::AccountId}:log-group:/aws/lambda/procurement-api*"
|
|
|
|
|
- Sid: CloudWatchLogsDescribe
|
|
|
|
|
Effect: Allow
|
|
|
|
|
Action:
|
|
|
|
|
- logs:DescribeLogGroups
|
|
|
|
|
Resource: "*"
|
|
|
|
|
- Sid: ArtifactsBucket
|
|
|
|
|
Effect: Allow
|
|
|
|
|
Action:
|
|
|
|
|
- s3:*
|
|
|
|
|
Resource:
|
|
|
|
|
- !Sub "arn:aws:s3:::procurement-ingest-artifacts-${AWS::AccountId}"
|
|
|
|
|
- !Sub "arn:aws:s3:::procurement-ingest-artifacts-${AWS::AccountId}/*"
|
|
|
|
|
- Sid: EmailBuckets
|
|
|
|
|
Effect: Allow
|
|
|
|
|
Action:
|
|
|
|
|
- s3:*
|
|
|
|
|
Resource:
|
|
|
|
|
- !Sub "arn:aws:s3:::po-ingest-emails-${AWS::AccountId}"
|
|
|
|
|
- !Sub "arn:aws:s3:::po-ingest-emails-${AWS::AccountId}/*"
|
|
|
|
|
- !Sub "arn:aws:s3:::workorder-ingest-emails-${AWS::AccountId}"
|
|
|
|
|
- !Sub "arn:aws:s3:::workorder-ingest-emails-${AWS::AccountId}/*"
|
|
|
|
|
- Sid: DynamoDBTables
|
|
|
|
|
Effect: Allow
|
|
|
|
|
Action:
|
|
|
|
|
- dynamodb:*
|
|
|
|
|
Resource:
|
|
|
|
|
- !Sub "arn:aws:dynamodb:us-east-1:${AWS::AccountId}:table/purchase-orders"
|
|
|
|
|
- !Sub "arn:aws:dynamodb:us-east-1:${AWS::AccountId}:table/purchase-orders/*"
|
|
|
|
|
- !Sub "arn:aws:dynamodb:us-east-1:${AWS::AccountId}:table/verified-sites"
|
|
|
|
|
- !Sub "arn:aws:dynamodb:us-east-1:${AWS::AccountId}:table/verified-sites/*"
|
|
|
|
|
- !Sub "arn:aws:dynamodb:us-east-1:${AWS::AccountId}:table/pending-site-review"
|
|
|
|
|
- !Sub "arn:aws:dynamodb:us-east-1:${AWS::AccountId}:table/pending-site-review/*"
|
|
|
|
|
- !Sub "arn:aws:dynamodb:us-east-1:${AWS::AccountId}:table/WorkOrders"
|
|
|
|
|
- !Sub "arn:aws:dynamodb:us-east-1:${AWS::AccountId}:table/WorkOrders/*"
|
|
|
|
|
- !Sub "arn:aws:dynamodb:us-east-1:${AWS::AccountId}:table/WorkOrderComments"
|
|
|
|
|
- !Sub "arn:aws:dynamodb:us-east-1:${AWS::AccountId}:table/WorkOrderComments/*"
|
2026-08-07 13:53:42 -04:00
|
|
|
- !Sub "arn:aws:dynamodb:us-east-1:${AWS::AccountId}:table/work-orders"
|
|
|
|
|
- !Sub "arn:aws:dynamodb:us-east-1:${AWS::AccountId}:table/work-orders/*"
|
|
|
|
|
- !Sub "arn:aws:dynamodb:us-east-1:${AWS::AccountId}:table/work-order-comments"
|
|
|
|
|
- !Sub "arn:aws:dynamodb:us-east-1:${AWS::AccountId}:table/work-order-comments/*"
|
2026-08-07 10:41:12 -04:00
|
|
|
- Sid: DynamoDBList
|
|
|
|
|
Effect: Allow
|
|
|
|
|
Action:
|
|
|
|
|
- dynamodb:ListTables
|
|
|
|
|
- dynamodb:ListStreams
|
|
|
|
|
Resource: "*"
|
|
|
|
|
- Sid: SqsQueues
|
|
|
|
|
Effect: Allow
|
|
|
|
|
Action:
|
|
|
|
|
- sqs:*
|
|
|
|
|
Resource:
|
|
|
|
|
- !Sub "arn:aws:sqs:us-east-1:${AWS::AccountId}:po-ingest-*"
|
|
|
|
|
- !Sub "arn:aws:sqs:us-east-1:${AWS::AccountId}:WorkorderIngestStack-*"
|
|
|
|
|
- !Sub "arn:aws:sqs:us-east-1:${AWS::AccountId}:workorder-shoc-emitter-*"
|
|
|
|
|
- Sid: CloudWatchAlarms
|
|
|
|
|
Effect: Allow
|
|
|
|
|
Action:
|
|
|
|
|
- cloudwatch:*
|
|
|
|
|
Resource:
|
|
|
|
|
- !Sub "arn:aws:cloudwatch:us-east-1:${AWS::AccountId}:alarm:po-*"
|
|
|
|
|
- !Sub "arn:aws:cloudwatch:us-east-1:${AWS::AccountId}:alarm:workorder-*"
|
|
|
|
|
- !Sub "arn:aws:cloudwatch:us-east-1:${AWS::AccountId}:alarm:procurement-api-*"
|
|
|
|
|
- !Sub "arn:aws:cloudwatch:us-east-1:${AWS::AccountId}:alarm:purchase-orders-*"
|
|
|
|
|
- !Sub "arn:aws:cloudwatch:us-east-1:${AWS::AccountId}:alarm:verified-sites-*"
|
|
|
|
|
- !Sub "arn:aws:cloudwatch:us-east-1:${AWS::AccountId}:alarm:pending-site-review-*"
|
|
|
|
|
- !Sub "arn:aws:cloudwatch:us-east-1:${AWS::AccountId}:alarm:WorkOrders-*"
|
|
|
|
|
- !Sub "arn:aws:cloudwatch:us-east-1:${AWS::AccountId}:alarm:WorkOrderComments-*"
|
2026-08-07 13:53:42 -04:00
|
|
|
- !Sub "arn:aws:cloudwatch:us-east-1:${AWS::AccountId}:alarm:work-orders-*"
|
|
|
|
|
- !Sub "arn:aws:cloudwatch:us-east-1:${AWS::AccountId}:alarm:work-order-comments-*"
|
2026-08-07 10:41:12 -04:00
|
|
|
- Sid: SiteAlertsSns
|
|
|
|
|
Effect: Allow
|
|
|
|
|
Action:
|
|
|
|
|
- sns:Publish
|
|
|
|
|
- sns:GetTopicAttributes
|
|
|
|
|
- sns:ListTagsForResource
|
|
|
|
|
Resource:
|
|
|
|
|
- !Sub "arn:aws:sns:us-east-1:${AWS::AccountId}:site-alerts"
|
|
|
|
|
- Sid: ApiGateway
|
|
|
|
|
Effect: Allow
|
|
|
|
|
Action:
|
|
|
|
|
- apigateway:*
|
|
|
|
|
Resource:
|
|
|
|
|
- !Sub "arn:aws:apigateway:us-east-1::/restapis/mvul1efda2"
|
|
|
|
|
- !Sub "arn:aws:apigateway:us-east-1::/restapis/mvul1efda2/*"
|
|
|
|
|
- arn:aws:apigateway:us-east-1::/domainnames/procurement-api.seahaven.com
|
|
|
|
|
- arn:aws:apigateway:us-east-1::/domainnames/procurement-api.seahaven.com/*
|
2026-08-07 11:09:06 -04:00
|
|
|
# TagResource/UntagResource authorize against /tags/<arn>.
|
|
|
|
|
- arn:aws:apigateway:us-east-1::/tags/*
|
2026-08-07 10:41:12 -04:00
|
|
|
- Sid: SesReceiptRules
|
|
|
|
|
Effect: Allow
|
|
|
|
|
Action:
|
|
|
|
|
- ses:CreateReceiptRule
|
|
|
|
|
- ses:UpdateReceiptRule
|
|
|
|
|
- ses:DeleteReceiptRule
|
|
|
|
|
- ses:DescribeReceiptRule
|
|
|
|
|
- ses:SetReceiptRulePosition
|
|
|
|
|
Resource:
|
|
|
|
|
- !Sub "arn:aws:ses:us-east-1:${AWS::AccountId}:receipt-rule-set/INBOUND_MAIL:receipt-rule/ExistingRuleSetPoEmailRuleAC8E9C87-qwGDj9lBoL1G"
|
|
|
|
|
- !Sub "arn:aws:ses:us-east-1:${AWS::AccountId}:receipt-rule-set/INBOUND_MAIL:receipt-rule/ExistingRuleSetWorkorderEmailRuleEA29F845-PKtaDBvIg61a"
|
|
|
|
|
- Sid: SesDescribeRuleSet
|
|
|
|
|
Effect: Allow
|
|
|
|
|
Action:
|
|
|
|
|
- ses:DescribeReceiptRuleSet
|
|
|
|
|
Resource: "*"
|
|
|
|
|
# Key management only on the live SHOC CMK — no kms:* (excludes
|
|
|
|
|
# unconstrained key-policy/destructive ops on other keys and
|
|
|
|
|
# avoids data-plane Encrypt/Decrypt on the apply role).
|
|
|
|
|
- Sid: ShocKms
|
|
|
|
|
Effect: Allow
|
|
|
|
|
Action:
|
|
|
|
|
- kms:DescribeKey
|
|
|
|
|
- kms:GetKeyPolicy
|
|
|
|
|
- kms:GetKeyRotationStatus
|
|
|
|
|
- kms:ListResourceTags
|
|
|
|
|
- kms:PutKeyPolicy
|
|
|
|
|
- kms:EnableKeyRotation
|
|
|
|
|
- kms:DisableKeyRotation
|
|
|
|
|
- kms:ScheduleKeyDeletion
|
|
|
|
|
- kms:CancelKeyDeletion
|
|
|
|
|
- kms:TagResource
|
|
|
|
|
- kms:UntagResource
|
|
|
|
|
- kms:EnableKey
|
|
|
|
|
- kms:DisableKey
|
|
|
|
|
Resource:
|
|
|
|
|
- !Sub "arn:aws:kms:us-east-1:${AWS::AccountId}:key/d10fd1f0-a61a-4405-8568-85e9fd11ba18"
|
|
|
|
|
- Sid: KmsList
|
|
|
|
|
Effect: Allow
|
|
|
|
|
Action:
|
|
|
|
|
- kms:ListAliases
|
|
|
|
|
- kms:ListKeys
|
|
|
|
|
Resource: "*"
|
|
|
|
|
- Sid: ShocKmsAlias
|
|
|
|
|
Effect: Allow
|
|
|
|
|
Action:
|
|
|
|
|
- kms:CreateAlias
|
|
|
|
|
- kms:DeleteAlias
|
|
|
|
|
- kms:UpdateAlias
|
|
|
|
|
Resource:
|
|
|
|
|
- !Sub "arn:aws:kms:us-east-1:${AWS::AccountId}:alias/workorder-ingest-shoc-webhook-kms"
|
|
|
|
|
- !Sub "arn:aws:kms:us-east-1:${AWS::AccountId}:key/d10fd1f0-a61a-4405-8568-85e9fd11ba18"
|
|
|
|
|
# Shell lifecycle only — no CreateSecret / UpdateSecret so apply
|
|
|
|
|
# never writes SecretString into HCP state or run logs. Create is
|
|
|
|
|
# isolated in ShocSecretCreate with an exact Name pin.
|
|
|
|
|
- Sid: ShocSecretShell
|
|
|
|
|
Effect: Allow
|
|
|
|
|
Action:
|
|
|
|
|
- secretsmanager:DeleteSecret
|
|
|
|
|
- secretsmanager:DescribeSecret
|
|
|
|
|
- secretsmanager:GetResourcePolicy
|
|
|
|
|
- secretsmanager:PutResourcePolicy
|
|
|
|
|
- secretsmanager:DeleteResourcePolicy
|
|
|
|
|
- secretsmanager:TagResource
|
|
|
|
|
- secretsmanager:UntagResource
|
|
|
|
|
- secretsmanager:RotateSecret
|
|
|
|
|
- secretsmanager:CancelRotateSecret
|
|
|
|
|
- secretsmanager:UpdateSecretVersionStage
|
|
|
|
|
Resource:
|
|
|
|
|
- !Sub "arn:aws:secretsmanager:us-east-1:${AWS::AccountId}:secret:workorder-ingest/shoc-webhook-hmac-*"
|
|
|
|
|
- Sid: ShocSecretCreate
|
|
|
|
|
Effect: Allow
|
|
|
|
|
Action:
|
|
|
|
|
- secretsmanager:CreateSecret
|
|
|
|
|
Resource: "*"
|
|
|
|
|
Condition:
|
|
|
|
|
StringEquals:
|
|
|
|
|
"secretsmanager:Name": workorder-ingest/shoc-webhook-hmac
|
|
|
|
|
- Sid: WebUiSecretDescribe
|
|
|
|
|
Effect: Allow
|
|
|
|
|
Action:
|
|
|
|
|
- secretsmanager:DescribeSecret
|
|
|
|
|
- secretsmanager:GetResourcePolicy
|
|
|
|
|
Resource:
|
|
|
|
|
- !Sub "arn:aws:secretsmanager:us-east-1:${AWS::AccountId}:secret:procurement-ingest/web-ui-auth-token-*"
|
2026-08-07 15:09:57 -04:00
|
|
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
# seahaven-site-prod (PLAT-91) — static site S3 + CloudFront + ACM + GHA
|
|
|
|
|
# content-deploy role. No Lambda → no boundary widen. No Route53 (apex DNS
|
|
|
|
|
# stays OOB in mgmt). Plan role: ViewOnly + plan-refresh sidecar.
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
HcptfSeahavenSitePlanRole:
|
|
|
|
|
Type: AWS::IAM::Role
|
|
|
|
|
Condition: IsProdAccount
|
|
|
|
|
Properties:
|
|
|
|
|
RoleName: hcptf-seahaven-site-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:seahaven-site-prod:run_phase:plan
|
|
|
|
|
ManagedPolicyArns:
|
|
|
|
|
- arn:aws:iam::aws:policy/job-function/ViewOnlyAccess
|
|
|
|
|
Policies:
|
|
|
|
|
- PolicyName: seahaven-site-plan-refresh
|
|
|
|
|
PolicyDocument:
|
|
|
|
|
Version: "2012-10-17"
|
|
|
|
|
Statement:
|
|
|
|
|
- Sid: RefreshDeployRole
|
|
|
|
|
Effect: Allow
|
|
|
|
|
Action:
|
|
|
|
|
- iam:GetRole
|
|
|
|
|
- iam:GetRolePolicy
|
|
|
|
|
- iam:ListRolePolicies
|
|
|
|
|
- iam:ListAttachedRolePolicies
|
|
|
|
|
- iam:ListRoleTags
|
|
|
|
|
Resource:
|
|
|
|
|
- !Sub "arn:aws:iam::${AWS::AccountId}:role/tf-managed/githubdeploy-seahaven-site"
|
|
|
|
|
- Sid: RefreshGithubOidcProvider
|
|
|
|
|
Effect: Allow
|
|
|
|
|
Action:
|
|
|
|
|
- iam:GetOpenIDConnectProvider
|
|
|
|
|
Resource:
|
|
|
|
|
- !Sub "arn:aws:iam::${AWS::AccountId}:oidc-provider/token.actions.githubusercontent.com"
|
|
|
|
|
- Sid: RefreshManagedPolicies
|
|
|
|
|
Effect: Allow
|
|
|
|
|
Action:
|
|
|
|
|
- iam:GetPolicy
|
|
|
|
|
- iam:GetPolicyVersion
|
|
|
|
|
Resource: "*"
|
|
|
|
|
- Sid: RefreshOriginBucket
|
|
|
|
|
Effect: Allow
|
|
|
|
|
Action:
|
|
|
|
|
- s3:Get*
|
|
|
|
|
- s3:ListBucket
|
|
|
|
|
Resource:
|
|
|
|
|
- arn:aws:s3:::seahaven-site-prod
|
|
|
|
|
- arn:aws:s3:::seahaven-site-prod/*
|
|
|
|
|
- Sid: RefreshCloudFront
|
|
|
|
|
Effect: Allow
|
|
|
|
|
Action:
|
|
|
|
|
- cloudfront:Get*
|
|
|
|
|
- cloudfront:List*
|
|
|
|
|
Resource: "*"
|
|
|
|
|
- Sid: RefreshAcm
|
|
|
|
|
Effect: Allow
|
|
|
|
|
Action:
|
|
|
|
|
- acm:DescribeCertificate
|
|
|
|
|
- acm:ListCertificates
|
|
|
|
|
- acm:ListTagsForCertificate
|
|
|
|
|
- acm:GetCertificate
|
|
|
|
|
Resource: "*"
|
2026-08-07 17:07:04 -04:00
|
|
|
- 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"
|
|
|
|
|
- Sid: RefreshWafWebAcl
|
|
|
|
|
Effect: Allow
|
|
|
|
|
Action:
|
|
|
|
|
- wafv2:GetWebACL
|
|
|
|
|
- wafv2:ListWebACLs
|
|
|
|
|
Resource: "*"
|
2026-08-07 15:09:57 -04:00
|
|
|
|
|
|
|
|
HcptfSeahavenSiteApplyRole:
|
|
|
|
|
Type: AWS::IAM::Role
|
|
|
|
|
Condition: IsProdAccount
|
|
|
|
|
Properties:
|
|
|
|
|
RoleName: hcptf-seahaven-site
|
|
|
|
|
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:seahaven-site-prod:run_phase:apply
|
|
|
|
|
ManagedPolicyArns:
|
|
|
|
|
- !Ref HcptfIamManagementPolicy
|
|
|
|
|
Policies:
|
|
|
|
|
- PolicyName: seahaven-site-services
|
|
|
|
|
PolicyDocument:
|
|
|
|
|
Version: "2012-10-17"
|
|
|
|
|
Statement:
|
|
|
|
|
- Sid: OriginBucket
|
|
|
|
|
Effect: Allow
|
|
|
|
|
Action:
|
|
|
|
|
- s3:*
|
|
|
|
|
Resource:
|
|
|
|
|
- arn:aws:s3:::seahaven-site-prod
|
|
|
|
|
- arn:aws:s3:::seahaven-site-prod/*
|
|
|
|
|
- Sid: ReadGithubOidcProvider
|
|
|
|
|
Effect: Allow
|
|
|
|
|
Action:
|
|
|
|
|
- iam:GetOpenIDConnectProvider
|
|
|
|
|
Resource:
|
|
|
|
|
- !Sub "arn:aws:iam::${AWS::AccountId}:oidc-provider/token.actions.githubusercontent.com"
|
|
|
|
|
- Sid: CloudFrontManage
|
|
|
|
|
Effect: Allow
|
|
|
|
|
Action:
|
|
|
|
|
- cloudfront:*
|
|
|
|
|
Resource: "*"
|
|
|
|
|
# RequestCertificate is account-level; pin via RequestTag matching
|
|
|
|
|
# provider default_tags (Project=seahaven-site). Post-create manage
|
|
|
|
|
# requires the same ResourceTag.
|
|
|
|
|
- Sid: AcmCreate
|
|
|
|
|
Effect: Allow
|
|
|
|
|
Action:
|
|
|
|
|
- acm:RequestCertificate
|
|
|
|
|
Resource: "*"
|
|
|
|
|
Condition:
|
|
|
|
|
StringEquals:
|
|
|
|
|
"aws:RequestTag/Project": seahaven-site
|
|
|
|
|
- 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": seahaven-site
|
2026-08-07 17:07:04 -04:00
|
|
|
# CloudFront web_acl_id is set via UpdateDistribution (cloudfront:*
|
|
|
|
|
# above). Read the shared ACL ARN from SSM (PLAT-92) and allow
|
|
|
|
|
# WAFv2 describe so plans/applies can validate the association.
|
|
|
|
|
- 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: ReadWafWebAcl
|
|
|
|
|
Effect: Allow
|
|
|
|
|
Action:
|
|
|
|
|
- wafv2:GetWebACL
|
|
|
|
|
- wafv2:GetWebACLForResource
|
|
|
|
|
- wafv2:ListWebACLs
|
|
|
|
|
- wafv2:ListResourcesForWebACL
|
|
|
|
|
Resource: "*"
|
2026-08-07 19:36:20 -04:00
|
|
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
# 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
|
2026-08-10 13:23:56 -04:00
|
|
|
# aws_ssm_parameter refresh lists tags on managed parameters.
|
|
|
|
|
- ssm:ListTagsForResource
|
2026-08-07 19:36:20 -04:00
|
|
|
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/*"
|
2026-08-07 20:11:48 -04:00
|
|
|
# aws_ssm_parameter refresh uses DescribeParameters (collection API;
|
|
|
|
|
# resource-level parameter ARNs are a silent no-op for this action).
|
|
|
|
|
- Sid: RefreshSsmDescribeParameters
|
|
|
|
|
Effect: Allow
|
|
|
|
|
Action:
|
|
|
|
|
- ssm:DescribeParameters
|
|
|
|
|
Resource: "*"
|
2026-08-07 19:36:20 -04:00
|
|
|
- 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/*"
|
2026-08-07 20:11:48 -04:00
|
|
|
# Collection API required for aws_ssm_parameter refresh/import.
|
|
|
|
|
- Sid: MealOrderSsmDescribeParameters
|
|
|
|
|
Effect: Allow
|
|
|
|
|
Action:
|
|
|
|
|
- ssm:DescribeParameters
|
|
|
|
|
Resource: "*"
|
2026-08-10 13:46:12 -04:00
|
|
|
# API Gateway Lambda authorizer requires PassRole to
|
|
|
|
|
# apigateway.amazonaws.com. Shared HcptfIamManagementPolicy only
|
|
|
|
|
# grants PassRole to lambda.amazonaws.com (see IAMPassRole comment).
|
|
|
|
|
- Sid: MealOrderPassRoleApiGateway
|
|
|
|
|
Effect: Allow
|
|
|
|
|
Action:
|
|
|
|
|
- iam:PassRole
|
|
|
|
|
Resource:
|
|
|
|
|
- !Sub "arn:aws:iam::${AWS::AccountId}:role/tf-managed/meal-order-manager-*"
|
|
|
|
|
Condition:
|
|
|
|
|
StringEquals:
|
|
|
|
|
"iam:PassedToService": "apigateway.amazonaws.com"
|
2026-08-07 19:36:20 -04:00
|
|
|
- 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: "*"
|