Borislav Hadzhiev
Reading timeยท4 min
Photo from Unsplash
If we need to customize a resource property that is not exposed by the higher level Construct we're using, then we need to use an escape hatch.
In order to use an escape hatch in CDK, we have to get access to the Level 1
Cfn Resource. We can do that by accessing the node.defaultChild
property on
the construct.
A common case where we have to use an escape hatch because of functionality that's not yet implemented in the higher level construct is when modifying the email configuration of a cognito user pool:
// ๐ define user pool using Level 2 construct const userPool = new cognito.UserPool(this, 'userpool', { // ...props }); // ๐ access the node.defaultChild property (escape hatch) const cfnUserPool = userPool.node.defaultChild as cognito.CfnUserPool; // ๐ work with the CloudFormation resource cfnUserPool.emailConfiguration = { emailSendingAccount: 'DEVELOPER', replyToEmailAddress: 'john@example.com', sourceArn: `arn:aws:ses:us-east-1:123456789:identity/john@example.com`, };
In the above code snippet:
We define our User Pool using a Level 2 construct. Unfortunately, at the time of writing the Level 2 construct does not support updating the email configuration, so we have to use an escape hatch.
We access the node.defaultChild
property on the Level 2 User Pool
construct and cast it as the CfnUserPool
resource.
We can now set the emailConfiguration
property on the Level 1
CfnUserPool construct.
Let's look at another example, this time we'll use a Dynamodb table:
// ๐ table definition using Level 2 construct const table = new dynamodb.Table(this, 'my-table', { partitionKey: {name: 'todoId', type: dynamodb.AttributeType.NUMBER}, }); // ๐ get access to the Level 1 Cfn resource (escape hatch) const cfnTable = table.node.defaultChild as dynamodb.CfnTable; // ๐ update properties on the Level 1 Cfn resource cfnTable.billingMode = dynamodb.BillingMode.PROVISIONED; cfnTable.provisionedThroughput = { readCapacityUnits: 1, writeCapacityUnits: 1, }; cfnTable.applyRemovalPolicy(cdk.RemovalPolicy.DESTROY); cfnTable.tags.setTag('env', 'dev');
In our code snippet we:
Define the Dynamodb table using the Level 2 Table construct.
We get the Level 1 Cfn resource by accessing the node.defaultChild
property
on the Level 2 construct. Note that we also cast the Cfn resource as the
appropriate type, so we can then access any properties and methods available
on the
CfnTable construct
Now that we have access to the CloudFormation resource we are able to set any properties and access any methods, made available in CloudFormation
When working with constructs in CDK we aim to use higher levels of abstraction.
Higher levels of abstraction allow us to take advantage of some of the sane defaults and glue methods for service to service interactions put in place by the CDK team in the CDK constructs library.
If we need to customize a resource property that is not exposed by the Construct we're using, then we need to use an escape hatch.
We only use escape hatches where we need to fill a gap and interact with configuration properties that are not accessible on a higher level construct.
Our CDK code eventually gets compiled to CloudFormation, which is very customizable, however, it also is quite verbose and difficult to manage at scale.
Escape hatches give us the ability to use the best of both worlds.
We can write our code using higher levels of abstraction with Level 2 and Level 3 constructs, however, we are also able to use an escape hatch and access the CloudFormation (Level 1) resource and update its properties.
Higher level constructs are just wrappers around the Level 1 Cfn constructs, which means that in order to use an escape hatch we have to find a way to access the Cfn construct.
Cfn constructs are named in the form of Cfn
+ the name of the resource, for
example:
We can access the Cfn resource of a higher level construct by using
construct.node.defaultChild
.
After we get access to a Cfn resource we have to cast its type and then we are able to set and access any of the properties and methods made available in CloudFormation.
The ability to use escape hatches enables us to write our code using higher level of abstractions and only revert back to CloudFormation resources in the case a functionality is not exposed to the user of the construct.