DEV Community

Cover image for CloudFormation Custom Resources with CDK and TypeScript
Kyle Lierer
Kyle Lierer

Posted on

CloudFormation Custom Resources with CDK and TypeScript

Lately, I have been doing a fair bit of CDK work with resources that CDK does not natively support. I was surprised to learn how powerful custom resources are with CDK but also how little documentation there was on how to build one with CDK. So, while this is not you want to do all the time, building a custom resource is a great tool to have in your toolbelt and can streamline your CDK codebase, infrastructure, and deployments when CDK does not support a resource that you need.

In this article, we will cover the following topics:

  • What is a Custom Resource?
  • Why Are Custom Resources Powerful with CDK?
  • How to Create a Custom Resource with CDK
    • The CDK Constructs
    • The AwsCustomResource Construct Example
    • The CustomResource Construct Example

All code examples in this article are written in TypeScript. If you are not familiar with TypeScript, you can refer to the TypeScript documentation for more information.

What is a Custom Resource?

While CloudFormation is a powerful infrastructure as a code tool, it does have some limitations. One of those limitations is that it does not support all AWS services (I have some strong opinions on this but that is for another time). A custom resource is AWS's way of allowing you to extend CloudFormation to support services and resources that are not natively supported. A custom resource is a Lambda function that is invoked by CloudFormation during the deployment process. The Lambda function can then do whatever it needs to do to create, modify, or delete the resource(s). The Lambda function is also responsible for reporting back to CloudFormation if the resource was created, modified, or deleted successfully.

It is really just as simple as that, a Lambda function that is invoked by CloudFormation during the deployment process and reports back to CloudFormation. For being such a simple concept, custom resources are actually really powerful and can be used to solve a lot of problems.

Why Are Custom Resources Powerful with CDK?

In good o'l fashion CloudFormation, custom resources, while not too difficult, can be quite tedious to work with. You have to create a Lambda function, zip it up, upload it to S3, and then reference it in your CloudFormation template. This is not too bad if you are doing it once or twice but if you are doing it for every custom resource you need to create, it can get old fast. If you want to streamline that process, you will have to either use a tool like AWS SAM, a third-party tool or build your own tooling. While these are all great options, they all have their own drawbacks. AWS SAM retains much of the same complexity as CloudFormation and can also be quite tedious to work with. Third-party tools are great but they are third-party tools and you have to trust them with your infrastructure. Building your own tooling is great but it is a lot of work and you have to maintain it.

CDK keeps things simple. You can create a custom resource with just a few lines of code. CDK will take care of the rest. It will create the Lambda function, zip it up, upload it to S3, and reference it in your CloudFormation template. That is it. You can then use that custom resource in your CDK codebase just like any other resource. It is really that simple.

How to Create a Custom Resource with CDK

Now that we have the high-level stuff out of the way, we can get more into the nitty gritty code and how to actually create a custom resource with CDK. We will start with the basics and then we will get into some more advanced topics and use cases.

The CDK Constructs

At the heart of all of this is the CustomResource and AwsCustomResource constructs. These constructs are what will create the custom resource for us. The CustomResource construct is the base construct that will create the custom resource. The AwsCustomResource construct is a higher-level construct that will create the custom resource and can handle some pretty common use cases. We'll start with an example of AwsCustomResource and then we will look at an example of CustomResource if we need to do something that AwsCustomResource cannot handle.

The docs for these constructs can be found here:

The AwsCustomResource Construct Example

The AwsCustomResource construct is a higher-level construct that will create the custom resource that can handle some pretty common use cases and can solve quite a few problems with ease. What makes the AwsCustomResource special is that it will create the Lambda function for us and it will handle the response to CloudFormation for us. This means that we do not have to worry about creating the Lambda function or handling the response to CloudFormation.

For this example, we will create a custom resource that finds an elastic IP that is not managed in CDK or CloudFormation. Sometimes, you might be working with AWS infrastructure that is not managed in code or was created in another CloudFormation stack with no exports. These scenarios can be a pain to work with because you have to manually find the resources and then add them to your CDK codebase. For EIPs, they do not have an existing CDK lookup option. To make this process easier, we can create a custom resource that will find the elastic IP for us. This way, we can just add the elastic IP to our CDK codebase and let the custom resource handle the rest.

The Code

