CDK: how to customize 3rd-party L3 constructs


If you’re using CDK, you should use L3 constructs to encapsulate common patterns and best practices in your architecture. The ability to create reusable, higher-level components is where CDK shines over other IaC tools such as SAM and Serverless Framework.

However, as Matt Bonig pointed out [1], sharing these reusable L3 constructs across organizations is difficult. Because every organization has its own quirks and requirements. As a construct author, it’s impossible to predict all these variations ahead of time.

As a consumer of 3rd-party L3 constructs, you often face the opposite problem - how to customize the resources created by these L3 constructs.

For example, you might find a third-party L3 construct that does most of what you want, but its Lambda functions are not configured to use a VPC. What do you do then?

You can clone the code and create your own construct, but that adds maintenance overhead to keep pace with changes to the original source code.

A better approach is to customize the resources created by the L3 construct with an Aspect [2].

Introduction to CDK Aspects

CDK puts all of your resources into a construct tree. The CDK app is at the root of the tree, followed by the stacks and their constructs.

You can traverse this tree using the methods on the Node class.

However, CDK has a built-in mechanism for traversing this tree through Aspects. To write your own Aspect, you create a class that implements this visit function:

visit(node: IConstruct): void

When an Aspect is applied to a node in the construct tree, CDK will traverse the node and its children and call your visit function on each.

Aspects.of(myConstruct).add(new MyAspect(...))

Customizing a 3rd-party L3 construct

For the aforementioned example, you can create an Aspect to customize the VPC settings on all Lambda functions it comes across.

const { aws_lambda } = require('aws-cdk-lib')

class LambdaVpcAspect {
  constructor(sgIds, subnetIds) {
    this.sgIds = sgIds
    this.subnetIds = subnetIds
  }

  visit(node) {
    if (node instanceof aws_lambda.Function) {
      const child = node.node.defaultChild
      child.vpcConfig = {
        securityGroupIds: this.sgIds,
        subnetIds: this.subnetIds
      }
    }
  }
}

module.exports = {
  LambdaVpcAspect,
}

When this Aspect is applied to an instance of the 3rd-party L3 construct:

Aspects.of(3rdPartyConstruct).add(new LambdaVpcAspect(mySgIds, mySubnetIds))

it will modify the vpcConfig of all the Lambda functions that are created by the L3 construct.

That’s it! An easy and effective way to customize 3rd-party L3 constructs in CDK.

Links

[1] AWS CDK: problem with creating reusable L3 constructs

[2] Official documentation on CDK Aspects

Master Serverless

Join 11K readers and level up you AWS game with just 5 mins a week. Every Monday, I share practical tips, tutorials and best practices for building serverless architectures on AWS.

Read more from Master Serverless

Software systems are getting bigger and more complex. And we are constantly looking for ways to test code in production without risking user experience. Canary deployments is a popular mechanism for rolling out changes incrementally, allowing us to limit the blast radius in case something goes wrong. However, they’re not without limitations. Canary deployments essentially sacrifice a small portion of users for the greater good. But what if you want to gain insights without impacting any real...

In security and access control, authentication and authorization are two distinct yet interconnected concepts. Authentication is the process of confirming the identity of a user or system, while authorization defines the actions that the authenticated user is permitted to perform within your system. Although API Gateway integrates directly with Cognito, it lacks built-in support for fine-grained authorization. In a previous article, we looked at implementing fine-grained authorization using a...

A common narrative is that one should always use access tokens to call your APIs, while ID tokens are strictly for identifying users. Some of it has come from this article by Auth0 [1], which makes a strong statement about using ID tokens: However, things are usually more nuanced. In some cases, using ID tokens instead of access tokens is both acceptable and pragmatic. Cognito User Pools might be one of these cases. Cost of using access tokens The common practice amongst Cognito users is to...