From f9f3681be9fc6a0c998cd26119053c5832ef9806 Mon Sep 17 00:00:00 2001 From: Pahud Hsieh Date: Thu, 9 May 2024 14:41:08 -0400 Subject: [PATCH] fix(iam): fromUserArn returns incorrect principalAccount (#30023) ### Issue # (if applicable) Closes https://github.com/aws/aws-cdk/issues/29999 ### Reason for this change As described in the issue [comment](https://github.com/aws/aws-cdk/issues/29999#issuecomment-2087672380). ### Description of changes ### Description of how you validated changes 1. added more unit tests. 2. added a new integ test 3. I have deployed this in my AWS account ```ts import { App, Stack, CfnParameter, aws_iam as iam, CfnOutput, } from 'aws-cdk-lib'; const app = new App(); const stack = new Stack(app, 'dummy-stack'); const userArn = 'arn:aws:iam::123456789012:user/OthersExternalIamUser'; const userparam = new CfnParameter(stack, 'UserParameter', { default: userArn, }); const imported = iam.User.fromUserArn(stack, 'imported-user', userArn); const imported2 = iam.User.fromUserArn(stack, 'imported-user2', userparam.valueAsString ); new CfnOutput(stack, 'User', { value: imported.principalAccount! }); new CfnOutput(stack, 'User2', { value: imported2.principalAccount! }); ``` And the output is correct: ``` Outputs: dummy-stack.User = 123456789012 dummy-stack.User2 = 123456789012 ``` ### Checklist - [x] My code adheres to the [CONTRIBUTING GUIDE](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) and [DESIGN GUIDELINES](https://github.com/aws/aws-cdk/blob/main/docs/DESIGN_GUIDELINES.md) ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../integ.user-import.js.snapshot/cdk.out | 1 + .../dummy-stack.assets.json | 19 ++ .../dummy-stack.template.json | 83 ++++++++ ...efaultTestDeployAssertF190D0AF.assets.json | 19 ++ ...aultTestDeployAssertF190D0AF.template.json | 36 ++++ .../integ.user-import.js.snapshot/integ.json | 12 ++ .../manifest.json | 137 ++++++++++++++ .../integ.user-import.js.snapshot/tree.json | 179 ++++++++++++++++++ .../test/aws-iam/test/integ.user-import.ts | 30 +++ packages/aws-cdk-lib/aws-iam/lib/user.ts | 4 +- .../aws-cdk-lib/aws-iam/test/user.test.ts | 37 ++++ 11 files changed, 555 insertions(+), 2 deletions(-) create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-iam/test/integ.user-import.js.snapshot/cdk.out create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-iam/test/integ.user-import.js.snapshot/dummy-stack.assets.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-iam/test/integ.user-import.js.snapshot/dummy-stack.template.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-iam/test/integ.user-import.js.snapshot/dummystackintegtestDefaultTestDeployAssertF190D0AF.assets.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-iam/test/integ.user-import.js.snapshot/dummystackintegtestDefaultTestDeployAssertF190D0AF.template.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-iam/test/integ.user-import.js.snapshot/integ.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-iam/test/integ.user-import.js.snapshot/manifest.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-iam/test/integ.user-import.js.snapshot/tree.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-iam/test/integ.user-import.ts diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-iam/test/integ.user-import.js.snapshot/cdk.out b/packages/@aws-cdk-testing/framework-integ/test/aws-iam/test/integ.user-import.js.snapshot/cdk.out new file mode 100644 index 0000000000000..1f0068d32659a --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-iam/test/integ.user-import.js.snapshot/cdk.out @@ -0,0 +1 @@ +{"version":"36.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-iam/test/integ.user-import.js.snapshot/dummy-stack.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-iam/test/integ.user-import.js.snapshot/dummy-stack.assets.json new file mode 100644 index 0000000000000..d447c72cf9240 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-iam/test/integ.user-import.js.snapshot/dummy-stack.assets.json @@ -0,0 +1,19 @@ +{ + "version": "36.0.0", + "files": { + "e3b7e8636fe810045c29b035b190ac34272928317373d89cfb9d32fba5f6c4ae": { + "source": { + "path": "dummy-stack.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "e3b7e8636fe810045c29b035b190ac34272928317373d89cfb9d32fba5f6c4ae.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-iam/test/integ.user-import.js.snapshot/dummy-stack.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-iam/test/integ.user-import.js.snapshot/dummy-stack.template.json new file mode 100644 index 0000000000000..a8f6bc51fa876 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-iam/test/integ.user-import.js.snapshot/dummy-stack.template.json @@ -0,0 +1,83 @@ +{ + "Parameters": { + "UserParameter": { + "Type": "String", + "Default": "arn:aws:iam::123456789012:user/OthersExternalIamUser" + }, + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Resources": { + "User00B015A1": { + "Type": "AWS::IAM::User" + } + }, + "Outputs": { + "UserOutput": { + "Value": "123456789012" + }, + "User2Output": { + "Value": { + "Fn::Select": [ + 4, + { + "Fn::Split": [ + ":", + { + "Ref": "UserParameter" + } + ] + } + ] + } + }, + "User3Output": { + "Value": { + "Fn::Select": [ + 4, + { + "Fn::Split": [ + ":", + { + "Fn::GetAtt": [ + "User00B015A1", + "Arn" + ] + } + ] + } + ] + } + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-iam/test/integ.user-import.js.snapshot/dummystackintegtestDefaultTestDeployAssertF190D0AF.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-iam/test/integ.user-import.js.snapshot/dummystackintegtestDefaultTestDeployAssertF190D0AF.assets.json new file mode 100644 index 0000000000000..3ef6c8a37cafc --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-iam/test/integ.user-import.js.snapshot/dummystackintegtestDefaultTestDeployAssertF190D0AF.assets.json @@ -0,0 +1,19 @@ +{ + "version": "36.0.0", + "files": { + "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { + "source": { + "path": "dummystackintegtestDefaultTestDeployAssertF190D0AF.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-iam/test/integ.user-import.js.snapshot/dummystackintegtestDefaultTestDeployAssertF190D0AF.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-iam/test/integ.user-import.js.snapshot/dummystackintegtestDefaultTestDeployAssertF190D0AF.template.json new file mode 100644 index 0000000000000..ad9d0fb73d1dd --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-iam/test/integ.user-import.js.snapshot/dummystackintegtestDefaultTestDeployAssertF190D0AF.template.json @@ -0,0 +1,36 @@ +{ + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-iam/test/integ.user-import.js.snapshot/integ.json b/packages/@aws-cdk-testing/framework-integ/test/aws-iam/test/integ.user-import.js.snapshot/integ.json new file mode 100644 index 0000000000000..4e5f1ec5ea743 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-iam/test/integ.user-import.js.snapshot/integ.json @@ -0,0 +1,12 @@ +{ + "version": "36.0.0", + "testCases": { + "dummy-stack/integ-test/DefaultTest": { + "stacks": [ + "dummy-stack" + ], + "assertionStack": "dummy-stack/integ-test/DefaultTest/DeployAssert", + "assertionStackName": "dummystackintegtestDefaultTestDeployAssertF190D0AF" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-iam/test/integ.user-import.js.snapshot/manifest.json b/packages/@aws-cdk-testing/framework-integ/test/aws-iam/test/integ.user-import.js.snapshot/manifest.json new file mode 100644 index 0000000000000..5eb37c4e70ed3 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-iam/test/integ.user-import.js.snapshot/manifest.json @@ -0,0 +1,137 @@ +{ + "version": "36.0.0", + "artifacts": { + "dummystackintegtestDefaultTestDeployAssertF190D0AF.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "dummystackintegtestDefaultTestDeployAssertF190D0AF.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dummystackintegtestDefaultTestDeployAssertF190D0AF": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "dummystackintegtestDefaultTestDeployAssertF190D0AF.template.json", + "terminationProtection": false, + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "dummystackintegtestDefaultTestDeployAssertF190D0AF.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "dummystackintegtestDefaultTestDeployAssertF190D0AF.assets" + ], + "metadata": { + "/dummy-stack/integ-test/DefaultTest/DeployAssert/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/dummy-stack/integ-test/DefaultTest/DeployAssert/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "dummy-stack/integ-test/DefaultTest/DeployAssert" + }, + "dummy-stack.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "dummy-stack.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dummy-stack": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "dummy-stack.template.json", + "terminationProtection": false, + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/e3b7e8636fe810045c29b035b190ac34272928317373d89cfb9d32fba5f6c4ae.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "dummy-stack.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "dummy-stack.assets" + ], + "metadata": { + "/dummy-stack/UserParameter": [ + { + "type": "aws:cdk:logicalId", + "data": "UserParameter" + } + ], + "/dummy-stack/User/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "User00B015A1" + } + ], + "/dummy-stack/UserOutput": [ + { + "type": "aws:cdk:logicalId", + "data": "UserOutput" + } + ], + "/dummy-stack/User2Output": [ + { + "type": "aws:cdk:logicalId", + "data": "User2Output" + } + ], + "/dummy-stack/User3Output": [ + { + "type": "aws:cdk:logicalId", + "data": "User3Output" + } + ], + "/dummy-stack/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/dummy-stack/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "dummy-stack" + }, + "Tree": { + "type": "cdk:tree", + "properties": { + "file": "tree.json" + } + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-iam/test/integ.user-import.js.snapshot/tree.json b/packages/@aws-cdk-testing/framework-integ/test/aws-iam/test/integ.user-import.js.snapshot/tree.json new file mode 100644 index 0000000000000..546fb5e87665e --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-iam/test/integ.user-import.js.snapshot/tree.json @@ -0,0 +1,179 @@ +{ + "version": "tree-0.1", + "tree": { + "id": "App", + "path": "", + "children": { + "dummy-stack": { + "id": "dummy-stack", + "path": "dummy-stack", + "children": { + "UserParameter": { + "id": "UserParameter", + "path": "dummy-stack/UserParameter", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "imported-user": { + "id": "imported-user", + "path": "dummy-stack/imported-user", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "imported-user2": { + "id": "imported-user2", + "path": "dummy-stack/imported-user2", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "User": { + "id": "User", + "path": "dummy-stack/User", + "children": { + "Resource": { + "id": "Resource", + "path": "dummy-stack/User/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::User", + "aws:cdk:cloudformation:props": {} + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.CfnUser", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.User", + "version": "0.0.0" + } + }, + "LocalUser": { + "id": "LocalUser", + "path": "dummy-stack/LocalUser", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "UserOutput": { + "id": "UserOutput", + "path": "dummy-stack/UserOutput", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnOutput", + "version": "0.0.0" + } + }, + "User2Output": { + "id": "User2Output", + "path": "dummy-stack/User2Output", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnOutput", + "version": "0.0.0" + } + }, + "User3Output": { + "id": "User3Output", + "path": "dummy-stack/User3Output", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnOutput", + "version": "0.0.0" + } + }, + "integ-test": { + "id": "integ-test", + "path": "dummy-stack/integ-test", + "children": { + "DefaultTest": { + "id": "DefaultTest", + "path": "dummy-stack/integ-test/DefaultTest", + "children": { + "Default": { + "id": "Default", + "path": "dummy-stack/integ-test/DefaultTest/Default", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "DeployAssert": { + "id": "DeployAssert", + "path": "dummy-stack/integ-test/DefaultTest/DeployAssert", + "children": { + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "dummy-stack/integ-test/DefaultTest/DeployAssert/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "dummy-stack/integ-test/DefaultTest/DeployAssert/CheckBootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Stack", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests-alpha.IntegTestCase", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests-alpha.IntegTest", + "version": "0.0.0" + } + }, + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "dummy-stack/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "dummy-stack/CheckBootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Stack", + "version": "0.0.0" + } + }, + "Tree": { + "id": "Tree", + "path": "Tree", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.App", + "version": "0.0.0" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-iam/test/integ.user-import.ts b/packages/@aws-cdk-testing/framework-integ/test/aws-iam/test/integ.user-import.ts new file mode 100644 index 0000000000000..1ba591ce68a7b --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-iam/test/integ.user-import.ts @@ -0,0 +1,30 @@ +import { + App, Stack, CfnParameter, + aws_iam as iam, + CfnOutput, +} from 'aws-cdk-lib'; +import { IntegTest } from '@aws-cdk/integ-tests-alpha'; + +const app = new App(); +const stack = new Stack(app, 'dummy-stack'); + +const userArn = 'arn:aws:iam::123456789012:user/OthersExternalIamUser'; + +const userparam = new CfnParameter(stack, 'UserParameter', { + default: userArn, +}); + +const imported = iam.User.fromUserArn(stack, 'imported-user', userArn); +const imported2 = iam.User.fromUserArn(stack, 'imported-user2', userparam.valueAsString ); +const imported3 = iam.User.fromUserArn(stack, 'LocalUser', new iam.User(stack, 'User').userArn); + +// should be 123456789012 +new CfnOutput(stack, 'UserOutput', { value: imported.principalAccount! }); +// should be 123456789012 +new CfnOutput(stack, 'User2Output', { value: imported2.principalAccount! }); +// should be your current account +new CfnOutput(stack, 'User3Output', { value: imported3.principalAccount! }); + +new IntegTest(stack, 'integ-test', { + testCases: [stack], +}); \ No newline at end of file diff --git a/packages/aws-cdk-lib/aws-iam/lib/user.ts b/packages/aws-cdk-lib/aws-iam/lib/user.ts index 1f48f990c8e48..edbd0f59a4f25 100644 --- a/packages/aws-cdk-lib/aws-iam/lib/user.ts +++ b/packages/aws-cdk-lib/aws-iam/lib/user.ts @@ -7,7 +7,7 @@ import { Policy } from './policy'; import { PolicyStatement } from './policy-statement'; import { AddToPrincipalPolicyResult, ArnPrincipal, IPrincipal, PrincipalPolicyFragment } from './principals'; import { AttachedPolicies, undefinedIfEmpty } from './private/util'; -import { Arn, Aws, Lazy, Resource, SecretValue, Stack } from '../../core'; +import { Arn, ArnFormat, Lazy, Resource, SecretValue, Stack } from '../../core'; /** * Represents an IAM user @@ -180,7 +180,7 @@ export class User extends Resource implements IIdentity, IUser { public static fromUserAttributes(scope: Construct, id: string, attrs: UserAttributes): IUser { class Import extends Resource implements IUser { public readonly grantPrincipal: IPrincipal = this; - public readonly principalAccount = Aws.ACCOUNT_ID; + public readonly principalAccount = Stack.of(scope).splitArn(attrs.userArn, ArnFormat.SLASH_RESOURCE_NAME).account; // Resource name with path can have multiple elements separated by slash. // Therefore, use element after last slash as userName. Happens to work for Tokens since // they don't have a '/' in them. diff --git a/packages/aws-cdk-lib/aws-iam/test/user.test.ts b/packages/aws-cdk-lib/aws-iam/test/user.test.ts index 73d45433f3702..c5bfc9391d663 100644 --- a/packages/aws-cdk-lib/aws-iam/test/user.test.ts +++ b/packages/aws-cdk-lib/aws-iam/test/user.test.ts @@ -119,6 +119,43 @@ describe('IAM user', () => { }); }); + test('user imported by user ARN has a principalAccount', () => { + // GIVEN + const stack = new Stack(); + const accountId = 'account-id'; + + // WHEN + const user = User.fromUserArn(stack, 'import', `arn:aws:iam::${accountId}:user/mockuser`); + + // THEN + expect(stack.resolve(user.principalAccount)).toStrictEqual(accountId); + }); + + test('user imported by tokenized user ARN has a principalAccount', () => { + // GIVEN + const stack = new Stack(); + + // WHEN + const user = User.fromUserArn(stack, 'import', Token.asString({ Ref: 'ARN' })); + + // THEN + expect(stack.resolve(user.principalAccount)).toStrictEqual({ + 'Fn::Select': [4, { 'Fn::Split': [':', { Ref: 'ARN' }] }], + }); + }); + test('user imported by a new User cosntruct has a principalAccount', () => { + // GIVEN + const stack = new Stack(); + + // WHEN + const user = User.fromUserArn(stack, 'import', new User(stack, 'LocalUser').userArn); + + // THEN + expect(stack.resolve(user.principalAccount)).toStrictEqual( + { 'Fn::Select': [4, { 'Fn::Split': [':', { 'Fn::GetAtt': ['LocalUser87F70DDF', 'Arn'] }] }] }, + ); + }); + test('user imported by user ARN with path', () => { // GIVEN const stack = new Stack();