import * as cr from 'aws-cdk-lib/custom-resources';
.
.
.
const provider = new cr.AwsCustomResource(this, 'EIP', {
  onCreate: {
    service: 'EC2',
    action: 'describeAddresses',
    parameters: {
      Filters: [
        {
          Name: 'domain',
          Values: ['vpc'],
        },
      ],
    },
    physicalResourceId: cr.PhysicalResourceId.of('EIP'),
  },
  policy: cr.AwsCustomResourcePolicy.fromSdkCalls({
    resources: cr.AwsCustomResourcePolicy.ANY_RESOURCE,
  }),
});

const eip = provider.getResponseField('Addresses.0.PublicIp');
Enter fullscreen mode Exit fullscreen mode

The Code Explained

The AwsCustomResource construct can handle OnCreate, OnUpdate, and OnDelete events via the onCreate, onUpdate, and onDelete properties. In our case, we only need to handle the OnCreate event. To do this, we can use the onCreate property. This property takes an object that defines the service, action, parameters, and physicalResourceId properties.

The service and action properties define the AWS service and action that will be invoked by the custom resource. If you want see the full list of services and actions that are supported, you can refer to the Actions, resources, and condition keys for AWS services documentation. In our case, we are using the EC2 service and the describeAddresses action. This action will return all of the elastic IPs in our account.

the action property defines the AWS service action that will be invoked by the custom resource. In our case, we are using the describeAddresses action. This action will return all of the elastic IPs in our account.

The parameters property defines the parameters that will be passed to the AWS service action.

The physicalResourceId property defines the physical resource ID that will be returned to CloudFormation. This is important because CloudFormation will use this ID to determine if the custom resource was created, updated, or deleted successfully. In our case, we are using the PhysicalResourceId construct to define the physical resource ID. This construct takes a string that will be used as the physical resource ID.

The policy property defines the IAM policy that will be used by the custom resource. In our case, we are using the AwsCustomResourcePolicy construct to define the IAM policy. This construct takes an object that defines the resources property. The resources property defines the resources that the custom resource will have access to. In our case, we are using the ANY_RESOURCE property which will give the custom resource access to all resources.

The getResponseField method is used to get a response field from the custom resource. This method takes a string that defines the response field that will be returned. In our case, we are using the Addresses.0.PublicIp response field which will return the public IP of the first elastic IP that is found. This is derived from the response of the describeAddresses action. You can refer to the DescribeAddresses documentation for more information on the response of the describeAddresses action. This will look different for every action and will most likely have to refer to the AWS SDK documentation to find the response field that you need.

The CustomResource Construct Example

It is not the most fun to end up in this situation, but sometimes you need to get your hands dirty and build something from scratch to suit your specific situation. Luckily, CustomResource is actually really easy to work with and can be used to build some pretty powerful custom resources. Let's take a look at how to build a custom resource from scratch.

For this example, we will create a custom resource that generates the priority of an AWS ALB listener rule. This is a real-world example that I have used in the past. AWS ALB listener rules famously do not have a priority property. This means that if you want to add a listener rule to an ALB, you have to specify a priority. This is not a big deal if you are only adding one listener rule but if you are adding multiple listener rules, you have to make sure that the priorities are unique and in the correct order. This can be a pain to manage. To make this process easier, we can create a custom resource that will generate the priority for us. This way, we can just add the listener rule and let the custom resource handle the priority.

The Lambda Code

The first thing we need to do is create the Lambda function. This is the function that will be invoked by CloudFormation during the deployment process. This function can be written in any language that Lambda supports. For this example, we will be using TypeScript to keep consistent with the rest of our code.

// Import necessary modules and libraries
import {
  CdkCustomResourceResponse,
  CdkCustomResourceEvent,
  Context,
  Handler,
} from 'aws-lambda';
import * as AWS from 'aws-sdk';

// Define a constant range for ALB rule priorities.
// 50000 is the maximum priority value, see for more info:
// https://docs.aws.amazon.com/elasticloadbalancing/latest/application/load-balancer-listeners.html#listener-rules
const ALB_RULE_PRIORITY_RANGE = [1, 50000];

/**
 * A lambda function that generates a random priority for an ALB listener rule and
 * avoids conflicts with existing priorities.
 * @param event The Lambda event.
 * @param context The Lambda context.
 */
