From efa77c48710c2e23cb9b9dda73e885e89286a35f Mon Sep 17 00:00:00 2001 From: nlang Date: Wed, 29 Apr 2026 11:56:43 +0200 Subject: [PATCH] fix(aws-util): use current session partition in GetRoleArn MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cross-account `AssumeRole` ARN construction is hardcoded to the commercial AWS partition (`arn:aws:iam::...`). In any non-commercial partition (e.g. AWS European Sovereign Cloud `aws-eusc`, or the China partitions `aws-cn`) this produces ARNs that cannot be resolved by the running partition's IAM, breaking every multi-account operation — including the post-CreateAccount initial assume into a freshly provisioned member account. Fix: read the partition from the cached value populated during `Initialize()` via `GetPartitionFromCurrentSession`. Falls back to 'aws' so behavior in commercial AWS is unchanged. The GovCloud-mode `GetPartitionRoleArn` is intentionally left alone — it represents a cross-partition target ARN and its semantics differ. Discovered while bootstrapping a multi-account organization in `eusc-de-east-1`: the first `update-organization` succeeded for the master account, then `CreateAccount` for a sandbox member account succeeded, and OFN immediately failed with `AccessDenied` on `sts:AssumeRole arn:aws:iam:::role/OrganizationAccountAccessRole` — note the `aws:` partition in the ARN. With this fix that ARN becomes `arn:aws-eusc:iam::...` and the assume succeeds. Note for reviewers: a separate audit identified a few more partition- relevant hardcodes (region whitelist in `validator.ts`, EventBridge region in `aws-events.ts:52`, large-template URL in `aws-util.ts`, catalog URL in `rp-build-task-plugin.ts`). They are in code paths not exercised by my use case and are intentionally left out of this PR to keep the diff focused. Happy to follow up. --- src/util/aws-util.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/util/aws-util.ts b/src/util/aws-util.ts index d60ec3be..3e9d8dd2 100644 --- a/src/util/aws-util.ts +++ b/src/util/aws-util.ts @@ -276,7 +276,14 @@ export class AwsUtil { } public static GetRoleArn(accountId: string, roleInTargetAccount: string): string { - return 'arn:aws:iam::' + accountId + ':role/' + roleInTargetAccount; + // Use the current session's partition (cached during Initialize via + // GetPartitionFromCurrentSession). Falls back to 'aws' so the + // commercial-partition path is unchanged. Without this, partitions + // like aws-eusc (European Sovereign Cloud) or aws-cn produce ARNs + // that fail cross-account AssumeRole because they reference the + // commercial partition instead of the running one. + const partition = AwsUtil.partition ?? 'aws'; + return `arn:${partition}:iam::${accountId}:role/${roleInTargetAccount}`; } private static GetPartitionRoleArn(accountId: string, roleInTargetAccount: string): string {