Note: This has been updated here
Building off of the previous example of building CDK Custom Resources Constructs, in this post, we will look at the steps required to convert this from a JavaScript Lambda to a Typescript Lambda using some of the magic of projen.
CDK Construct Code
. ├── .eslintrc.json ├── .gitattributes ├── .gitignore ├── .jsii ├── .mergify.yml ├── .npmignore ├── .projenrc.js ├── API.md ├── LICENSE ├── README.md ├── package.json ├── src │ ├── custom-resource-function.ts │ ├── custom-resource.lambda.ts │ └── index.ts ├── test │ └── customResource.test.ts ├── tsconfig.dev.json ├── tsconfig.json └── yarn.lock
Lambda Function
In this example, we will be using projen AWS Lambda Functions to automatically create the Lambda function that will be used by the Custom Resource. In order to create this function, we will create custom-resource.lambda.ts
in the src
directory. This will include the Lambda code to be executed during the CDK deployment.
After creating this file, running npx projen
will create the custom-resource-function.ts
file in the src
directory. This is what we will be using in the index.ts
file as the Lambda function for the Custom Resource.
After importing the function:
import { CustomResourceFunction } from './custom-resource-function';
We can reference it here:
const customResourceLambda = new CustomResourceFunction( this, 'customResourceLambda', { role: customResourceRole, architecture: Architecture.ARM_64, timeout: Duration.seconds(60), }, );
To define the runtime of the Lambda function, in the .projenrc
file:
lambdaOptions: { runtime: awscdk.LambdaRuntime.NODEJS_18_X, bundlingOptions: { externals: ['aws-sdk'], sourcemap: true, }, },
Be sure to check out projen documentation for details on how to use this. https://projen.io/awscdk.html#aws-lambda-functions
NOTE: NODEJS_18_X is not currently available and this will default to NODEJS_14_X for now.
Provider
Next, we will include a Provider
The Provider Framework will simplify some of the responses in the Lambda function. This is the recommended approach when using Custom Resources in CDKs.
const customResourceProvider = new Provider(this, 'customResourceProvider', { onEventHandler: customResourceLambda, });
Custom Resource
Finally, we will call this Custom Resource
const customResourceResult = new CustomResource(this, 'customResourceResult', { serviceToken: customResourceProvider.serviceToken, properties: { customResourceNumber: props.customResourceNumber, }, });
Custom Resource Lambda Code
The Typescript Lambda will use the type definitions from DefinitelyTyped
to make things easier in the Lambda code.
import { CdkCustomResourceEvent, CdkCustomResourceResponse, Context, } from 'aws-lambda'; export const handler = async ( event: CdkCustomResourceEvent, context: Context, ): Promise<CdkCustomResourceResponse> => { console.log('Lambda is invoked with:' + JSON.stringify(event)); const response: CdkCustomResourceResponse = { StackId: event.StackId, RequestId: event.RequestId, LogicalResourceId: event.LogicalResourceId, PhysicalResourceId: context.logGroupName, }; if (event.RequestType == 'Delete') { response.Status = 'SUCCESS'; response.Data = { Result: 'None' }; return response; } try { const multiplyResult = event.ResourceProperties.customResourceNumber * 2; response.Status = 'SUCCESS'; response.Data = { Result: multiplyResult }; return response; } catch (error) { if (error instanceof Error) { response.Reason = error.message; } response.Status = 'FAILED'; response.Data = { Result: error }; return response; } };
Because we used the Provider
when creating this Custom Resource, the response is significantly easier. We just need to return a SUCCESS
or FAILED
to the onEvent
. We can do this with the CdkCustomResourceResponse
.
Using the Construct
No actual changes are needed in the demo.
CDK Stack Code
import { CfnOutput, Stack, StackProps } from 'aws-cdk-lib'; import { Construct } from 'constructs'; import { CustomResourceExample } from 'cdk-custom-resource-construct-example'; export class CdkCustomResourceConstructDemoStack extends Stack { constructor(scope: Construct, id: string, props?: StackProps) { super(scope, id, props); const result = new CustomResourceExample(this, 'customResourceResult', { customResourceNumber: 5, }); new CfnOutput(this, 'customResourceOutput', { value: result.customResourceResult, }); } }
Deploy
git clone https://github.com/schuettc/cdk-custom-resource-construct-demo cd cdk-custom-resource-construct-demo yarn yarn run build yarn cdk deploy
Result
CdkCustomResourceConstructDemoStack: deploying...
✅ CdkCustomResourceConstructDemoStack
✨ Deployment time: 81.79s
Outputs:
CdkCustomResourceConstructDemoStack.customResourceOutput = 10
Stack ARN:
arn:aws:cloudformation:us-east-1:112233445566:stack/CdkCustomResourceConstructDemoStack/e47e0d90-742d-11ed-ab03-128d8230a997
✨ Total time: 84.66s