export const handler: Handler = async (
  event: CdkCustomResourceEvent,
  context: Context
): Promise<CdkCustomResourceResponse> => {
  // Log the received event for debugging
  console.log('Received event: ' + JSON.stringify(event, null, 2));

  // Create a response object with initial properties
  const response: CdkCustomResourceResponse = {
    StackId: event.StackId,
    RequestId: event.RequestId,
    LogicalResourceId: event.LogicalResourceId,
    PhysicalResourceId: context.logGroupName,
  };

  try {
    // Check if the request type is 'Create'
    // There is no need to handle 'Update' or 'Delete' requests in
    // the context of listener rule priority generation. CloudFormation
    // will handle the deletion of the custom resource automatically.
    if (event.RequestType === 'Create') {
      // Create an ELBv2 instance using AWS SDK
      const elbv2: AWS.ELBv2 = new AWS.ELBv2();

      // Get the listener ARN from the properties
      const listenerArn: string = event.ResourceProperties.ListenerArn;

      // Log the listener ARN
      console.log(`Listener ARN: ${listenerArn}`);

      // Describe the rules for the specified listener ARN using AWS SDK
      // This requires the lambda to have "elasticloadbalancing:DescribeRules"
      // permission in the IAM policy.
      const result: AWS.ELBv2.DescribeRulesOutput = await elbv2
        .describeRules({ ListenerArn: listenerArn })
        .promise();

      // Extract the priorities from the rule descriptions
      const inUse = result.Rules!
        .map((r: AWS.ELBv2.Rule) => r.Priority)
        .filter((p: number | null) => Number.isInteger(p))
        .map((p: number) => p);


      // Log the in-use priorities
      console.log(`In use priorities: ${inUse}`);

      // Check if the generated priority is already in use, otherwise
      // generate a new one by adding the collision step size to the
      // generated priority until a free priority is found.
      let priority;
      while (!priority || inUse.includes(priority)) {
        // Increment the priority by the collision step size until
        // a free priority is found.
        priority =
        Number.parseInt(event.ResourceProperties.Priority) ||
        Math.floor(
          Math.random() *
            (ALB_RULE_PRIORITY_RANGE[1] - ALB_RULE_PRIORITY_RANGE[0] + 1) +
            ALB_RULE_PRIORITY_RANGE[0]
        );
      }

      // Log the generated priority
      console.log(`Generated priority: ${priority}`);

      // Set the response data with the generated priority
      response.Data = {
        Priority: priority,
      };
    }
  } catch (error) {
    // Log the error
    console.error(error);

    // Set the response status code to FAILED
    response.Status = 'FAILED';

    if (error instanceof Error) {
      // Set the response reason to the error message
      response.Reason = error.message;
    }
  }

  // Return the response object
  return response;
};
Enter fullscreen mode Exit fullscreen mode

The Lambda Code Explained

Now that we have the Lambda function, let's break it down and explain what is going on.

The Event

Probably the most important part of the custom resource Lambda function is the event. The event will communicate everything that is needed to define any custom resource you will be creating.

The event object comes in the following form (reference):

{
   "RequestType" : "Create",
   "ResponseURL" : "http://pre-signed-S3-url-for-response",
   "StackId" : "arn:aws:cloudformation:us-west-2:123456789012:stack/stack-name/guid",
   "RequestId" : "unique id for this create request",
   "ResourceType" : "Custom::TestResource",
   "LogicalResourceId" : "MyTestResource",
   "ResourceProperties" : {
      "Name" : "Value",
      "List" : [ "1", "2", "3" ]
   }
}
Enter fullscreen mode Exit fullscreen mode
Property Description
RequestType The type of request. The following are the possible values: Create, Update, Delete
ResponseURL The pre-signed Amazon S3 URL that can be used to send responses.
StackId The Amazon Resource Name (ARN) that identifies the stack that contains the custom resource.
RequestId The identifier that matches the request with the response. This value must be identical to the value of the RequestId property in the request.
ResourceType The resource type of the custom resource in the template. Custom resource type names can be up to 60 characters long and can include alphanumeric and the following characters: @-
LogicalResourceId The name of the custom resource in the template. This is provided to facilitate communication between the custom resource provider and the template developer.
ResourceProperties The custom resource resource properties that are defined in the CloudFormation template. The format of the properties is determined by the custom resource provider.

In TypeScript, luckily we have a type for this object. The type is CdkCustomResourceEvent. This type is defined in the aws-lambda module. The type is defined as follows:

export interface CdkCustomResourceEvent {
    RequestType: string;
    ResponseURL: string;
    StackId: string;
    RequestId: string;
    ResourceType: string;
    LogicalResourceId: string;
    ResourceProperties: {
        [Key: string]: any;
    };
    OldResourceProperties?: {
        [Key: string]: any;
    } | undefined;
}
Enter fullscreen mode Exit fullscreen mode

