Create your own CDK L2 Construct
The article provides a guide for creating a consistent and best-practice "skeleton" Level 2 (L2) construct in the AWS Cloud Development Kit (CDK). It outlines the minimal requirements for an L2 construct, including supporting only mandatory properties and following the official CDK Design Guidelines. The article also includes sample code and the importance of integration and unit tests.
Pahud Hsieh
Amazon Employee
Published Sep 15, 2024
In the previous article, I discussed the concept of a "skeleton L2 construct" - a minimal, deployable AWS CDK Level 2 (L2) construct that adheres to the necessary requirements. However, the key question is: what are the minimal requirements for an L2 construct, and what is the pattern I should learn to create one?
This opinionated blog post outlines the concept of a "skeleton" L2 construct. It aims to provide a general guideline to help you build your own L2 constructs, as well as a knowledge base if you plan to leverage generative AI tools like the Amazon Q Developer /dev agent or Amazon Bedrock Agent to generate L2 constructs.
Features of a Skeleton L2
Let's assume we need an L2 construct for a resource called "Foo":
- The
Foo
construct should be a minimal implementation of its corresponding L1 Resource (CfnFoo
). - It should work simply by instantiating it with
new Foo(scope, id, props);
. - It should only support the mandatory properties.
- It must follow the official CDK Design Guidelines.
Here's a summary of the key points from the AWS Construct Library Design Guidelines document:
1. Introduction:
- The document provides guidelines for designing APIs in the AWS Construct Library to ensure consistency across the entire AWS surface area.
- The AWS Construct Library includes low-level CFN Resources (L1), higher-level L2 constructs, L2.5 constructs, and even higher-level patterns.
2. API Design:
- Modules are organized based on the AWS service namespace.
- Construct classes must extend the cdk.Resource or cdk.Construct class directly.
- Construct interfaces are used to reference owned and unowned constructs.
- Construct props should be a struct (FooProps) and expose the full set of service capabilities.
- Props should use strong types, provide sensible defaults, be flat, and have concise naming.
- Constructs should expose all resource attributes as readonly properties.
- Configuration APIs should be marked with the @config tag.
- "From" static methods allow importing unowned resources.
- Grants, metrics, events, and connections should be exposed on the construct interface.
- Integrations should be modeled using interfaces implemented by classes.
3. Implementation Guidelines:
- Construct IDs should be treated as part of the external contract.
- Errors should be reported through exceptions, validators, or annotations.
- Tokens should be used for late-bound values, and Lazys should not mutate the construct tree.
4. Documentation, Testing, and Versioning:
- Inline documentation, READMEs, unit tests, and integration tests are required.
- Semantic versioning should be followed, with changes to construct IDs or scope hierarchy considered breaking.
- Naming and coding style guidelines are provided.
The basic L2 pattern looks like:
As part of developing your L2 construct, you should include at least one integration test and one unit test. The integration test should deploy the construct and generate snapshots using the
IntegTest
class from the @aws-cdk/integ-tests-alpha
module.If L1 resources require a
roleArn
prop, regardless of whether it is mandatory or not, the L2 construct should offer a role: iam.IRole
prop instead. This allows the construct to generate a scoped IAM role for the L1 resource using the following pattern:This ensures that construct users always have a scoped IAM role without having to create one themselves, which can increase the potential risks.
(to be continued)
(to be continued)
Any opinions in this post are those of the individual author and may not reflect the opinions of AWS.