when working with AWS CDK, you normally use L2/L3 constructs for convenience, defaults, and strong typing. But sometimes you need to dive into L1 (Cfn) constructs to access low-level features not exposed in higher-level constructs. That's where the familiar "escape hatches" come in - but you might be missing out on the lesser-known counterpart: "un‑escape hatches."
Construct Levels: A Brief Summary
To understand escape hatches and un‑escape hatches, it is important to know how AWS CDK structures its abstractions. CDK organizes constructs into three levels:
- Level 1 (L1) - These are low-level constructs that map directly to CloudFormation resources. Every resource in CloudFormation has a corresponding
CfnXxx
class in CDK. You get full control, but little to no conveniences.
Example:s3.CfnBucket
- Level 2 (L2) - These constructs are opinionated wrappers around L1 resources. They offer sensible defaults, validation, and helper methods like
.grantRead()
, etc.
Example:lambda.Function
- Level 3 (L3) - These are preconfigured patterns composed of multiple L2 constructs. They aim to solve higher-level use cases with minimal setup.
Example:ecs_patterns.ApplicationLoadBalancedFargateService
What Are Escape Hatches?
An escape hatch lets you break out of the CDK abstraction to tweak the underlying CloudFormation resource. For example:
const bucket = new s3.Bucket(this, 'MyBucket');
const cfn = bucket.node.defaultChild as s3.CfnBucket;
cfn.analyticsConfigurations = [ ... ];
You access node.defaultChild
, cast it to the L1 construct, and tweak whatever you need.
But What About Un‑Escape Hatches?
An un‑escape hatch goes in the opposite direction. Suppose you already have an L1 construct - maybe because the feature isn't available at L2 yet. You still want your code to use L2 convenience methods (e.g,.grantRead()
).
That's where L2.fromCfnXxx()
comes in. It wraps an existing Cfn resource in a brand-new L2 instance.
This gives you the best of both worlds: raw L1 control plus the L2 API cleaner integration. This is the same example from AWS docs AWS-Documentation.
How to Apply It
- Create or obtain your L1 construct (e.g.,
CfnBucket
,CfnFunction
, etc.). - Call the corresponding static un‑escape constructor:
Bucket.fromCfnBucket()
- Use the newly created L2 object in your CDK code as if you had originally used the high-level construct.
Example:
// 1. Create L1 resource manually
const cfnBucket = new s3.CfnBucket(this, 'RawBucket', { /* ... */ });
// 2. Wrap it in L2
const bucket = s3.Bucket.fromCfnBucket(cfnBucket);
// 3. Now use L2 methods
bucket.grantRead(myRole);
TL;DR
- Escape hatches: escape from L2 to L1 to tweak low-level config
- Un‑escape hatches: wrap an L1 in L2 to access high-level convenience tools