* chore(backup): drop the file-share rollback volume from phase 2 (PLAT-77)
The rollback hold is being deleted now, so the selection must not keep an ARN that will not exist.
* docs(backup): state why the file-share volume leaves phase 2 (PLAT-77)
The comment now matches the cleanup: the missing volume is already gone, and the rollback volume leaves because the hold ended.
* chore(backup): drop retired file-share volumes from phase 2 (PLAT-77)
The 20 GiB volume is already gone and the 500 GiB volume is only a rollback hold, so the backup selection should not include either one.
* fix(backup): keep the file-share rollback volume in phase 2 (PLAT-77)
The 500 GiB disk is the rollback hold until 2026-10-06, so it stays in the offsite selection. Only the missing 20 GiB volume comes out.
The IAM roles are already gone. Removing the logical IDs while DeletionPolicy stays Retain lets CloudFormation drop them without updating missing roles.
The policy-check role trust now matches pull request and merge queue
subjects. A failed assume must fail the job instead of skipping
ValidatePolicy and CheckNoNewAccess.
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Adam Moussa <amoussa1229@users.noreply.github.com>
* docs(agents): document Node 24 for cloud agents
Co-authored-by: Adam Moussa <amoussa1229@users.noreply.github.com>
* docs(agents): keep cdk diff required and exempt docs titles
Cloud agents still review cdk diff when the VM can read the accounts.
Docs, hygiene, and dependency pull requests omit a Jira suffix instead
of opening a ticket only to fill the title.
Co-authored-by: Adam Moussa <amoussa1229@users.noreply.github.com>
* fix: address review comments
---------
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Adam Moussa <amoussa1229@users.noreply.github.com>
* fix(iam): shrink shared lambda boundary to the four-statement floor (PLAT-52)
PermissionsBoundaryUsageCount is 0 in prod and dev, so the shared
seahaven-lambda-execution-boundary drops the packed IsProdAccount
data-plane statements and keeps CloudWatchLogsWrite,
CloudWatchLogsDescribe, XRay, and Ec2Eni.
Co-authored-by: Adam Moussa <amoussa1229@users.noreply.github.com>
* docs(iam): correct shared boundary size and scoping notes (PLAT-52)
The dev floor is the same 708-character document as the shared policy.
691 was stale. The scoping note now says the shared document is the
four-statement floor, and allow-list retirement is a follow-up.
Co-authored-by: Adam Moussa <amoussa1229@users.noreply.github.com>
* docs(iam): limit scoping rule to remaining SAM workloads (PLAT-52)
The shared boundary shrink is unchanged. The scoping note now matches
the HCP path already stated later in the same file.
Co-authored-by: Adam Moussa <amoussa1229@users.noreply.github.com>
* docs(iam): record the shared floor size as 691 characters (PLAT-52)
The stated measurement, with the account id resolved, is 691 characters
and 4 statements for the shared boundary and for the dev floor copies.
Headroom is 5453.
Co-authored-by: Adam Moussa <amoussa1229@users.noreply.github.com>
---------
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Adam Moussa <amoussa1229@users.noreply.github.com>
* feat(iam): add platform permission set and org-admin assume alarm (SEC-37)
Adds an Identity Center platform group and Platform permission set
assigned to the management account, and a CloudTrail alarm on
AssumeRole of OrganizationAccountAccessRole. Scripts still assume
that role. This change is not deployed.
Co-authored-by: Adam Moussa <amoussa1229@users.noreply.github.com>
* fix(iam): skip sanctioned org-admin assumes in the alarm (SEC-37)
Count failed assumes for every principal. Do not page on a successful
assume by the Platform permission set or the two repo script sessions.
Co-authored-by: Adam Moussa <amoussa1229@users.noreply.github.com>
---------
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Adam Moussa <amoussa1229@users.noreply.github.com>
* feat(scp): deny iam changes on role/platform unless the platform principal (PLAT-233)
Adds ProtectPlatformPath beside the existing name denies in the
prod/nonprod SCP and the security OU copy. New hcptf-bootstrap
creates use /platform/. Existing roles are not recreated.
Co-authored-by: Adam Moussa <amoussa1229@users.noreply.github.com>
* fix(scp): use live bootstrap ARNs and close the platform path gaps (PLAT-233)
Resolve simulate and printed role ARNs from iam:GetRole so a /platform/
create is not reported as an unpathed role. Deny boundary changes on
role/platform/*, and match both Identity Center SSO role ARN shapes.
Co-authored-by: Adam Moussa <amoussa1229@users.noreply.github.com>
* fix(scp): deny platform-path changes in bootstrap simulate (PLAT-233)
Add a simulate case for role/platform/hcptf-example and fail when CreateRole,
PutRolePolicy, or DeleteRole is allowed. The unpathed hcptf-* import check stays.
Co-authored-by: Adam Moussa <amoussa1229@users.noreply.github.com>
---------
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Adam Moussa <amoussa1229@users.noreply.github.com>
* ci(iam): check synthesized policies with Access Analyzer (PLAT-234)
Adds a CI job that checks bootstrap trust for StringEquals, rejects
lambda writes on the plan refresh template, and runs ValidatePolicy
plus CheckNoNewAccess when the policy-check role can be assumed.
Co-authored-by: Adam Moussa <amoussa1229@users.noreply.github.com>
* ci(iam): fail closed on widened policies (PLAT-234)
Compare new and removed SCPs, and fail when a Deny shrinks or a Condition
changes. Run CheckNoNewAccess on bootstrap templates from the base repo.
Install the base worktree's own dependencies and warn when analyzer
credentials are skipped.
Co-authored-by: Adam Moussa <amoussa1229@users.noreply.github.com>
---------
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Adam Moussa <amoussa1229@users.noreply.github.com>
The live boundary denies lambda:InvokeFunction, so the Monday and Thursday
schedules would be created and would not fire. Allow that action only on
paychex-payroll-schedule, and allow SendMessage on paychex-checkcomponents
so the schedule role can enqueue the flush.
* feat(iam): move seahaven-site exec roles into their own stack (PLAT-225)
Drop the retained roles from the substrate template so the new stack can import them without a second owner.
* fix(iam): address review feedback
* fix(iam): allow frontend HCP apply to write deploy SSM and githubdeploy trust (PLAT-212)
* fix(iam): grant frontend HCP plan named SSM describe and tag reads (PLAT-212)
* fix(iam): allow frontend githubdeploy to read deploy SSM (PLAT-212)
HCP apply already writes /shoc-frontend-new/<env>/deploy/*, but the
githubdeploy ceiling omitted GetParameter so Deploy Web cannot resolve
bucket and distribution after origin moves to the bucket root.
* fix(iam): allow staging HCP apply to update the SHOC backend EB stack (PLAT-213)
* fix(iam): allow staging HCP apply to use the Elastic Beanstalk bucket (PLAT-213)
* fix(iam): allow staging HCP apply to copy the current release zip (PLAT-213)
* fix(iam): allow staging HCP apply versioned ACLs on EB env objects (PLAT-213)
* fix(iam): give staging HCP apply the proven Elastic Beanstalk bucket grants (PLAT-213)
* fix(iam): allow staging HCP apply to write CloudFormation template buckets (PLAT-213)
* fix(iam): let staging HCP apply read Elastic Beanstalk service templates (PLAT-213)
* fix(iam): enable meal-order-manager Lambda boundary in seahaven-dev (PLAT-210)
The per-workload boundary was floor-only outside prod, so meals-dev Lambdas
were denied DynamoDB. Keep Paychex SQS, SNS, and SES prod-only.
* fix(iam): keep meal-order-manager boundary Description unchanged (PLAT-210)
Named IAM managed-policy Description is immutable. Changing it replaces the
resource and 409s on ManagedPolicyName. PolicyDocument still widens in place.
* fix(iam): allow paychex period table and optional secrets (PLAT-195)
The processor role already allows these ARNs. The live boundary denied
them, so GetSecretValue and period PutItem returned HTTP 400. Sync the
template to the live ceiling and add the missing period table plus
slack-admin and afterhours secret suffixes.
* fix(iam): keep paychex boundary description unchanged (PLAT-195)
IAM managed-policy Description is immutable. Changing it on a named
policy forces replacement and 409s against the live ManagedPolicyName.
Widen PolicyDocument only.
* feat(iam): allow frontend HCP apply to own release pointer and invalidation (PLAT-188)
Plan and apply roles can read .release/current; apply can PutObject that key and CreateInvalidation on the exact distribution.
* fix(iam): allow frontend HCP roles to tag the release pointer (PLAT-188)
Terraform aws_s3_object lists object tags on every refresh, so plan and apply need GetObjectTagging and apply needs PutObjectTagging on the exact .release/current key.
* feat(iam): allow frontend HCP apply to update exact CloudFront resources (PLAT-187)
Phase 2 ownership tags cannot apply while UpdateDistribution and UpdateFunction are denied on *. Allow those two actions only on the pinned distribution and function ARNs.
* fix(iam): allow PublishFunction on exact frontend CloudFront functions (PLAT-187)
The AWS provider publishes after UpdateFunction, including tag-only applies, so denying PublishFunction on * still blocked Phase 2 function updates.
* feat(iam): lock app-owned HCP IAM and add bootstrap SCP (PLAT-143)
* fix(iam): pin HCP boundary ARNs and bootstrap trust window (PLAT-143)
Null on iam:PermissionsBoundary accepted any ceiling, including AdministratorAccess. Import apply cannot self-mutate hcptf-* while bootstrap trust is iam-bootstrap only; add a time-boxed exact StringEquals workspace grant instead of StringLike.
* fix(iam): update paychex boundary in place without fn if
CloudFormation replaced the named managed policy when the secrets statement was wrapped in Fn::If (409 duplicate name). Keep the six minted ARNs as a static statement so the document updates in place.
* fix(iam): leave paychex boundary description unchanged
Keep the live ManagedPolicy Description so CloudFormation only updates PolicyDocument.
CreateDomainName cannot be hostname-pinned, and mgmt still holds doorunlock.seahaven.com. Attach the domain at cutover instead of granting collection POST.
* feat(iam): add door-unlock-api hcptf roles and boundary
Give HCP Terraform a prod plan/apply pair, a per-workload Lambda boundary with exact SSM and 3CX ARNs, and API access-log delivery so PLAT-76 can leave the mgmt CDK stack.
* fix(iam): pin door-unlock apigw domain and ssm reads
Stop the apply role from managing every HTTP API custom domain, and keep SecureString door-unlock parameters off HCP plan and apply GetParameter.