Skip to content

Commit 99ab46d

Browse files
jogoldrix0rrr
authored andcommitted
feat(rds): cluster retention, reference KMS key by object (#2063)
Add control over DeletionPolicy and UpdateReplacePolicy, both defaulting to Retain to avoid data loss. Reference encryption key reference with key interface. BREAKING CHANGE: Replaced `kmsKeyArn: string` by `kmsKey: kms.IEncryptionKey` in `DatabaseClusterProps`
1 parent 1e3d41e commit 99ab46d

File tree

6 files changed

+99
-21
lines changed

6 files changed

+99
-21
lines changed

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ When the master password is generated and stored in AWS Secrets Manager, it can
6868

6969
Rotation of the master password is also supported for an existing cluster:
7070
```ts
71-
new rds.RotationSingleUser(stack, 'Rotation', {
71+
new RotationSingleUser(stack, 'Rotation', {
7272
secret: importedSecret,
7373
engine: DatabaseEngine.Oracle,
7474
target: importedCluster,

packages/@aws-cdk/aws-rds/lib/cluster.ts

+30-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import ec2 = require('@aws-cdk/aws-ec2');
2+
import kms = require('@aws-cdk/aws-kms');
23
import secretsmanager = require('@aws-cdk/aws-secretsmanager');
34
import cdk = require('@aws-cdk/cdk');
45
import { IClusterParameterGroup } from './cluster-parameter-group';
@@ -72,9 +73,19 @@ export interface DatabaseClusterProps {
7273
defaultDatabaseName?: string;
7374

7475
/**
75-
* ARN of KMS key if you want to enable storage encryption
76+
* Whether to enable storage encryption
77+
*
78+
* @default false
79+
*/
80+
storageEncrypted?: boolean
81+
82+
/**
83+
* The KMS key for storage encryption. If specified `storageEncrypted`
84+
* will be set to `true`.
85+
*
86+
* @default default master key
7687
*/
77-
kmsKeyArn?: string;
88+
kmsKey?: kms.IEncryptionKey;
7889

7990
/**
8091
* A daily time range in 24-hours UTC format in which backups preferably execute.
@@ -91,6 +102,14 @@ export interface DatabaseClusterProps {
91102
* @default No parameter group
92103
*/
93104
parameterGroup?: IClusterParameterGroup;
105+
106+
/**
107+
* The CloudFormation policy to apply when the cluster and its instances
108+
* are removed from the stack or replaced during an update.
109+
*
110+
* @default Retain
111+
*/
112+
deleteReplacePolicy?: cdk.DeletionPolicy
94113
}
95114

96115
/**
@@ -261,10 +280,14 @@ export class DatabaseCluster extends DatabaseClusterBase implements IDatabaseClu
261280
preferredMaintenanceWindow: props.preferredMaintenanceWindow,
262281
databaseName: props.defaultDatabaseName,
263282
// Encryption
264-
kmsKeyId: props.kmsKeyArn,
265-
storageEncrypted: props.kmsKeyArn ? true : false,
283+
kmsKeyId: props.kmsKey && props.kmsKey.keyArn,
284+
storageEncrypted: props.kmsKey ? true : props.storageEncrypted
266285
});
267286

287+
const deleteReplacePolicy = props.deleteReplacePolicy || cdk.DeletionPolicy.Retain;
288+
cluster.options.deletionPolicy = deleteReplacePolicy;
289+
cluster.options.updateReplacePolicy = deleteReplacePolicy;
290+
268291
this.clusterIdentifier = cluster.ref;
269292
this.clusterEndpoint = new Endpoint(cluster.dbClusterEndpointAddress, cluster.dbClusterEndpointPort);
270293
this.readerEndpoint = new Endpoint(cluster.dbClusterReadEndpointAddress, cluster.dbClusterEndpointPort);
@@ -303,6 +326,9 @@ export class DatabaseCluster extends DatabaseClusterBase implements IDatabaseClu
303326
dbSubnetGroupName: subnetGroup.ref,
304327
});
305328

329+
instance.options.deletionPolicy = deleteReplacePolicy;
330+
instance.options.updateReplacePolicy = deleteReplacePolicy;
331+
306332
// We must have a dependency on the NAT gateway provider here to create
307333
// things in the right order.
308334
instance.node.addDependency(internetConnected);

packages/@aws-cdk/aws-rds/test/integ.cluster-rotation.lit.expected.json

+9-4
Original file line numberDiff line numberDiff line change
@@ -650,7 +650,6 @@
650650
]
651651
]
652652
},
653-
"StorageEncrypted": false,
654653
"VpcSecurityGroupIds": [
655654
{
656655
"Fn::GetAtt": [
@@ -659,7 +658,9 @@
659658
]
660659
}
661660
]
662-
}
661+
},
662+
"DeletionPolicy": "Retain",
663+
"UpdateReplacePolicy": "Retain"
663664
},
664665
"DatabaseInstance1844F58FD": {
665666
"Type": "AWS::RDS::DBInstance",
@@ -677,7 +678,9 @@
677678
"VPCPrivateSubnet1DefaultRouteAE1D6490",
678679
"VPCPrivateSubnet2DefaultRouteF4F5CFD2",
679680
"VPCPrivateSubnet3DefaultRoute27F311AE"
680-
]
681+
],
682+
"DeletionPolicy": "Retain",
683+
"UpdateReplacePolicy": "Retain"
681684
},
682685
"DatabaseInstance2AA380DEE": {
683686
"Type": "AWS::RDS::DBInstance",
@@ -695,7 +698,9 @@
695698
"VPCPrivateSubnet1DefaultRouteAE1D6490",
696699
"VPCPrivateSubnet2DefaultRouteF4F5CFD2",
697700
"VPCPrivateSubnet3DefaultRoute27F311AE"
698-
]
701+
],
702+
"DeletionPolicy": "Retain",
703+
"UpdateReplacePolicy": "Retain"
699704
},
700705
"DatabaseRotationSecurityGroup17736B63": {
701706
"Type": "AWS::EC2::SecurityGroup",

packages/@aws-cdk/aws-rds/test/integ.cluster.expected.json

+10-4
Original file line numberDiff line numberDiff line change
@@ -484,7 +484,9 @@
484484
]
485485
}
486486
]
487-
}
487+
},
488+
"DeletionPolicy": "Retain",
489+
"UpdateReplacePolicy": "Retain"
488490
},
489491
"DatabaseInstance1844F58FD": {
490492
"Type": "AWS::RDS::DBInstance",
@@ -502,7 +504,9 @@
502504
"DependsOn": [
503505
"VPCPublicSubnet1DefaultRoute91CEF279",
504506
"VPCPublicSubnet2DefaultRouteB7481BBA"
505-
]
507+
],
508+
"DeletionPolicy": "Retain",
509+
"UpdateReplacePolicy": "Retain"
506510
},
507511
"DatabaseInstance2AA380DEE": {
508512
"Type": "AWS::RDS::DBInstance",
@@ -520,7 +524,9 @@
520524
"DependsOn": [
521525
"VPCPublicSubnet1DefaultRoute91CEF279",
522526
"VPCPublicSubnet2DefaultRouteB7481BBA"
523-
]
527+
],
528+
"DeletionPolicy": "Retain",
529+
"UpdateReplacePolicy": "Retain"
524530
}
525531
}
526-
}
532+
}

packages/@aws-cdk/aws-rds/test/integ.cluster.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ const cluster = new DatabaseCluster(stack, 'Database', {
2828
vpc
2929
},
3030
parameterGroup: params,
31-
kmsKeyArn: kmsKey.keyArn,
31+
kmsKey,
3232
});
3333

3434
cluster.connections.allowDefaultPortFromAnyIpv4('Open to the world');

packages/@aws-cdk/aws-rds/test/test.cluster.ts

+48-7
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
import { expect, haveResource } from '@aws-cdk/assert';
1+
import { expect, haveResource, ResourcePart } from '@aws-cdk/assert';
22
import ec2 = require('@aws-cdk/aws-ec2');
3+
import kms = require('@aws-cdk/aws-kms');
34
import cdk = require('@aws-cdk/cdk');
45
import { Test } from 'nodeunit';
56
import { ClusterParameterGroup, DatabaseCluster, DatabaseClusterEngine } from '../lib';
@@ -25,12 +26,21 @@ export = {
2526

2627
// THEN
2728
expect(stack).to(haveResource('AWS::RDS::DBCluster', {
28-
Engine: "aurora",
29-
DBSubnetGroupName: { Ref: "DatabaseSubnets56F17B9A" },
30-
MasterUsername: "admin",
31-
MasterUserPassword: "tooshort",
32-
VpcSecurityGroupIds: [ {"Fn::GetAtt": ["DatabaseSecurityGroup5C91FDCB", "GroupId"]}]
33-
}));
29+
Properties: {
30+
Engine: "aurora",
31+
DBSubnetGroupName: { Ref: "DatabaseSubnets56F17B9A" },
32+
MasterUsername: "admin",
33+
MasterUserPassword: "tooshort",
34+
VpcSecurityGroupIds: [ {"Fn::GetAtt": ["DatabaseSecurityGroup5C91FDCB", "GroupId"]}]
35+
},
36+
DeletionPolicy: 'Retain',
37+
UpdateReplacePolicy: 'Retain'
38+
}, ResourcePart.CompleteDefinition));
39+
40+
expect(stack).to(haveResource('AWS::RDS::DBInstance', {
41+
DeletionPolicy: 'Retain',
42+
UpdateReplacePolicy: 'Retain'
43+
}, ResourcePart.CompleteDefinition));
3444

3545
test.done();
3646
},
@@ -232,6 +242,37 @@ export = {
232242
}
233243
}));
234244

245+
test.done();
246+
},
247+
248+
'create an encrypted cluster with custom KMS key'(test: Test) {
249+
// GIVEN
250+
const stack = testStack();
251+
const vpc = new ec2.VpcNetwork(stack, 'VPC');
252+
253+
// WHEN
254+
new DatabaseCluster(stack, 'Database', {
255+
engine: DatabaseClusterEngine.AuroraMysql,
256+
masterUser: {
257+
username: 'admin'
258+
},
259+
instanceProps: {
260+
instanceType: new ec2.InstanceTypePair(ec2.InstanceClass.Burstable2, ec2.InstanceSize.Small),
261+
vpc
262+
},
263+
kmsKey: new kms.EncryptionKey(stack, 'Key')
264+
});
265+
266+
// THEN
267+
expect(stack).to(haveResource('AWS::RDS::DBCluster', {
268+
KmsKeyId: {
269+
'Fn::GetAtt': [
270+
'Key961B73FD',
271+
'Arn'
272+
]
273+
}
274+
}));
275+
235276
test.done();
236277
}
237278
};

0 commit comments

Comments
 (0)