From 97b00ad0f440c6b0a9bc7e454c159a3a36de0258 Mon Sep 17 00:00:00 2001 From: Adam Moussa Date: Fri, 11 Sep 2026 12:54:07 -0400 Subject: [PATCH] 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. --- README.md | 8 ++++++-- .../shoc-frontend-resources.ts | 18 ++++++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b6eef7d..c1de6ce 100644 --- a/README.md +++ b/README.md @@ -439,7 +439,9 @@ job: existing site resources only after a no-replacement plan. Apply-role writes are limited to ordinary tags, `PutRolePolicy` on the exact deploy role, `PutBucketPolicy` on the exact bucket, `UpdateDistribution` on the exact - distribution, `UpdateFunction` and `PublishFunction` on the exact CloudFront + distribution, `CreateInvalidation`/`GetInvalidation` on the exact + distribution, `GetObject`/`PutObject` on `.release/current`, + `UpdateFunction` and `PublishFunction` on the exact CloudFront function, and A/AAAA changes for the exact site name with CREATE/DELETE/UPSERT conditions. 5. For a future tf-poc, first provision and inventory the site outside these @@ -456,7 +458,9 @@ The apply roles explicitly deny role lifecycle/trust/boundary/managed-policy changes, `PassRole`, secret and parameter reads, CloudFront/S3 create and delete (including OAC mutation), and deletion of inline role or bucket policies. `UpdateDistribution` is allowed on the exact pinned distribution ARN. -`UpdateFunction` and `PublishFunction` are allowed on the exact pinned function +`CreateInvalidation` and `GetInvalidation` are allowed on that same ARN so the +Terraform invalidation action can run. `GetObject`/`PutObject` on +`.release/current` lets Terraform own the release pointer. `UpdateFunction` and `PublishFunction` are allowed on the exact pinned function ARN so Phase 2 ownership tags can apply; create, delete, and OAC updates stay denied. IAM does not expose a condition key for an inline policy name, so `PutRolePolicy` is constrained to the exact target-role ARN and requires the diff --git a/lib/terraform-substrate/shoc-frontend-resources.ts b/lib/terraform-substrate/shoc-frontend-resources.ts index 3c8ee7b..4cdbcaa 100644 --- a/lib/terraform-substrate/shoc-frontend-resources.ts +++ b/lib/terraform-substrate/shoc-frontend-resources.ts @@ -113,6 +113,12 @@ const frontendReadPolicy = ( ], Resource: siteBucketArn, }, + { + Sid: "ReadReleasePointerObject", + Effect: "Allow", + Action: ["s3:GetObject", "s3:GetObjectVersion"], + Resource: `${siteBucketArn}/.release/current`, + }, { Sid: "ReadExactCloudFrontResources", Effect: "Allow", @@ -343,6 +349,18 @@ const frontendApplyPolicy = ( Action: ["cloudfront:UpdateFunction", "cloudfront:PublishFunction"], Resource: functionArn(environment.functionName), }, + { + Sid: "InvalidateExactDistribution", + Effect: "Allow", + Action: ["cloudfront:CreateInvalidation", "cloudfront:GetInvalidation"], + Resource: distributionArn(environment.distributionId), + }, + { + Sid: "WriteReleasePointerObject", + Effect: "Allow", + Action: ["s3:GetObject", "s3:GetObjectVersion", "s3:PutObject"], + Resource: `${bucketArn(environment.bucketName)}/.release/current`, + }, { Sid: "ReplaceExactDeployInlinePolicy", Effect: "Allow",