Skip to content

Commit 57a4186

Browse files
committed
feat: support external IDs when assuming a role with a custom resource
Signed-off-by: Stephen Crowe <[email protected]>
1 parent e5f71db commit 57a4186

File tree

8 files changed

+32
-5
lines changed

8 files changed

+32
-5
lines changed

packages/@aws-cdk-testing/framework-integ/test/custom-resources/test/aws-custom-resource/integ.cross-account-assumeRole.js.snapshot/cross-account-opt-in-stack.template.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@
7575
"Properties": {
7676
"Code": {
7777
"S3Bucket": "cdk-hnb659fds-assets-234567890123-af-south-1",
78-
"S3Key": "746da84b10e215c552e68b6d2061024e4429f0386f43a35ef5e4d2940655692e.zip"
78+
"S3Key": "e6691dc6014ee9cc203ede754a4dbd33c0a6f12909a8bedd9fdd630d3f545154.zip"
7979
},
8080
"Handler": "index.handler",
8181
"Role": {

packages/@aws-cdk-testing/framework-integ/test/custom-resources/test/aws-custom-resource/integ.cross-account-assumeRole.js.snapshot/cross-account-stack.template.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
"Arn"
1010
]
1111
},
12-
"Create": "{\"assumedRoleArn\":\"arn:aws:iam::12345678:role/MyUniqueRole\",\"service\":\"@aws-sdk/client-sts\",\"action\":\"GetCallerIdentityCommand\",\"physicalResourceId\":{\"id\":\"id\"}}",
12+
"Create": "{\"assumedRoleArn\":\"arn:aws:iam::12345678:role/MyUniqueRole\",\"assumedRoleExternalId\":\"external-id-1234\",\"service\":\"@aws-sdk/client-sts\",\"action\":\"GetCallerIdentityCommand\",\"physicalResourceId\":{\"id\":\"id\"}}",
1313
"InstallLatestAwsSdk": false
1414
},
1515
"DependsOn": [
@@ -75,7 +75,7 @@
7575
"Properties": {
7676
"Code": {
7777
"S3Bucket": "cdk-hnb659fds-assets-234567890123-us-east-1",
78-
"S3Key": "746da84b10e215c552e68b6d2061024e4429f0386f43a35ef5e4d2940655692e.zip"
78+
"S3Key": "e6691dc6014ee9cc203ede754a4dbd33c0a6f12909a8bedd9fdd630d3f545154.zip"
7979
},
8080
"Handler": "index.handler",
8181
"Role": {

packages/@aws-cdk-testing/framework-integ/test/custom-resources/test/aws-custom-resource/integ.cross-account-assumeRole.js.snapshot/role-stack.template.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@
77
"Statement": [
88
{
99
"Action": "sts:AssumeRole",
10+
"Condition": {
11+
"StringEquals": {
12+
"sts:ExternalId": "external-id-1234"
13+
}
14+
},
1015
"Effect": "Allow",
1116
"Principal": {
1217
"AWS": "arn:aws:iam::234567890123:root"

packages/@aws-cdk-testing/framework-integ/test/custom-resources/test/aws-custom-resource/integ.cross-account-assumeRole.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,11 @@ const crossAccountOptInStack = new cdk.Stack(app, 'cross-account-opt-in-stack',
5858

5959
const roleName = 'MyUniqueRole';
6060

61+
const assumedRoleExternalId = 'external-id-1234';
6162
new iam.Role(roleStack, 'AssumeThisRole', {
6263
roleName: roleName,
6364
assumedBy: new iam.AccountPrincipal(crossAccount),
65+
externalIds: [assumedRoleExternalId],
6466
});
6567

6668
const assumedRoleArn = cdk.Stack.of(crossAccountStack).formatArn({
@@ -75,6 +77,7 @@ new AwsCustomResource(crossAccountStack, 'CrossAccountCR', {
7577
installLatestAwsSdk: false,
7678
onCreate: {
7779
assumedRoleArn: assumedRoleArn,
80+
assumedRoleExternalId: assumedRoleExternalId,
7881
service: '@aws-sdk/client-sts',
7982
action: 'GetCallerIdentityCommand',
8083
physicalResourceId: PhysicalResourceId.of('id'),

packages/@aws-cdk/custom-resource-handlers/lib/custom-resources/aws-custom-resource-handler/construct-types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,6 @@ export interface AwsSdkCall {
2121
readonly outputPath?: string;
2222
readonly outputPaths?: string[];
2323
readonly assumedRoleArn?: string;
24+
readonly assumedRoleExternalId?: string;
2425
readonly logApiResponseData?: boolean;
2526
}

packages/@aws-cdk/custom-resource-handlers/lib/custom-resources/aws-custom-resource-handler/utils.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,11 +103,15 @@ export async function getCredentials(call: AwsSdkCall, physicalResourceId: strin
103103
if (call.assumedRoleArn) {
104104
const timestamp = (new Date()).getTime();
105105

106-
const params = {
106+
const params:{RoleArn: string; RoleSessionName: string; ExternalId?: string} = {
107107
RoleArn: call.assumedRoleArn,
108108
RoleSessionName: `${timestamp}-${physicalResourceId}`.substring(0, 64),
109109
};
110110

111+
if (call.assumedRoleExternalId) {
112+
params.ExternalId = call.assumedRoleExternalId;
113+
}
114+
111115
const { fromTemporaryCredentials } = await import('@aws-sdk/credential-providers');
112116
credentials = fromTemporaryCredentials({
113117
params,

packages/@aws-cdk/custom-resource-handlers/test/custom-resources/aws-custom-resource/aws-sdk-v3-handler.test.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -610,6 +610,7 @@ test('SDK credentials are not persisted across subsequent invocations', async ()
610610
service: '@aws-sdk/client-s3',
611611
action: 'GetObjectCommand',
612612
assumedRoleArn: 'arn:aws:iam::123456789012:role/CoolRole',
613+
assumedRoleExternalId: 'external-id-1234',
613614
parameters: {
614615
Bucket: 'foo',
615616
Key: 'bar',
@@ -622,7 +623,12 @@ test('SDK credentials are not persisted across subsequent invocations', async ()
622623
ServiceToken: 'serviceToken',
623624
StackId: 'stackId',
624625
}, {} as AWSLambda.Context);
625-
expect(credentialProviderMock).toHaveBeenCalled();
626+
expect(credentialProviderMock).toHaveBeenCalledWith(
627+
expect.objectContaining({
628+
RoleArn: 'arn:aws:iam::123456789012:role/CoolRole',
629+
ExternalId: 'external-id-1234',
630+
}),
631+
);
626632
credentialProviderMock.mockClear();
627633

628634
await handler({

packages/aws-cdk-lib/custom-resources/lib/aws-custom-resource/aws-custom-resource.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,14 @@ export interface AwsSdkCall {
189189
*/
190190
readonly assumedRoleArn?: string;
191191

192+
/**
193+
* When assumedRoleArn is also specified, this external ID is used while assuming
194+
* the role.
195+
*
196+
* @default - assume role without using an external ID
197+
*/
198+
readonly assumedRoleExternalId?: string;
199+
192200
/**
193201
* A property used to configure logging during lambda function execution.
194202
*

0 commit comments

Comments
 (0)