Skip to content

Commit

Permalink
feat(cli): add ability to specify an external id for the deploy-role (a…
Browse files Browse the repository at this point in the history
…ws#15604)

added the ability to specify an external id for the deploy-role as part of the synthesizer config.
this is similar to the existing functionality that allows specifying the external id for the image
and file publishing roles

In order to take advantage of this functionality you would need to customize the bootstrap template. To test this feature I customized the DeploymentActionRole in the bootstrap template to have the `AssumeRolePolicyDocument` as:

```yaml
DeploymentActionRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Statement:
          - Action: sts:AssumeRole
            Condition:
              StringEquals:
                sts:ExternalId: "my-external-id"
            Effect: Allow
            Principal:
              AWS:
                - Fn::Join:
                    - ""
                    - - "arn:"
                      - Ref: AWS::Partition
                      - :iam::1111111111111:root
        Version: "2012-10-17"
      ...
      RoleName:
        Fn::Sub: cdk-${Qualifier}-deploy-role-${AWS::AccountId}-${AWS::Region}
```

I then created a test stack that specified that external id:

```ts
new CdkExternalIdTestStack(app, 'CdkExternalIdTestStack', {
  env: {
    account: '111111111111',
    region: 'us-east-2',
  },
  synthesizer: new cdk.DefaultStackSynthesizer({
    deployRoleExternalId: 'my-external-id',
  }),
});
```
----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
  • Loading branch information
corymhall authored and hollanddd committed Aug 26, 2021
1 parent 44f9803 commit 0b07a8d
Show file tree
Hide file tree
Showing 8 changed files with 59 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,13 @@ export interface AwsCloudFormationStackProperties {
*/
readonly assumeRoleArn?: string;

/**
* External ID to use when assuming role for cloudformation deployments
*
* @default - No external ID
*/
readonly assumeRoleExternalId?: string;

/**
* The role that is passed to CloudFormation to execute the change set
*
Expand Down Expand Up @@ -149,4 +156,4 @@ export interface NestedCloudAssemblyProperties {
export type ArtifactProperties = AwsCloudFormationStackProperties
| AssetManifestProperties
| TreeArtifactProperties
| NestedCloudAssemblyProperties;
| NestedCloudAssemblyProperties;
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,10 @@
"description": "The role that needs to be assumed to deploy the stack (Default - No role is assumed (current credentials are used))",
"type": "string"
},
"assumeRoleExternalId": {
"description": "External ID to use when assuming role for cloudformation deployments (Default - No external ID)",
"type": "string"
},
"cloudFormationExecutionRoleArn": {
"description": "The role that is passed to CloudFormation to execute the change set (Default - No role is passed (currently assumed role/credentials are used))",
"type": "string"
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"version":"12.0.0"}
{"version":"13.0.0"}
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,13 @@ export interface DefaultStackSynthesizerProps {
*/
readonly imageAssetPublishingExternalId?: string;

/**
* External ID to use when assuming role for cloudformation deployments
*
* @default - No external ID
*/
readonly deployRoleExternalId?: string;

/**
* The role to assume to initiate a deployment in this environment
*
Expand Down Expand Up @@ -424,6 +431,7 @@ export class DefaultStackSynthesizer extends StackSynthesizer {
const artifactId = this.writeAssetManifest(session);

this.emitStackArtifact(this.stack, session, {
assumeRoleExternalId: this.props.deployRoleExternalId,
assumeRoleArn: this._deployRoleArn,
cloudFormationExecutionRoleArn: this._cloudFormationExecutionRoleArn,
stackTemplateAssetObjectUrl: templateManifestUrl,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,13 @@ export interface SynthesizeStackArtifactOptions {
*/
readonly assumeRoleArn?: string;

/**
* The externalID to use with the assumeRoleArn
*
* @default - No externalID is used
*/
readonly assumeRoleExternalId?: string;

/**
* The role that is passed to CloudFormation to execute the change set
*
Expand Down Expand Up @@ -121,4 +128,4 @@ export interface SynthesizeStackArtifactOptions {
* @default - Bootstrap stack version number looked up
*/
readonly bootstrapStackVersionSsmParameter?: string;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,26 @@ nodeunitShim({
test.done();
},

'customize deploy role externalId'(test: Test) {
// GIVEN
const myapp = new App();

// WHEN
const mystack = new Stack(myapp, 'mystack', {
synthesizer: new DefaultStackSynthesizer({
deployRoleExternalId: 'deploy-external-id',
}),
});

// THEN
const asm = myapp.synth();

const stackArtifact = asm.getStack(mystack.stackName);
expect(stackArtifact.assumeRoleExternalId).toEqual('deploy-external-id');

test.done();
},

'synthesis with bucketPrefix'(test: Test) {
// GIVEN
const myapp = new App();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,13 @@ export class CloudFormationStackArtifact extends CloudArtifact {
*/
public readonly assumeRoleArn?: string;

/**
* External ID to use when assuming role for cloudformation deployments
*
* @default - No external ID
*/
readonly assumeRoleExternalId?: string;

/**
* The role that is passed to CloudFormation to execute the change set
*
Expand Down Expand Up @@ -121,6 +128,7 @@ export class CloudFormationStackArtifact extends CloudArtifact {
// from the stack metadata
this.tags = properties.tags ?? this.tagsFromMetadata();
this.assumeRoleArn = properties.assumeRoleArn;
this.assumeRoleExternalId = properties.assumeRoleExternalId;
this.cloudFormationExecutionRoleArn = properties.cloudFormationExecutionRoleArn;
this.stackTemplateAssetObjectUrl = properties.stackTemplateAssetObjectUrl;
this.requiresBootstrapStackVersion = properties.requiresBootstrapStackVersion;
Expand Down Expand Up @@ -165,4 +173,4 @@ export class CloudFormationStackArtifact extends CloudArtifact {
}
return ret;
}
}
}
1 change: 1 addition & 0 deletions packages/aws-cdk/lib/api/cloudformation-deployments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,7 @@ export class CloudFormationDeployments {

const stackSdk = await this.sdkProvider.forEnvironment(resolvedEnvironment, mode, {
assumeRoleArn: arns.assumeRoleArn,
assumeRoleExternalId: stack.assumeRoleExternalId,
});

return {
Expand Down

0 comments on commit 0b07a8d

Please sign in to comment.