This can be used in your Lambda function by importing it from the aws-lambda module.

import { CdkCustomResourceEvent } from 'aws-lambda';
Enter fullscreen mode Exit fullscreen mode

The Response

The response object is what will be returned to CloudFormation after the Lambda function has been completed. This object will tell CloudFormation if the custom resource was created, updated, or deleted successfully. It will also contain any data that you want to return to CloudFormation. This data can be used in other resources in your CloudFormation template if is needed or wanted.

In TypeScript, luckily we have a type for this object. The type is CdkCustomResourceResponse. This type is defined in the aws-lambda module. Before we look at that, an example of the response object is below (reference):

{
   "Status" : "SUCCESS",
   "PhysicalResourceId" : "TestResource1",
   "StackId" : "arn:aws:cloudformation:us-west-2:123456789012:stack/stack-name/guid",
   "RequestId" : "unique id for this create request",
   "LogicalResourceId" : "MyTestResource",
   "NoEcho" : "false",
   "Data" : {
      "OutputName1" : "Value1",
      "OutputName2" : "Value2",
   }
}
Enter fullscreen mode Exit fullscreen mode
Property Description
Status The status value sent by the custom resource provider in response to an AWS CloudFormation-generated request. You must send SUCCESS or FAILED.
Reason An optional message that should be displayed to the user. This message can be up to 1 KB in size.
PhysicalResourceId An identifier that can be the physical ID that is tied to the custom resource. This value must be unique for the custom resource provider.
StackId The Amazon Resource Name (ARN) that identifies the stack that contains the custom resource.
RequestId The identifier that matches the request with the response. This value must be identical to the value of the RequestId property in the request.
LogicalResourceId The name of the custom resource in the template. This is provided to facilitate communication between the custom resource provider and the template developer.
NoEcho Indicates whether to mask the output of the custom resource when retrieved by using the Fn::GetAtt function. If set to true, all returned values are masked with asterisks (*****). The default value is false.
Data A map of the custom resource provider's outputs.

Again, in TypeScript, we have a type for this object. The type is CdkCustomResourceResponse. This type is defined in the aws-lambda module. The type is defined as follows:

export interface CdkCustomResourceResponse {
    PhysicalResourceId?: string;
    Data?:
        | {
            [Key: string]: any;
        }
        | undefined;
    [Key: string]: any;
}
Enter fullscreen mode Exit fullscreen mode

This can be used in your Lambda function by importing it from the aws-lambda module.

import { CdkCustomResourceResponse } from 'aws-lambda';
Enter fullscreen mode Exit fullscreen mode

The Handler

If you have done any Lambda development, you are probably familiar with the handler. The handler is the function that is invoked by Lambda. It is the entry point to your Lambda function. In TypeScript, the handler is defined as follows:

export type Handler = (
    event: any,
    context: Context,
    callback: Callback<any>
) => void;
Enter fullscreen mode Exit fullscreen mode

and it typically looks like this:

export const handler: Handler = async (
  event: any,
  context: Context,
  callback: Callback<any>
): Promise<void> => {
  // Do something
};
Enter fullscreen mode Exit fullscreen mode

In our case though, we can strongly type the event and response objects. This will make our code more readable and easier to maintain. To do this, we can use the CdkCustomResourceEvent and CdkCustomResourceResponse types that we discussed above. This will make our handler look like this:

export const handler: Handler = async (
  event: CdkCustomResourceEvent,
  context: Context
): Promise<CdkCustomResourceResponse> => {
  // Do something
};
Enter fullscreen mode Exit fullscreen mode

The Priority Generator

Alright, now that we have the event, response, and handler defined, we can get into the rest of the code. In our example above, we are creating a custom resource that will generate a priority for an ALB listener rule. This means that we will need to do the following:

  • Get the listener ARN from the properties
  • Get the existing listener rules priorities from the listener ARN
  • Generate a priority that is not in use
  • Return the generated priority

To do this, we can use the AWS SDK. We will need to import the aws-sdk module and create an instance of the ELBv2 class. This will allow us to interact with the ELBv2 service. We can then use the describeRules method to get the existing listener rules priorities from the listener ARN. This will look like this:

