Skip to content

Commit 29d7336

Browse files
authored
feat(pipeline): enable key rotation (#23620)
---- closes #23595 ### All Submissions: * [y] Have you followed the guidelines in our [Contributing guide?](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) ### Adding new Construct Runtime Dependencies: * [n] This PR adds new construct runtime dependencies following the process described [here](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md/#adding-construct-runtime-dependencies) ### New Features * [y] Have you added the new feature to an [integration test](https://github.com/aws/aws-cdk/blob/main/INTEGRATION_TESTS.md)? * [y] Did you use `yarn integ` to deploy the infrastructure and generate the snapshot (i.e. `yarn integ` without `--dry-run`)? *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
1 parent c2f8f9e commit 29d7336

File tree

54 files changed

+9098
-5
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

54 files changed

+9098
-5
lines changed

packages/@aws-cdk/pipelines/README.md

+3
Original file line numberDiff line numberDiff line change
@@ -525,12 +525,15 @@ pipeline.
525525
This will encrypt the artifact bucket(s), but incurs a cost for maintaining the
526526
KMS key.
527527

528+
You may also wish to enable automatic key rotation for the created KMS key.
529+
528530
Example:
529531

530532
```ts
531533
const pipeline = new pipelines.CodePipeline(this, 'Pipeline', {
532534
// Encrypt artifacts, required for cross-account deployments
533535
crossAccountKeys: true,
536+
enableKeyRotation: true, // optional
534537
synth: new pipelines.ShellStep('Synth', {
535538
input: pipelines.CodePipelineSource.connection('my-org/my-app', 'main', {
536539
connectionArn: 'arn:aws:codestar-connections:us-east-1:222222222222:connection/7d2469ff-514a-4e4f-9003-5ca4a43cdc41', // Created using the AWS console * });',

packages/@aws-cdk/pipelines/lib/codepipeline/codepipeline.ts

+14
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,16 @@ export interface CodePipelineProps {
225225
* @default true
226226
*/
227227
readonly useChangeSets?: boolean;
228+
229+
/**
230+
* Enable KMS key rotation for the generated KMS keys.
231+
*
232+
* By default KMS key rotation is disabled, but will add
233+
* additional costs when enabled.
234+
*
235+
* @default - false (key rotation is disabled)
236+
*/
237+
readonly enableKeyRotation?: boolean;
228238
}
229239

230240
/**
@@ -381,6 +391,9 @@ export class CodePipeline extends PipelineBase {
381391
if (this.props.crossAccountKeys !== undefined) {
382392
throw new Error('Cannot set \'crossAccountKeys\' if an existing CodePipeline is given using \'codePipeline\'');
383393
}
394+
if (this.props.enableKeyRotation !== undefined) {
395+
throw new Error('Cannot set \'enableKeyRotation\' if an existing CodePipeline is given using \'codePipeline\'');
396+
}
384397
if (this.props.reuseCrossRegionSupportStacks !== undefined) {
385398
throw new Error('Cannot set \'reuseCrossRegionSupportStacks\' if an existing CodePipeline is given using \'codePipeline\'');
386399
}
@@ -398,6 +411,7 @@ export class CodePipeline extends PipelineBase {
398411
// to happen only after the builds of the latest pipeline definition).
399412
restartExecutionOnUpdate: true,
400413
role: this.props.role,
414+
enableKeyRotation: this.props.enableKeyRotation,
401415
});
402416
}
403417

packages/@aws-cdk/pipelines/test/codepipeline/codepipeline.test.ts

+72-5
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,11 @@ describe('Providing codePipeline parameter and prop(s) of codePipeline parameter
8181
pipelineName: 'randomName',
8282
}).create()).toThrowError('Cannot set \'pipelineName\' if an existing CodePipeline is given using \'codePipeline\'');
8383
});
84+
test('Providing codePipeline parameter and enableKeyRotation parameter should throw error', () => {
85+
expect(() => new CodePipelinePropsCheckTest(app, 'CodePipeline', {
86+
enableKeyRotation: true,
87+
}).create()).toThrowError('Cannot set \'enableKeyRotation\' if an existing CodePipeline is given using \'codePipeline\'');
88+
});
8489
test('Providing codePipeline parameter and crossAccountKeys parameter should throw error', () => {
8590
expect(() => new CodePipelinePropsCheckTest(app, 'CodePipeline', {
8691
crossAccountKeys: true,
@@ -192,6 +197,60 @@ test('CodeBuild action role has the right AssumeRolePolicyDocument', () => {
192197
});
193198
});
194199

200+
test('CodePipeline throws when key rotation is enabled without enabling cross account keys', ()=>{
201+
const pipelineStack = new cdk.Stack(app, 'PipelineStack', { env: PIPELINE_ENV });
202+
const repo = new ccommit.Repository(pipelineStack, 'Repo', {
203+
repositoryName: 'MyRepo',
204+
});
205+
const cdkInput = cdkp.CodePipelineSource.codeCommit(
206+
repo,
207+
'main',
208+
);
209+
210+
expect(() => new CodePipeline(pipelineStack, 'Pipeline', {
211+
enableKeyRotation: true,
212+
synth: new cdkp.ShellStep('Synth', {
213+
input: cdkInput,
214+
installCommands: ['npm ci'],
215+
commands: [
216+
'npm run build',
217+
'npx cdk synth',
218+
],
219+
}),
220+
}).buildPipeline()).toThrowError('Setting \'enableKeyRotation\' to true also requires \'crossAccountKeys\' to be enabled');
221+
});
222+
223+
224+
test('CodePipeline enables key rotation on cross account keys', ()=>{
225+
const pipelineStack = new cdk.Stack(app, 'PipelineStack', { env: PIPELINE_ENV });
226+
const repo = new ccommit.Repository(pipelineStack, 'Repo', {
227+
repositoryName: 'MyRepo',
228+
});
229+
const cdkInput = cdkp.CodePipelineSource.codeCommit(
230+
repo,
231+
'main',
232+
);
233+
234+
new CodePipeline(pipelineStack, 'Pipeline', {
235+
enableKeyRotation: true,
236+
crossAccountKeys: true, // requirement of key rotation
237+
synth: new cdkp.ShellStep('Synth', {
238+
input: cdkInput,
239+
installCommands: ['npm ci'],
240+
commands: [
241+
'npm run build',
242+
'npx cdk synth',
243+
],
244+
}),
245+
});
246+
247+
const template = Template.fromStack(pipelineStack);
248+
249+
template.hasResourceProperties('AWS::KMS::Key', {
250+
EnableKeyRotation: true,
251+
});
252+
});
253+
195254
test('CodePipeline supports use of existing role', () => {
196255
const pipelineStack = new cdk.Stack(app, 'PipelineStack', { env: PIPELINE_ENV });
197256
const repo = new ccommit.Repository(pipelineStack, 'Repo', {
@@ -393,6 +452,7 @@ class ReuseStack extends cdk.Stack {
393452
interface CodePipelineStackProps extends cdk.StackProps {
394453
pipelineName?: string;
395454
crossAccountKeys?: boolean;
455+
enableKeyRotation?: boolean;
396456
reuseCrossRegionSupportStacks?: boolean;
397457
role?: iam.IRole;
398458
}
@@ -414,21 +474,28 @@ class CodePipelinePropsCheckTest extends cdk.Stack {
414474
if (this.cProps.crossAccountKeys !== undefined) {
415475
new cdkp.CodePipeline(this, 'CodePipeline2', {
416476
crossAccountKeys: this.cProps.crossAccountKeys,
417-
codePipeline: new Pipeline(this, 'Pipline2'),
477+
codePipeline: new Pipeline(this, 'Pipeline2'),
418478
synth: new cdkp.ShellStep('Synth', { commands: ['ls'] }),
419479
}).buildPipeline();
420480
}
421-
if (this.cProps.reuseCrossRegionSupportStacks !== undefined) {
481+
if (this.cProps.enableKeyRotation !== undefined) {
422482
new cdkp.CodePipeline(this, 'CodePipeline3', {
483+
enableKeyRotation: this.cProps.enableKeyRotation,
484+
codePipeline: new Pipeline(this, 'Pipeline3'),
485+
synth: new cdkp.ShellStep('Synth', { commands: ['ls'] }),
486+
}).buildPipeline();
487+
}
488+
if (this.cProps.reuseCrossRegionSupportStacks !== undefined) {
489+
new cdkp.CodePipeline(this, 'CodePipeline4', {
423490
reuseCrossRegionSupportStacks: this.cProps.reuseCrossRegionSupportStacks,
424-
codePipeline: new Pipeline(this, 'Pipline3'),
491+
codePipeline: new Pipeline(this, 'Pipeline4'),
425492
synth: new cdkp.ShellStep('Synth', { commands: ['ls'] }),
426493
}).buildPipeline();
427494
}
428495
if (this.cProps.role !== undefined) {
429-
new cdkp.CodePipeline(this, 'CodePipeline4', {
496+
new cdkp.CodePipeline(this, 'CodePipeline5', {
430497
role: this.cProps.role,
431-
codePipeline: new Pipeline(this, 'Pipline4'),
498+
codePipeline: new Pipeline(this, 'Pipeline5'),
432499
synth: new cdkp.ShellStep('Synth', { commands: ['ls'] }),
433500
}).buildPipeline();
434501
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"version": "29.0.0",
3+
"files": {
4+
"21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": {
5+
"source": {
6+
"path": "LambdaTestDefaultTestDeployAssert1AF2B360.template.json",
7+
"packaging": "file"
8+
},
9+
"destinations": {
10+
"current_account-current_region": {
11+
"bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}",
12+
"objectKey": "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json",
13+
"assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}"
14+
}
15+
}
16+
}
17+
},
18+
"dockerImages": {}
19+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
{
2+
"Parameters": {
3+
"BootstrapVersion": {
4+
"Type": "AWS::SSM::Parameter::Value<String>",
5+
"Default": "/cdk-bootstrap/hnb659fds/version",
6+
"Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]"
7+
}
8+
},
9+
"Rules": {
10+
"CheckBootstrapVersion": {
11+
"Assertions": [
12+
{
13+
"Assert": {
14+
"Fn::Not": [
15+
{
16+
"Fn::Contains": [
17+
[
18+
"1",
19+
"2",
20+
"3",
21+
"4",
22+
"5"
23+
],
24+
{
25+
"Ref": "BootstrapVersion"
26+
}
27+
]
28+
}
29+
]
30+
},
31+
"AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI."
32+
}
33+
]
34+
}
35+
}
36+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"version": "29.0.0",
3+
"files": {
4+
"c1bda7e2c84e91a5658af0b194a4c8918dead52ad783e29acbc3701149e73f45": {
5+
"source": {
6+
"path": "PipelineStack.template.json",
7+
"packaging": "file"
8+
},
9+
"destinations": {
10+
"current_account-current_region": {
11+
"bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}",
12+
"objectKey": "c1bda7e2c84e91a5658af0b194a4c8918dead52ad783e29acbc3701149e73f45.json",
13+
"assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}"
14+
}
15+
}
16+
}
17+
},
18+
"dockerImages": {}
19+
}

0 commit comments

Comments
 (0)