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>
This commit is contained in:
Cursor Agent 2026-09-28 01:19:58 +00:00
parent e3adfc3cdc
commit f35512edaa
No known key found for this signature in database
3 changed files with 46 additions and 6 deletions

View file

@ -350,6 +350,8 @@ export class OrgGovernanceStack extends cdk.Stack {
"iam:DeleteRolePolicy",
"iam:DeleteRole",
"iam:UpdateRole",
"iam:PutRolePermissionsBoundary",
"iam:DeleteRolePermissionsBoundary",
"iam:TagRole",
"iam:UntagRole",
],
@ -358,7 +360,7 @@ export class OrgGovernanceStack extends cdk.Stack {
ArnNotLike: {
"aws:PrincipalArn": [
"arn:aws:iam::*:role/OrganizationAccountAccessRole",
"arn:aws:iam::*:role/aws-reserved/sso.amazonaws.com/*/AWSReservedSSO_Platform_*",
"arn:aws:iam::*:role/aws-reserved/sso.amazonaws.com/*AWSReservedSSO_Platform_*",
],
},
},

View file

@ -44,6 +44,8 @@
"iam:DeleteRolePolicy",
"iam:DeleteRole",
"iam:UpdateRole",
"iam:PutRolePermissionsBoundary",
"iam:DeleteRolePermissionsBoundary",
"iam:TagRole",
"iam:UntagRole"
],
@ -52,7 +54,7 @@
"ArnNotLike": {
"aws:PrincipalArn": [
"arn:aws:iam::*:role/OrganizationAccountAccessRole",
"arn:aws:iam::*:role/aws-reserved/sso.amazonaws.com/*/AWSReservedSSO_Platform_*"
"arn:aws:iam::*:role/aws-reserved/sso.amazonaws.com/*AWSReservedSSO_Platform_*"
]
}
}

View file

@ -16,7 +16,8 @@
# scripts/create-hcptf-bootstrap-roles.sh --account prod --allow-workspace STACK-prod
#
# --simulate runs iam:SimulatePrincipalPolicy against the apply role. Requires
# the role to already exist.
# the role to already exist. The policy source is the live Role.Arn from
# iam:GetRole, so an existing unpathed role and a /platform/ create both match.
#
# Default trust is exact StringEquals for workspace iam-bootstrap-<env> only.
# HCP workspace names are org-unique, so prod and dev cannot both be
@ -145,8 +146,41 @@ AWS_SESSION_TOKEN="$(python3 -c 'import json,sys; print(json.load(sys.stdin)["Se
echo "account: ${ACCOUNT_ID} (${ACCOUNT_KEY})"
echo "caller: $(aws sts get-caller-identity --query Arn --output text)"
# Live ARN, checked against the role path. IAM role names are unique per
# account, so GetRole finds either /hcptf-bootstrap or /platform/hcptf-bootstrap.
role_arn() {
local name="$1"
local line arn path
# Command substitution so set -e stops when GetRole fails. A missing role
# must not fall through to a printed ARN.
line="$(aws iam get-role --role-name "$name" \
--query 'Role.[Arn,Path]' --output text)"
arn="${line%%$'\t'*}"
path="${line#*$'\t'}"
path="${path%$'\r'}"
case "$path" in
/)
[[ "$arn" == "arn:aws:iam::${ACCOUNT_ID}:role/${name}" ]] || {
echo "${name}: ARN ${arn} does not match path ${path}" >&2
exit 1
}
;;
/platform/)
[[ "$arn" == "arn:aws:iam::${ACCOUNT_ID}:role/platform/${name}" ]] || {
echo "${name}: ARN ${arn} does not match path ${path}" >&2
exit 1
}
;;
*)
echo "${name}: unexpected path ${path} (ARN ${arn})" >&2
exit 1
;;
esac
printf '%s\n' "$arn"
}
if [[ "$SIMULATE" -eq 1 ]]; then
APPLY_ARN="arn:aws:iam::${ACCOUNT_ID}:role/hcptf-bootstrap"
APPLY_ARN="$(role_arn hcptf-bootstrap)"
echo "== simulate ${APPLY_ARN} =="
echo "-- CreateRole with tf-managed boundary (expect allowed) --"
aws iam simulate-principal-policy \
@ -281,9 +315,11 @@ aws iam attach-role-policy \
2>/dev/null || true
echo " hcptf-bootstrap-plan: attached ViewOnlyAccess"
APPLY_ARN="$(role_arn hcptf-bootstrap)"
PLAN_ARN="$(role_arn hcptf-bootstrap-plan)"
echo "done. Next: HCP workspace ${BOOTSTRAP_WORKSPACE} in ${HCP_PROJECT}, Manual apply,"
echo " TFC_AWS_APPLY_ROLE_ARN=arn:aws:iam::${ACCOUNT_ID}:role/hcptf-bootstrap"
echo " TFC_AWS_PLAN_ROLE_ARN=arn:aws:iam::${ACCOUNT_ID}:role/hcptf-bootstrap-plan"
echo " TFC_AWS_APPLY_ROLE_ARN=${APPLY_ARN}"
echo " TFC_AWS_PLAN_ROLE_ARN=${PLAN_ARN}"
if [[ -n "$ALLOW_WORKSPACE" ]]; then
echo "First-apply/import window: point workspace ${ALLOW_WORKSPACE} TFC_AWS_* at the pair above,"
echo " apply, retarget scoped ARNs, then re-run this script with no --allow-workspace."