// Import necessary modules and libraries
import * as AWS from 'aws-sdk';
.
.
.
// Create an ELBv2 instance using AWS SDK
const elbv2: AWS.ELBv2 = new AWS.ELBv2();
.
.
.
// Describe the rules for the specified listener ARN using AWS SDK
// This requires the lambda to have "elasticloadbalancing:DescribeRules"
// permission in the IAM policy.
const result: AWS.ELBv2.DescribeRulesOutput = await elbv2
  .describeRules({ ListenerArn: listenerArn })
  .promise();

// Verify that the result is valid.
if (!result || !result.Rules) {
  throw new Error('Something went wrong try to describe the rules...', result);
}

// Create a nice function to extract the priorities from the rule descriptions.
const inUse = result.Rules
  .map((r: AWS.ELBv2.Rule) => r.Priority)
  .filter((p: number | null) => Number.isInteger(p))
  .map((p: number) => p);

// Log the in-use priorities
console.log(`In use priorities: ${inUse}`);

// Check if the generated priority is already in use, otherwise
// generate a new one by adding the collision step size to the
// generated priority until a free priority is found.
let priority;
while (!priority || inUse.includes(priority)) {
  // Increment the priority by the collision step size until
  // a free priority is found.
  priority =
  Number.parseInt(event.ResourceProperties.Priority) ||
  Math.floor(
    Math.random() *
      (ALB_RULE_PRIORITY_RANGE[1] - ALB_RULE_PRIORITY_RANGE[0] + 1) +
      ALB_RULE_PRIORITY_RANGE[0]
  );
}
.
.
.
Enter fullscreen mode Exit fullscreen mode

The Response

Now that we have the priority generated, we can return it to CloudFormation. To do this, we can set the Data property on the response object. This will look like this:

// Set the response data with the generated priority
response.Data = {
  Priority: priority,
};
Enter fullscreen mode Exit fullscreen mode

The Error Handling

Now that we have the happy path working, we need to handle the error path. This is important because if we do not handle the error path, CloudFormation will not know if the custom resource was created, updated, or deleted successfully. To do this, we can wrap the priority generator in a try/catch block. In the catch block, we can set the Status property on the response object to FAILED and even set the Reason property to the error message; both of which will be returned to CloudFormation. This will look like this:

try {
  // Our happy path code
  } catch (error) {
    // Log the error
    console.error(error);

    // Set the response status code to FAILED
    response.Status = 'FAILED';

    if (error instanceof Error) {
      // Set the response reason to the error message
      response.Reason = error.message;
    }
  }

  // Return the response object
  return response;
};
Enter fullscreen mode Exit fullscreen mode

All Together

Now that we have all of the pieces, we can put them all together. This will look like this:

// Import necessary modules and libraries
import {
  CdkCustomResourceResponse,
  CdkCustomResourceEvent,
  Context,
  Handler,
} from 'aws-lambda';
import * as AWS from 'aws-sdk';

// Define a constant range for ALB rule priorities.
// 50000 is the maximum priority value, see for more info:
// https://docs.aws.amazon.com/elasticloadbalancing/latest/application/load-balancer-listeners.html#listener-rules
const ALB_RULE_PRIORITY_RANGE = [1, 50000];

/**
 * A lambda function that generates a random priority for an ALB listener rule and
 * avoids conflicts with existing priorities.
 * @param event The Lambda event.
 * @param context The Lambda context.
 */
