Skip to content

Commit 6525d51

Browse files
authored
fix(app-staging-synthesizer): custom bootstrap qualifier is not passed through to deployment role name (#35118)
### Issue #28195 Closes #28195 ### Reason for this change When passing `bootstrapQualifier` in props to `AppStagingSynthesizer.defaultResources`, the synthesizer would still use the default qualifier `'hnb659fds'` when looking for bootstrap roles. ### Description of changes `BootstraplessSynthesizer` is modified to take `qualifier` as an optional argument (if not provided, default bootstrap qualifier 'hnb659fds' is used). The `bootstrapqualifier` is passed to `BootstraplessSynthesizer`, which is called in `AppStagingSynthesizer.defaultResources()`. These changes ensure that calls to `AppStagingSynthesizer.defaultResources` using the `bootstrapQualifier` will use the qualifier in the deployment and CloudFormation execution roles instead of the default qualifier 'hnb659fds'. ### Describe any new or updated permissions being added None. ### Description of how you validated changes Added unit tests for: - `BootstraplessSynthesizer`, which now optionally takes `qualifier` as an option - `AppStagingSynthesizer`, which passes `qualifier` to `BootstraplessSynthesizer` Tested by hand in a personal dev account. ### 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*
1 parent 3723aca commit 6525d51

File tree

9 files changed

+122
-3
lines changed

9 files changed

+122
-3
lines changed

packages/@aws-cdk/app-staging-synthesizer-alpha/lib/default-staging-stack.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ export interface DefaultStagingStackProps extends DefaultStagingStackOptions, St
155155
/**
156156
* The qualifier used to specialize strings
157157
*
158-
* Shouldn't be necessary but who knows what people might do.
158+
* Can be used to specify custom bootstrapped role names
159159
*/
160160
readonly qualifier: string;
161161
}
@@ -259,10 +259,13 @@ export class DefaultStagingStack extends Stack implements IStagingResources {
259259
constructor(scope: App, id: string, private readonly props: DefaultStagingStackProps) {
260260
super(scope, id, {
261261
...props,
262-
synthesizer: new BootstraplessSynthesizer(),
262+
synthesizer: new BootstraplessSynthesizer({
263+
qualifier: props.qualifier,
264+
}),
263265
description: `This stack includes resources needed to deploy the AWS CDK app ${props.appId} into this environment`,
264266
analyticsReporting: false, // removing AWS::CDK::Metadata construct saves ~3KB
265267
});
268+
266269
// removing path metadata saves ~2KB
267270
this.node.setContext(cxapi.PATH_METADATA_ENABLE_CONTEXT, false);
268271

@@ -283,7 +286,6 @@ export class DefaultStagingStack extends Stack implements IStagingResources {
283286
this.stagingBucketEncryption = props.stagingBucketEncryption;
284287

285288
const specializer = new StringSpecializer(this, props.qualifier);
286-
287289
this.providedFileRole = props.fileAssetPublishingRole?._specialize(specializer);
288290
this.providedImageRole = props.imageAssetPublishingRole?._specialize(specializer);
289291
this.stagingRepos = {};

packages/@aws-cdk/app-staging-synthesizer-alpha/test/app-staging-synthesizer.test.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,35 @@ describe(AppStagingSynthesizer, () => {
2626
});
2727
});
2828

29+
test('uses qualifier in deployment roles and staging context', () => {
30+
// GIVEN
31+
const qualifier = 'custom-qualifier';
32+
const customApp = new App({
33+
defaultStackSynthesizer: AppStagingSynthesizer.defaultResources({
34+
appId: APP_ID,
35+
bootstrapQualifier: qualifier,
36+
stagingBucketEncryption: BucketEncryption.S3_MANAGED,
37+
}),
38+
});
39+
new Stack(customApp, 'CustomStack', {
40+
env: {
41+
account: '111111111111',
42+
region: 'us-east-1',
43+
},
44+
});
45+
46+
// WHEN
47+
const assembly = customApp.synth();
48+
const artifact = assembly.getStackArtifact('CustomStack');
49+
50+
// THEN
51+
// The deployment role should contain the custom qualifier
52+
expect(artifact.assumeRoleArn).toBe(`arn:\${AWS::Partition}:iam::111111111111:role/cdk-${qualifier}-deploy-role-111111111111-us-east-1`);
53+
54+
// The CloudFormation execution role should contain the custom qualifier
55+
expect(artifact.cloudFormationExecutionRoleArn).toBe(`arn:\${AWS::Partition}:iam::111111111111:role/cdk-${qualifier}-cfn-exec-role-111111111111-us-east-1`);
56+
});
57+
2958
test('stack template is in asset manifest', () => {
3059
// GIVEN
3160
new CfnResource(stack, 'Resource', {

packages/aws-cdk-lib/core/lib/stack-synthesizers/bootstrapless-synthesizer.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,16 @@ import { UnscopedValidationError } from '../errors';
77
* Construction properties of `BootstraplessSynthesizer`.
88
*/
99
export interface BootstraplessSynthesizerProps {
10+
/**
11+
* The qualifier used to specialize strings
12+
*
13+
* Can be used to specify custom bootstrapped role names
14+
*
15+
* @default 'hnb659fds'
16+
*
17+
*/
18+
readonly qualifier?: string;
19+
1020
/**
1121
* The deploy Role ARN to use.
1222
*
@@ -47,6 +57,7 @@ export class BootstraplessSynthesizer extends DefaultStackSynthesizer {
4757
deployRoleArn: props.deployRoleArn,
4858
cloudFormationExecutionRole: props.cloudFormationExecutionRoleArn,
4959
generateBootstrapVersionRule: false,
60+
qualifier: props.qualifier,
5061
});
5162
}
5263

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import { FileAssetPackaging } from '@aws-cdk/cloud-assembly-schema';
2+
import { App } from '../../lib/app';
3+
import { Stack } from '../../lib/stack';
4+
import { BootstraplessSynthesizer } from '../../lib/stack-synthesizers/bootstrapless-synthesizer';
5+
6+
describe('BootstraplessSynthesizer', () => {
7+
test('differs from DefaultStackSynthesizer in bootstrap requirements', () => {
8+
// GIVEN
9+
const app = new App();
10+
new Stack(app, 'BootstraplessStack', {
11+
synthesizer: new BootstraplessSynthesizer(),
12+
env: {
13+
account: '111111111111',
14+
region: 'us-east-1',
15+
},
16+
});
17+
18+
const assembly = app.synth();
19+
20+
// THEN - bootstrapless stack has no bootstrap requirements
21+
const bootstraplessArtifact = assembly.getStackArtifact('BootstraplessStack');
22+
expect(bootstraplessArtifact.requiresBootstrapStackVersion).toBeUndefined();
23+
expect(bootstraplessArtifact.template.Parameters?.BootstrapVersion).toBeUndefined();
24+
expect(bootstraplessArtifact.template.Parameters?.FileAssetsBucketName).toBeUndefined();
25+
expect(bootstraplessArtifact.template.Parameters?.ImageRepositoryName).toBeUndefined();
26+
});
27+
28+
test('throws when attempting to add assets', () => {
29+
// GIVEN
30+
const app = new App();
31+
const stack = new Stack(app, 'Stack', {
32+
synthesizer: new BootstraplessSynthesizer(),
33+
env: {
34+
account: '111111111111',
35+
region: 'us-east-1',
36+
},
37+
});
38+
39+
// THEN
40+
expect(() => stack.synthesizer.addFileAsset({
41+
fileName: __filename,
42+
packaging: FileAssetPackaging.FILE,
43+
sourceHash: 'file-hash',
44+
})).toThrow('Cannot add assets to a Stack that uses the BootstraplessSynthesizer');
45+
46+
expect(() => stack.synthesizer.addDockerImageAsset({
47+
directoryName: '.',
48+
sourceHash: 'docker-hash',
49+
})).toThrow('Cannot add assets to a Stack that uses the BootstraplessSynthesizer');
50+
});
51+
52+
test('uses qualifier in deployment and execution roles', () => {
53+
// GIVEN
54+
const qualifier = 'custom-qualifier';
55+
const app = new App();
56+
const stack = new Stack(app, 'Stack', {
57+
synthesizer: new BootstraplessSynthesizer({
58+
qualifier,
59+
}),
60+
env: {
61+
account: '111111111111',
62+
region: 'us-east-1',
63+
},
64+
});
65+
66+
// WHEN
67+
const assembly = app.synth();
68+
const artifact = assembly.getStackArtifact('Stack');
69+
70+
// THEN
71+
// The deployment role should contain the custom qualifier and use AWS::Partition placeholder
72+
expect(artifact.assumeRoleArn).toBe(`arn:\${AWS::Partition}:iam::111111111111:role/cdk-${qualifier}-deploy-role-111111111111-us-east-1`);
73+
74+
// The CloudFormation execution role should contain the custom qualifier and use AWS::Partition placeholder
75+
expect(artifact.cloudFormationExecutionRoleArn).toBe(`arn:\${AWS::Partition}:iam::111111111111:role/cdk-${qualifier}-cfn-exec-role-111111111111-us-east-1`);
76+
});
77+
});

0 commit comments

Comments
 (0)