From 9f451bda33ae83e41e395799d9bb3f07ce4e100d Mon Sep 17 00:00:00 2001 From: Hassan Azhar <57677979+hassanazharkhan@users.noreply.github.com> Date: Mon, 4 Jan 2021 17:19:29 +0500 Subject: [PATCH] feat(aws-kms): support waiting period (#12224) Closes [#12218](https://github.com/aws/aws-cdk/issues/12218) ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/@aws-cdk/aws-kms/README.md | 11 +++++++++ packages/@aws-cdk/aws-kms/lib/key.ts | 27 +++++++++++++++++++++- packages/@aws-cdk/aws-kms/test/key.test.ts | 8 +++++++ 3 files changed, 45 insertions(+), 1 deletion(-) diff --git a/packages/@aws-cdk/aws-kms/README.md b/packages/@aws-cdk/aws-kms/README.md index ac7c9c2571a1e..495f42a16ec25 100644 --- a/packages/@aws-cdk/aws-kms/README.md +++ b/packages/@aws-cdk/aws-kms/README.md @@ -21,6 +21,17 @@ new kms.Key(this, 'MyKey', { }); ``` +Define a KMS key with waiting period: + +Specifies the number of days in the waiting period before AWS KMS deletes a CMK that has been removed from a CloudFormation stack. + +```ts +const key = new kms.Key(this, 'MyKey', { + pendingWindow: 10 // Default to 30 Days +}); +``` + + Add a couple of aliases: ```ts diff --git a/packages/@aws-cdk/aws-kms/lib/key.ts b/packages/@aws-cdk/aws-kms/lib/key.ts index 82098129e27a2..d8de804e61a12 100644 --- a/packages/@aws-cdk/aws-kms/lib/key.ts +++ b/packages/@aws-cdk/aws-kms/lib/key.ts @@ -1,5 +1,5 @@ import * as iam from '@aws-cdk/aws-iam'; -import { FeatureFlags, IResource, RemovalPolicy, Resource, Stack } from '@aws-cdk/core'; +import { FeatureFlags, IResource, RemovalPolicy, Resource, Stack, Duration } from '@aws-cdk/core'; import * as cxapi from '@aws-cdk/cx-api'; import { IConstruct, Construct } from 'constructs'; import { Alias } from './alias'; @@ -330,6 +330,22 @@ export interface KeyProps { * @deprecated redundant with the '@aws-cdk/aws-kms:defaultKeyPolicies' feature flag */ readonly trustAccountIdentities?: boolean; + + /** + * Specifies the number of days in the waiting period before + * AWS KMS deletes a CMK that has been removed from a CloudFormation stack. + * + * When you remove a customer master key (CMK) from a CloudFormation stack, AWS KMS schedules the CMK for deletion + * and starts the mandatory waiting period. The PendingWindowInDays property determines the length of waiting period. + * During the waiting period, the key state of CMK is Pending Deletion, which prevents the CMK from being used in + * cryptographic operations. When the waiting period expires, AWS KMS permanently deletes the CMK. + * + * Enter a value between 7 and 30 days. + * + * @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-kms-key.html#cfn-kms-key-pendingwindowindays + * @default - 30 days + */ + readonly pendingWindow?: Duration; } /** @@ -400,11 +416,20 @@ export class Key extends KeyBase { } } + let pendingWindowInDays; + if (props.pendingWindow) { + pendingWindowInDays = props.pendingWindow.toDays(); + if (pendingWindowInDays < 7 || pendingWindowInDays > 30) { + throw new Error(`'pendingWindow' value must between 7 and 30 days. Received: ${pendingWindowInDays}`); + } + } + const resource = new CfnKey(this, 'Resource', { description: props.description, enableKeyRotation: props.enableKeyRotation, enabled: props.enabled, keyPolicy: this.policy, + pendingWindowInDays: pendingWindowInDays, }); this.keyArn = resource.attrArn; diff --git a/packages/@aws-cdk/aws-kms/test/key.test.ts b/packages/@aws-cdk/aws-kms/test/key.test.ts index 6560a31381691..1bc64d63a13d4 100644 --- a/packages/@aws-cdk/aws-kms/test/key.test.ts +++ b/packages/@aws-cdk/aws-kms/test/key.test.ts @@ -283,6 +283,7 @@ test('key with some options', () => { const key = new kms.Key(stack, 'MyKey', { enableKeyRotation: true, enabled: false, + pendingWindow: cdk.Duration.days(7), }); cdk.Tags.of(key).add('tag1', 'value1'); @@ -292,6 +293,7 @@ test('key with some options', () => { expect(stack).toHaveResourceLike('AWS::KMS::Key', { Enabled: false, EnableKeyRotation: true, + PendingWindowInDays: 7, Tags: [ { Key: 'tag1', @@ -309,6 +311,11 @@ test('key with some options', () => { }); }); +test('setting pendingWindow value to not in allowed range will throw', () => { + expect(() => new kms.Key(stack, 'MyKey', { enableKeyRotation: true, pendingWindow: cdk.Duration.days(6) })) + .toThrow('\'pendingWindow\' value must between 7 and 30 days. Received: 6'); +}); + test('setting trustAccountIdentities to false will throw (when the defaultKeyPolicies feature flag is enabled)', () => { expect(() => new kms.Key(stack, 'MyKey', { trustAccountIdentities: false })) .toThrow('`trustAccountIdentities` cannot be false if the @aws-cdk/aws-kms:defaultKeyPolicies feature flag is set'); @@ -435,6 +442,7 @@ describe('imported keys', () => { }); describe('addToResourcePolicy allowNoOp and there is no policy', () => { + // eslint-disable-next-line jest/expect-expect test('succeed if set to true (default)', () => { const key = kms.Key.fromKeyArn(stack, 'Imported', 'arn:aws:kms:us-east-1:123456789012:key/12345678-1234-1234-1234-123456789012');