Skip to content

Commit

Permalink
add instanceRemovalPolicy and securityGroupRemovalPolicy properties
Browse files Browse the repository at this point in the history
  • Loading branch information
lpizzinidev committed Feb 24, 2024
1 parent d57f095 commit 5d19137
Show file tree
Hide file tree
Showing 5 changed files with 181 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -542,8 +542,8 @@
"VPCPublicSubnet2DefaultRouteB7481BBA",
"VPCPublicSubnet2RouteTableAssociation5A808732"
],
"UpdateReplacePolicy": "Delete",
"DeletionPolicy": "Delete"
"UpdateReplacePolicy": "Retain",
"DeletionPolicy": "Retain"
}
},
"Parameters": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ class TestStack extends cdk.Stack {
parameterGroup: params,
kmsKey,
removalPolicy: cdk.RemovalPolicy.SNAPSHOT,
instanceRemovalPolicy: cdk.RemovalPolicy.RETAIN, // Remember to cleanup after running this test
enablePerformanceInsights: true,
});

Expand Down
42 changes: 41 additions & 1 deletion packages/aws-cdk-lib/aws-docdb/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,47 @@ const cluster = new docdb.DatabaseCluster(this, 'Database', {
```

**Note**: A `RemovalPolicy.DESTROY` removal policy will be applied to the
cluster's instances and security group as they don't support the snapshot
cluster's instances and security group by default as they don't support the snapshot
removal policy.

> Visit [DeletionPolicy](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-attribute-deletionpolicy.html) for more details.
To specify a custom removal policy for the cluster's instances, use the
`instanceRemovalPolicy` property:

```ts
declare const vpc: ec2.Vpc;

const cluster = new docdb.DatabaseCluster(this, 'Database', {
masterUser: {
username: 'myuser',
},
instanceType: ec2.InstanceType.of(ec2.InstanceClass.MEMORY5, ec2.InstanceSize.LARGE),
vpcSubnets: {
subnetType: ec2.SubnetType.PUBLIC,
},
vpc,
removalPolicy: RemovalPolicy.SNAPSHOT,
instanceRemovalPolicy: RemovalPolicy.RETAIN,
});
```

To specify a custom removal policy for the cluster's security group, use the
`securityGroupRemovalPolicy` property:

```ts
declare const vpc: ec2.Vpc;

const cluster = new docdb.DatabaseCluster(this, 'Database', {
masterUser: {
username: 'myuser',
},
instanceType: ec2.InstanceType.of(ec2.InstanceClass.MEMORY5, ec2.InstanceSize.LARGE),
vpcSubnets: {
subnetType: ec2.SubnetType.PUBLIC,
},
vpc,
removalPolicy: RemovalPolicy.SNAPSHOT,
securityGroupRemovalPolicy: RemovalPolicy.RETAIN,
});
```
55 changes: 47 additions & 8 deletions packages/aws-cdk-lib/aws-docdb/lib/cluster.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,11 @@ export interface DatabaseClusterProps {
* removal policy also applies to the implicit security group created for the
* cluster if one is not supplied as a parameter.
*
* When set to `SNAPSHOT`, the removal policy for the instances and the security group
* will default to `DESTROY` as those resources do not support the policy.
*
* Use the `instanceRemovalPolicy` and `securityGroupRemovalPolicy` to change the behavior.
*
* @default - Retain cluster.
*/
readonly removalPolicy?: RemovalPolicy;
Expand Down Expand Up @@ -190,6 +195,28 @@ export interface DatabaseClusterProps {
* @default - false
*/
readonly enablePerformanceInsights?: boolean;

/**
* The removal policy to apply to the cluster's instances.
*
* Cannot be set to `SNAPSHOT`.
*
* @default - `RemovalPolicy.DESTROY` when `removalPolicy` is set to `SNAPSHOT`, `removalPolicy` otherwise.
*
* @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-attribute-deletionpolicy.html
*/
readonly instanceRemovalPolicy?: RemovalPolicy;

/**
* The removal policy to apply to the cluster's security group.
*
* Cannot be set to `SNAPSHOT`.
*
* @default - `RemovalPolicy.DESTROY` when `removalPolicy` is set to `SNAPSHOT`, `removalPolicy` otherwise.
*
* @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-attribute-deletionpolicy.html
*/
readonly securityGroupRemovalPolicy?: RemovalPolicy;
}

/**
Expand Down Expand Up @@ -422,10 +449,7 @@ export class DatabaseCluster extends DatabaseClusterBase {
});
// HACK: Use an escape-hatch to apply a consistent removal policy to the
// security group so we don't get errors when trying to delete the stack.
// AWS::EC2::SecurityGroup does not support snapshot removal policy
// see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-attribute-deletionpolicy.html
const securityGroupRemovalPolicy = !props.removalPolicy || props.removalPolicy !== RemovalPolicy.SNAPSHOT ?
props.removalPolicy : RemovalPolicy.DESTROY;
const securityGroupRemovalPolicy = this.getSecurityGroupRemovalPolicy(props);
(securityGroup.node.defaultChild as CfnResource).applyRemovalPolicy(securityGroupRemovalPolicy, {
applyToUpdateReplacePolicy: true,
});
Expand Down Expand Up @@ -508,10 +532,7 @@ export class DatabaseCluster extends DatabaseClusterBase {
throw new Error('At least one instance is required');
}

// AWS::DocDB::DBInstance does not support SNAPSHOT removal policy
// see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-attribute-deletionpolicy.html
const instanceRemovalPolicy = !props.removalPolicy || props.removalPolicy !== RemovalPolicy.SNAPSHOT ?
props.removalPolicy : RemovalPolicy.DESTROY;
const instanceRemovalPolicy = this.getInstanceRemovalPolicy(props);

for (let i = 0; i < instanceCount; i++) {
const instanceIndex = i + 1;
Expand Down Expand Up @@ -561,6 +582,24 @@ export class DatabaseCluster extends DatabaseClusterBase {
}
}

private getInstanceRemovalPolicy(props: DatabaseClusterProps) {
if (props.instanceRemovalPolicy === RemovalPolicy.SNAPSHOT) {
throw new Error('AWS::DocDB::DBInstance does not support the SNAPSHOT removal policy');
}
if (props.instanceRemovalPolicy) return props.instanceRemovalPolicy;
return !props.removalPolicy || props.removalPolicy !== RemovalPolicy.SNAPSHOT ?
props.removalPolicy : RemovalPolicy.DESTROY;
}

private getSecurityGroupRemovalPolicy(props: DatabaseClusterProps) {
if (props.securityGroupRemovalPolicy === RemovalPolicy.SNAPSHOT) {
throw new Error('AWS::EC2::SecurityGroup does not support the SNAPSHOT removal policy');
}
if (props.securityGroupRemovalPolicy) return props.securityGroupRemovalPolicy;
return !props.removalPolicy || props.removalPolicy !== RemovalPolicy.SNAPSHOT ?
props.removalPolicy : RemovalPolicy.DESTROY;
}

/**
* Adds the single user rotation of the master password to this cluster.
*
Expand Down
90 changes: 90 additions & 0 deletions packages/aws-cdk-lib/aws-docdb/test/cluster.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -998,6 +998,96 @@ describe('DatabaseCluster', () => {
UpdateReplacePolicy: 'Delete',
});
});

test('can specify instances removal policy', () => {
// GIVEN
const stack = testStack();
const vpc = new ec2.Vpc(stack, 'VPC');

// WHEN
new DatabaseCluster(stack, 'Database', {
instances: 1,
masterUser: {
username: 'admin',
password: cdk.SecretValue.unsafePlainText('tooshort'),
},
instanceType: ec2.InstanceType.of(ec2.InstanceClass.BURSTABLE2, ec2.InstanceSize.SMALL),
vpc,
instanceRemovalPolicy: cdk.RemovalPolicy.DESTROY,
});

// THEN
Template.fromStack(stack).hasResource('AWS::DocDB::DBInstance', {
DeletionPolicy: 'Delete',
UpdateReplacePolicy: 'Delete',
});
});

test('can specify security group removal policy', () => {
// GIVEN
const stack = testStack();
const vpc = new ec2.Vpc(stack, 'VPC');

// WHEN
new DatabaseCluster(stack, 'Database', {
instances: 1,
masterUser: {
username: 'admin',
password: cdk.SecretValue.unsafePlainText('tooshort'),
},
instanceType: ec2.InstanceType.of(ec2.InstanceClass.BURSTABLE2, ec2.InstanceSize.SMALL),
vpc,
securityGroupRemovalPolicy: cdk.RemovalPolicy.DESTROY,
});

// THEN
Template.fromStack(stack).hasResource('AWS::EC2::SecurityGroup', {
DeletionPolicy: 'Delete',
UpdateReplacePolicy: 'Delete',
});
});

test('instances removal policy cannot be set to snapshot', () => {
// GIVEN
const stack = testStack();
const vpc = new ec2.Vpc(stack, 'VPC');

// WHEN
// THEN
expect(() => {
new DatabaseCluster(stack, 'Database', {
instances: 1,
masterUser: {
username: 'admin',
password: cdk.SecretValue.unsafePlainText('tooshort'),
},
instanceType: ec2.InstanceType.of(ec2.InstanceClass.BURSTABLE2, ec2.InstanceSize.SMALL),
vpc,
instanceRemovalPolicy: cdk.RemovalPolicy.SNAPSHOT,
});
}).toThrow(/AWS::DocDB::DBInstance does not support the SNAPSHOT removal policy/);
});

test('security group removal policy cannot be set to snapshot', () => {
// GIVEN
const stack = testStack();
const vpc = new ec2.Vpc(stack, 'VPC');

// WHEN
// THEN
expect(() => {
new DatabaseCluster(stack, 'Database', {
instances: 1,
masterUser: {
username: 'admin',
password: cdk.SecretValue.unsafePlainText('tooshort'),
},
instanceType: ec2.InstanceType.of(ec2.InstanceClass.BURSTABLE2, ec2.InstanceSize.SMALL),
vpc,
securityGroupRemovalPolicy: cdk.RemovalPolicy.SNAPSHOT,
});
}).toThrow(/AWS::EC2::SecurityGroup does not support the SNAPSHOT removal policy/);
});
});

function testStack() {
Expand Down

0 comments on commit 5d19137

Please sign in to comment.