Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(pipeline): allow enabling KMS key rotation for cross-region Stacks #16468

Merged
merged 48 commits into from
Sep 20, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
da4a69a
feat(pipeline): add enable KMS key rotation prop
pboeder Sep 10, 2021
e1740f3
chore(pipeline): restore readme formatting
pboeder Sep 10, 2021
dd8d2e0
feat(pipeline): add enable KMS key rotation prop
pboeder Sep 10, 2021
cc9a830
chore(pipeline): restore readme formatting
pboeder Sep 10, 2021
8de11e0
Merge branch 'feat-pipelines-keyrotation' of github.com:ppuritscher/a…
pboeder Sep 13, 2021
f6b88e4
fix(pipeline): update snapshots
pboeder Sep 13, 2021
7462c30
chore(pipeline): update snapshot event-target
pboeder Sep 13, 2021
45b7239
chore(pipeline): update snapshots
pboeder Sep 13, 2021
94989f3
Merge branch 'master' of github.com:aws/aws-cdk into feat-pipelines-k…
pboeder Sep 14, 2021
1eab24d
fix: 🐛 set default value of enableKeyRotation to undefined
pboeder Sep 14, 2021
4561ef5
refactor: 💡 restore expected.json order
pboeder Sep 14, 2021
7cf2c2f
refactor(pipeline): 💡 cleanup tests
pboeder Sep 15, 2021
6e9c7e2
refactor(pipeline): 💡 cleanup docs enableKeyRotation
pboeder Sep 15, 2021
3731468
chore(pipeline): 🤖 fix documentation
pboeder Sep 16, 2021
885c652
Merge branch 'aws:master' into feat-pipelines-keyrotation
pboeder Sep 16, 2021
fcc8d02
docs(pipeline): ✏️ fix documentations
pboeder Sep 16, 2021
2e54db0
test(codepipeline): 💍 enableKeyRotation if existing CodePipeline given
pboeder Sep 16, 2021
df454ef
feat(pipeline): add enable KMS key rotation prop
pboeder Sep 10, 2021
2cb95bf
chore(pipeline): restore readme formatting
pboeder Sep 10, 2021
5d1161c
feat(pipeline): add enable KMS key rotation prop
pboeder Sep 16, 2021
a32a0a9
fix(pipeline): update snapshots
pboeder Sep 13, 2021
8d73ce4
chore(pipeline): update snapshot event-target
pboeder Sep 13, 2021
c339f95
chore(pipeline): update snapshots
pboeder Sep 13, 2021
7d816a2
fix: 🐛 set default value of enableKeyRotation to undefined
pboeder Sep 14, 2021
640bffb
refactor: 💡 restore expected.json order
pboeder Sep 14, 2021
f64b7dd
refactor(pipeline): 💡 cleanup tests
pboeder Sep 15, 2021
89bc1c2
refactor(pipeline): 💡 cleanup docs enableKeyRotation
pboeder Sep 16, 2021
9ba0bf5
chore(pipeline): 🤖 fix documentation
pboeder Sep 16, 2021
2d01117
docs(pipeline): ✏️ fix documentations
pboeder Sep 16, 2021
3f0256c
test(codepipeline): 💍 enableKeyRotation if existing CodePipeline given
pboeder Sep 16, 2021
8722e8a
Merge branch 'feat-pipelines-keyrotation' of github.com:ppuritscher/a…
pboeder Sep 16, 2021
b5c121b
feat(pipeline): add enable KMS key rotation prop
pboeder Sep 16, 2021
f09459d
chore(pipeline): restore readme formatting
pboeder Sep 16, 2021
5862486
feat(pipeline): add enable KMS key rotation prop
pboeder Sep 16, 2021
eff1ee6
chore(pipeline): restore readme formatting
pboeder Sep 16, 2021
35a9d7b
fix(pipeline): update snapshots
pboeder Sep 16, 2021
d863243
chore(pipeline): update snapshot event-target
pboeder Sep 13, 2021
169ad8b
chore(pipeline): update snapshots
pboeder Sep 13, 2021
59d3d3c
fix: 🐛 set default value of enableKeyRotation to undefined
pboeder Sep 14, 2021
a7a4ebd
refactor: 💡 restore expected.json order
pboeder Sep 14, 2021
2fa0870
refactor(pipeline): 💡 cleanup docs enableKeyRotation
pboeder Sep 15, 2021
643e0c0
chore(pipeline): 🤖 fix documentation
pboeder Sep 16, 2021
ff8d30e
docs(pipeline): ✏️ fix documentations
pboeder Sep 16, 2021
3b051b9
Merge branch 'feat-pipelines-keyrotation' of github.com:ppuritscher/a…
pboeder Sep 16, 2021
0b65fd2
chore(codepipeline): 🤖 revert merge issues
pboeder Sep 16, 2021
5f90991
test(pipelines): 💍 add tests for cases with given codePipeline
pboeder Sep 20, 2021
d8fc5c0
Merge branch 'master' into feat-pipelines-keyrotation
pboeder Sep 20, 2021
3bf5dd1
Merge branch 'master' into feat-pipelines-keyrotation
mergify[bot] Sep 20, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -792,4 +792,4 @@
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1622,4 +1622,4 @@
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -788,4 +788,4 @@
]
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -393,4 +393,4 @@
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -731,4 +731,4 @@
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -815,4 +815,4 @@
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -545,4 +545,4 @@
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -842,4 +842,4 @@
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -540,4 +540,4 @@
"DeletionPolicy": "Retain"
}
}
}
}
11 changes: 11 additions & 0 deletions packages/@aws-cdk/aws-codepipeline/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,17 @@ const pipeline = new codepipeline.Pipeline(this, 'MyFirstPipeline', {
});
```

If you want to enable key rotation for the generated KMS keys,
you can configure it by passing `enableKeyRotation: true` when creating the pipeline.
Note that key rotation will incur an additional cost of **$1/month**.

```ts
const pipeline = new codepipeline.Pipeline(this, 'MyFirstPipeline', {
// ...
enableKeyRotation: true,
});
```

## Stages

You can provide Stages when creating the Pipeline:
Expand Down
26 changes: 22 additions & 4 deletions packages/@aws-cdk/aws-codepipeline/lib/pipeline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ export interface PipelineProps {
readonly stages?: StageProps[];

/**
* Create KMS keys for cross-account deployments
* Create KMS keys for cross-account deployments.
*
* This controls whether the pipeline is enabled for cross-account deployments.
*
Expand All @@ -126,6 +126,16 @@ export interface PipelineProps {
* @default true
*/
readonly crossAccountKeys?: boolean;

/**
* Enable KMS key rotation for the generated KMS keys.
*
* By default KMS key rotation is disabled, but will add an additional $1/month
* for each year the key exists when enabled.
*
* @default - false (key rotation is disabled)
*/
readonly enableKeyRotation?: boolean;
}

abstract class PipelineBase extends Resource implements IPipeline {
Expand Down Expand Up @@ -317,6 +327,7 @@ export class Pipeline extends PipelineBase {
private readonly _crossRegionSupport: { [region: string]: CrossRegionSupport } = {};
private readonly _crossAccountSupport: { [account: string]: Stack } = {};
private readonly crossAccountKeys: boolean;
private readonly enableKeyRotation?: boolean;

constructor(scope: Construct, id: string, props: PipelineProps = {}) {
super(scope, id, {
Expand All @@ -330,9 +341,14 @@ export class Pipeline extends PipelineBase {
throw new Error('Only one of artifactBucket and crossRegionReplicationBuckets can be specified!');
}


// @deprecated(v2): switch to default false
this.crossAccountKeys = props.crossAccountKeys ?? true;
this.enableKeyRotation = props.enableKeyRotation;

// Cross account keys must be set for key rotation to be enabled
if (this.enableKeyRotation && !this.crossAccountKeys) {
throw new Error("Setting 'enableKeyRotation' to true also requires 'crossAccountKeys' to be enabled");
}

// If a bucket has been provided, use it - otherwise, create a bucket.
let propsBucket = this.getArtifactBucketFromProps(props);
Expand All @@ -345,6 +361,7 @@ export class Pipeline extends PipelineBase {
// remove the key - there is a grace period of a few days before it's gone for good,
// that should be enough for any emergency access to the bucket artifacts
removalPolicy: RemovalPolicy.DESTROY,
enableKeyRotation: this.enableKeyRotation,
});
// add an alias to make finding the key in the console easier
new kms.Alias(this, 'ArtifactsBucketEncryptionKeyAlias', {
Expand Down Expand Up @@ -573,8 +590,7 @@ export class Pipeline extends PipelineBase {
return crossRegionSupport;
}

private createSupportResourcesForRegion(otherStack: Stack | undefined, actionRegion: string):
CrossRegionSupport {
private createSupportResourcesForRegion(otherStack: Stack | undefined, actionRegion: string): CrossRegionSupport {
// if we have a stack from the resource passed - use that!
if (otherStack) {
// check if the stack doesn't have this magic construct already
Expand All @@ -583,6 +599,7 @@ export class Pipeline extends PipelineBase {
if (!crossRegionSupportConstruct) {
crossRegionSupportConstruct = new CrossRegionSupportConstruct(otherStack, id, {
createKmsKey: this.crossAccountKeys,
enableKeyRotation: this.enableKeyRotation,
});
}

Expand All @@ -609,6 +626,7 @@ export class Pipeline extends PipelineBase {
account: pipelineAccount,
synthesizer: this.getCrossRegionSupportSynthesizer(),
createKmsKey: this.crossAccountKeys,
enableKeyRotation: this.enableKeyRotation,
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,13 @@ export interface CrossRegionSupportConstructProps {
* @default true
*/
readonly createKmsKey?: boolean;

/**
* Enables KMS key rotation for cross-account keys.
*
* @default - false (key rotation is disabled)
*/
readonly enableKeyRotation?: boolean;
}

export class CrossRegionSupportConstruct extends Construct {
Expand All @@ -58,6 +65,7 @@ export class CrossRegionSupportConstruct extends Construct {
if (createKmsKey) {
const encryptionKey = new kms.Key(this, 'CrossRegionCodePipelineReplicationBucketEncryptionKey', {
removalPolicy: cdk.RemovalPolicy.DESTROY,
enableKeyRotation: props.enableKeyRotation,
});
encryptionAlias = new AliasWithShorterGeneratedName(this, 'CrossRegionCodePipelineReplicationBucketEncryptionAlias', {
targetKey: encryptionKey,
Expand Down Expand Up @@ -106,6 +114,13 @@ export interface CrossRegionSupportStackProps {
* @default true
*/
readonly createKmsKey?: boolean;

/**
* Enables KMS key rotation for cross-account keys.
*
* @default - false (key rotation is disabled)
*/
readonly enableKeyRotation?: boolean;
}

/**
Expand All @@ -130,6 +145,7 @@ export class CrossRegionSupportStack extends cdk.Stack {

const crossRegionSupportConstruct = new CrossRegionSupportConstruct(this, 'Default', {
createKmsKey: props.createKmsKey,
enableKeyRotation: props.enableKeyRotation,
});
this.replicationBucket = crossRegionSupportConstruct.replicationBucket;
}
Expand Down
35 changes: 35 additions & 0 deletions packages/@aws-cdk/aws-codepipeline/test/pipeline.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,41 @@ describe('', () => {


});

test('does not allow enabling key rotation if cross account keys have been disabled', () => {
const app = new cdk.App();
const stack = new cdk.Stack(app, 'PipelineStack');

expect(() => {
new codepipeline.Pipeline(stack, 'Pipeline', {
crossAccountKeys: false,
enableKeyRotation: true,
});
}).toThrow("Setting 'enableKeyRotation' to true also requires 'crossAccountKeys' to be enabled");
});

test("enabling key rotation sets 'EnableKeyRotation' to 'true' in the main generated KMS key", () => {
const app = new cdk.App();
const stack = new cdk.Stack(app, 'PipelineStack');
const sourceOutput = new codepipeline.Artifact();
new codepipeline.Pipeline(stack, 'Pipeline', {
enableKeyRotation: true,
stages: [
{
stageName: 'Source',
actions: [new FakeSourceAction({ actionName: 'Source', output: sourceOutput })],
},
{
stageName: 'Build',
actions: [new FakeBuildAction({ actionName: 'Build', input: sourceOutput })],
},
],
});

expect(stack).toHaveResourceLike('AWS::KMS::Key', {
'EnableKeyRotation': true,
});
});
});
});
});
Expand Down
17 changes: 17 additions & 0 deletions packages/@aws-cdk/pipelines/lib/legacy/pipeline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,19 @@ export interface CdkPipelineProps {
readonly crossAccountKeys?: boolean;
// @deprecated(v2): switch to default false


/**
* Enables KMS key rotation for cross-account keys.
*
* Cannot be set if `crossAccountKeys` was set to `false`.
*
* Key rotation costs $1/month when enabled.
*
* @default - false (key rotation is disabled)
*/
readonly enableKeyRotation?: boolean;


/**
* CDK CLI version to use in pipeline
*
Expand Down Expand Up @@ -221,12 +234,16 @@ export class CdkPipeline extends CoreConstruct {
if (props.crossAccountKeys !== undefined) {
throw new Error('Cannot set \'crossAccountKeys\' if an existing CodePipeline is given using \'codePipeline\'');
}
if (props.enableKeyRotation !== undefined) {
throw new Error('Cannot set \'enableKeyRotation\' if an existing CodePipeline is given using \'codePipeline\'');
}

this._pipeline = props.codePipeline;
} else {
this._pipeline = new codepipeline.Pipeline(this, 'Pipeline', {
pipelineName: props.pipelineName,
crossAccountKeys: props.crossAccountKeys,
enableKeyRotation: props.enableKeyRotation,
restartExecutionOnUpdate: true,
});
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import * as codePipeline from '@aws-cdk/aws-codepipeline';
import * as cdk from '@aws-cdk/core';
import * as cdkp from '../../lib';

test('Does not allow setting a pipelineName if an existing CodePipeline is given', () => {
const app = new cdk.App();
const stack = new cdk.Stack(app, 'PipelineStack');
const existingCodePipeline = new codePipeline.Pipeline(stack, 'CustomCodePipeline');

expect(() => {
new cdkp.CdkPipeline(stack, 'CDKPipeline', {
pipelineName: 'CustomPipelineName',
codePipeline: existingCodePipeline,
cloudAssemblyArtifact: new codePipeline.Artifact(),
});
}).toThrow("Cannot set 'pipelineName' if an existing CodePipeline is given using 'codePipeline'");
});

test('Does not allow enabling crossAccountKeys if an existing CodePipeline is given', () => {
const app = new cdk.App();
const stack = new cdk.Stack(app, 'PipelineStack');
const existingCodePipeline = new codePipeline.Pipeline(stack, 'CustomCodePipeline');

expect(() => {
new cdkp.CdkPipeline(stack, 'CDKPipeline', {
crossAccountKeys: true,
codePipeline: existingCodePipeline,
cloudAssemblyArtifact: new codePipeline.Artifact(),
});
}).toThrow("Cannot set 'crossAccountKeys' if an existing CodePipeline is given using 'codePipeline'");
});

test('Does not allow enabling key rotation if an existing CodePipeline is given', () => {
const app = new cdk.App();
const stack = new cdk.Stack(app, 'PipelineStack');
const existingCodePipeline = new codePipeline.Pipeline(stack, 'CustomCodePipeline');

expect(() => {
new cdkp.CdkPipeline(stack, 'CDKPipeline', {
enableKeyRotation: true,
codePipeline: existingCodePipeline,
cloudAssemblyArtifact: new codePipeline.Artifact(),
});
}).toThrow("Cannot set 'enableKeyRotation' if an existing CodePipeline is given using 'codePipeline'");
});