export const handler: Handler = async (
  event: CdkCustomResourceEvent,
  context: Context
): Promise<CdkCustomResourceResponse> => {
  // Log the received event for debugging
  console.log('Received event: ' + JSON.stringify(event, null, 2));

  // Create a response object with initial properties
  const response: CdkCustomResourceResponse = {
    StackId: event.StackId,
    RequestId: event.RequestId,
    LogicalResourceId: event.LogicalResourceId,
    PhysicalResourceId: context.logGroupName,
  };

  try {
    // Check if the request type is 'Create'
    // There is no need to handle 'Update' or 'Delete' requests in
    // the context of listener rule priority generation. CloudFormation
    // will handle the deletion of the custom resource automatically.
    if (event.RequestType === 'Create') {
      // Create an ELBv2 instance using AWS SDK
      const elbv2: AWS.ELBv2 = new AWS.ELBv2();

      // Get the listener ARN from the properties
      const listenerArn: string = event.ResourceProperties.ListenerArn;

      // Log the listener ARN
      console.log(`Listener ARN: ${listenerArn}`);

      // Describe the rules for the specified listener ARN using AWS SDK
      // This requires the lambda to have "elasticloadbalancing:DescribeRules"
      // permission in the IAM policy.
      const result: AWS.ELBv2.DescribeRulesOutput = await elbv2
        .describeRules({ ListenerArn: listenerArn })
        .promise();

      // Extract the priorities from the rule descriptions
      const inUse = result.Rules!
        .map((r: AWS.ELBv2.Rule) => r.Priority)
        .filter((p: number | null) => Number.isInteger(p))
        .map((p: number) => p);


      // Log the in-use priorities
      console.log(`In use priorities: ${inUse}`);

      // Check if the generated priority is already in use, otherwise
      // generate a new one by adding the collision step size to the
      // generated priority until a free priority is found.
      let priority;
      while (!priority || inUse.includes(priority)) {
        // Increment the priority by the collision step size until
        // a free priority is found.
        priority =
        Number.parseInt(event.ResourceProperties.Priority) ||
        Math.floor(
          Math.random() *
            (ALB_RULE_PRIORITY_RANGE[1] - ALB_RULE_PRIORITY_RANGE[0] + 1) +
            ALB_RULE_PRIORITY_RANGE[0]
        );
      }

      // Log the generated priority
      console.log(`Generated priority: ${priority}`);

      // Set the response data with the generated priority
      response.Data = {
        Priority: priority,
      };
    }
  } catch (error) {
    // Log the error
    console.error(error);

    // Set the response status code to FAILED
    response.Status = 'FAILED';

    if (error instanceof Error) {
      // Set the response reason to the error message
      response.Reason = error.message;
    }
  }

  // Return the response object
  return response;
};
Enter fullscreen mode Exit fullscreen mode

The CDK Construct Code

Now that we have our Lambda which will act as the backend of our custom resource, we can actually create the custom resource (I know, shocking). Luckily, this is pretty easy to do with CDK. To do this, we can create a new object CustomResource.

const lambda = new lambda.NodejsFunction(this, 'Lambda', {
  entry: 'src/lambda.ts',
  handler: 'handler',
  runtime: lambda.Runtime.NODEJS_18_X,
  timeout: cdk.Duration.seconds(30),
  bundling: {
    minify: true,
    sourceMap: true,
  },
});

const provider = new cr.Provider(this, 'Provider', {
  onEventHandler: lambda,
});

const resource = new cdk.CustomResource(this, 'CustomResource', {
  serviceToken: provider.serviceToken,
  properties: {
    ListenerArn: 'arn:aws:elasticloadbalancing:<region>:<account>:listener/<listener-id>',
  },
});

const priority = resource.getAttString('Priority');
Enter fullscreen mode Exit fullscreen mode

The Construct Code Explained

Unlike the AwsCustomResource construct, the CustomResource construct does not create the Lambda function for us. This means that we have to create the Lambda function ourselves. To do this, we can use the NodejsFunction construct from the @aws-cdk/aws-lambda-nodejs module. This construct will create the Lambda function for us.

We can then use the Provider construct from the @aws-cdk/custom-resources module to create the custom resource provider. A custom resource provider is where we pass the Lambda to the custom resource. What makes it special is that we can use one Lambda for multiple custom resources. via the service token. This means that we can use the same Lambda for multiple custom resources. This is great because it means that we do not have to create a new Lambda for every custom resource that we create.

Once that is done, we can pass it to the custom resource provider to the CustomResource construct. This will create a custom resource for us. This construct takes an object that defines the serviceToken and properties properties. The serviceToken property defines the service token of the custom resource provider. The properties property defines the properties that will be passed to the Lambda function. In our case, we are passing the listener ARN to the Lambda function.

Now, all we have to do is get the priority from the custom resource. To do this, we can use the getAttString method. This method takes a string that defines the attribute that will be returned. In our case, we are using the Priority attribute which will return the priority that was generated by the custom resource.

Conclusion

Custom Resources are quite a powerful tool to have in your CDK toolbelt. While not necessary all the time, they can be used to solve a lot of problems.

If you are doing something simple that an AWS Action already covers well, you can use the AwsCustomResource construct. This will allow you to create a custom resource with just a few lines of code. This is great for simple use cases.

If you are doing something more complex that requires a custom Lambda function, you can use the CustomResource construct. This will allow you to create a custom resource with just a few lines of code. This is great for more complex use cases.

Hopefully, this article has helped you understand custom resources a little better and how to use them with CDK!

Top comments (0)