CDK
Introduction
The AWS Cloud Development Kit (CDK) is an open-source software development framework for defining cloud infrastructure in code and provisioning it through AWS CloudFormation. It allows developers to use familiar programming languages like TypeScript, Python, Java, and C# to create and manage AWS resources, streamlining infrastructure development and enabling infrastructure as code (IaC).
AWS CDK uses programming languages for a developer-centric approach, while Terraform is cloud-agnostic with its HCL for multi-cloud environments. CloudFormation is AWS-specific using JSON/YAML, and Pulumi supports multiple clouds using programming languages. Each tool's choice depends on project needs and workflows.
Custom Resources
Custom resources in AWS CDK allow you to extend AWS CloudFormation templates to manage resources not natively supported by CloudFormation. They enable you to create, update, or delete resources dynamically during stack operations using Lambda functions or other AWS services. To build a Custom Resource we need 3 things:
- A Lambda function for
onEvent
handlingcreate
,delete
andupdate
events. - A
Provider
which points the Custom Resource to the lambda. - The
CustomResource
itself with any props you need for your third party resource or otherwise.
You can also provide an additional Lambda function to handle isComplete
.
This is used when the lifecycle operation cannot be completed immediately.
The isComplete
handler will be retried asynchronously after onEvent
until it returns { IsComplete: true }
, or until it times out.
Provider
A provider in AWS CDK is a mechanism that supplies the custom resource functionality by defining how the custom resource interacts with AWS services. It typically involves:
- Lambda Function: Executes the logic for create, update, and delete operations.
- Permissions: Grants the necessary IAM permissions to the Lambda function to perform its actions.
- Service Token: Used by the custom resource to communicate with the provider.
This setup enables you to manage resources that are not natively supported by CloudFormation through AWS CDK custom resources.
Example
Here’s an example of defining a custom resource using a Lambda provider:
import * as cdk from 'aws-cdk-lib';
import * as customResources from 'aws-cdk-lib/custom-resources';
import * as lambda from 'aws-cdk-lib/aws-lambda';
import * as path from 'path';
const stack = new cdk.Stack();
const myFunction = new lambda.Function(stack, 'MyFunction', {
runtime: lambda.Runtime.NODEJS_18_X,
handler: 'index.handler',
code: lambda.Code.fromAsset(path.join(__dirname, 'my-lambda-handler')),
});
const provider = new customResources.Provider(stack, 'MyProvider', {
onEventHandler: myFunction,
});
new cdk.CustomResource(stack, 'MyCustomResource', {
serviceToken: provider.serviceToken,
});
In this example:
- A Lambda function
myFunction
is defined. - A
Provider
is created using the Lambda function. - A
CustomResource
is created using the provider’s service token.
For more details, visit the AWS CDK Custom Resources documentation.
Aspects
Aspects in AWS CDK are used when you need to apply common behaviors or policies to multiple constructs in your CDK application. This can include enforcing security standards, tagging resources, or applying common configurations across resources.
In the below example scenario, imagine that you want to ensure that all S3 buckets in your stack have server-side encryption enabled. Instead of manually setting this property for each bucket, you can use an aspect to enforce this policy across all buckets.
- Aspect Class:
EnsureEncryptionAspect
implementscdk.IAspect
and checks if a node is an S3 bucket. If the bucket does not have encryption enabled, it overrides the property to enable server-side encryption. - Applying the Aspect: The aspect is added to the stack using
cdk.Aspects.of(this).add(new EnsureEncryptionAspect());
, ensuring all buckets in the stack have the encryption policy enforced.
import * as cdk from 'aws-cdk-lib';
import { IConstruct } from 'constructs';
import * as s3 from 'aws-cdk-lib/aws-s3';
class EnsureEncryptionAspect implements cdk.IAspect {
visit(node: IConstruct): void {
if (node instanceof s3.Bucket) {
if (!node.encryption) {
node.addPropertyOverride('BucketEncryption.ServerSideEncryptionConfiguration', [{
ServerSideEncryptionByDefault: { SSEAlgorithm: 'AES256' }
}]);
}
}
}
}
class MyStack extends cdk.Stack {
constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// Define multiple S3 buckets
new s3.Bucket(this, 'MyBucket1');
new s3.Bucket(this, 'MyBucket2');
// Apply the encryption aspect to all constructs in the stack
cdk.Aspects.of(this).add(new EnsureEncryptionAspect());
}
}
const app = new cdk.App();
new MyStack(app, 'MyStack');