Advanced Custom Resources with AWS CDK

Roy Ben Yosef
CyberArk Engineering
5 min readMay 23, 2021

--

Photo by Laura Ockel on Unsplash

Part 1: Custom Resources with CDK’s AWSCustomResource

In Part 1, we learned what AWS CloudFormation custom resources are and how to create a custom resource using the AWS CDK’s AWSCustomResource.

As the AWS documentation explains: “Custom resources provide a way for you to write custom provisioning logic in CloudFormation template and have CloudFormation run it during a stack operation, such as when you create, update or delete a stack.”

The AWSCustomResource construct is a simple way to create custom resources and it works great when you have a simple, one-to-one mapping between the create, update and delete events of your resource and the JavaScript SDK APIs. For example, an Amazon Simple Storage Service (Amazon S3) object custom resource using the s3.putObject and s3.deleteObject APIs.

This isn’t always the case. You might not be able to create your resource with single API calls, or perhaps you need to implement some logic during the resource deployment.

In this blog, we will learn how to create custom resources in cases where your resources require more than a single API call for create, update and delete, which the AWSCustomResource construct provides. See code snippets and a link to a fully working example at the bottom.

The Problem with Deploying an IoT Policy

I recently had to deploy an IoT policy — and naturally, I used iot.CfnPolicy. This is an AWS CDK construct for deploying an IoT policy.

When I tried to update the policy document, I found that this isn’t supported due to this AWS CloudFormation open issue.

A possible way to work around this is by using a custom resource. For this, we will need to implement the following events:

  1. On create — create the policy
  2. On update — make sure that we won’t exceed the maximum number of versions by cleaning up the oldest version
  3. On delete — delete all versions before deleting the policy (otherwise policy deletion fails)

Since we have a little logic to implement and more than one API call to make, we need to find the right tool for the job.

We will have to go deeper.

Photo by Iñaki del Olmo on Unsplash

Custom Resources with CDK — 102

AWSCustomResource should be used if a single API call corresponds to the create, update and delete events of your resource. If this is not the case, you need to go to a lower abstraction and use the Custom Resources module.

When working with Custom Resources, AWS CloudFormation sends lifecycle events (create, update, delete) to custom resource providers. In AWS CDK Custom Resources this is the Provider class that uses a Lambda event handler to carry out the create, update and delete operations.

  • Provider: It is described as a “mini-framework for implementing providers for AWS CloudFormation custom resources.” (see the docs for more information)
  • Lambda event handler: Implements the create, update and delete events. In case our Lambda needs access to a certain VPC, Subnets and Security groups, you can configure this at the provider level.

Long-running resource deployment: In case your custom resource events are long-running and will exceed the max Lambda timeout, you can implement the custom resource as an async operation. In this case, the event Lambda will initiate the creation of the resource and return, and another Lambda (the isCompleteHandler) will be called periodically, test the completion of the operation and report back.

To make things clear, let’s look at some code.

Photo by Jason Abdilla on Unsplash

Let’s Get to Work

The code snippets below are all in AWS CDK using Python. If you’re new to AWS CDK, take a look at “Getting started with AWS CDK” first. You can grab a fully working example here.

First, let’s create the Lambda handler which will take care of the create, update and delete events.

A few things to note:

  • on_create: must return the physical ID of the resource.
  • on_updated: handles the max number of versions (five), by deleting the non-default, oldest version, which is indicated by the lowest version ID. Should return the physical ID of the resource. If a new physical ID value is returned, CloudFormation will follow with a subsequent Delete for the previous ID (resource replacement).
  • on_delete: deletes all non-default versions and then deletes the policy. You cannot delete the policy and its default version before deleting all non-default versions.
  • Important Note: In Lambda role policy, I provided permissions to the exact IoT policy name. But note that if you try to update the policy name, it will fail, see this open issue. If you are required to support policy name update, a workaround, until this issue is resolved, is to set the resources on IotPolicyProvisioningPolicy to
    arn:aws:iot:{region}:{account_id}:policy/* which is more permissive.

Next, let’s create our AWS CDK construct which will include the event handler Lambda, its role, the Provider and the CDK’s CustomResource construct.

  • The Lambda role includes the basic Lambda role and the permissions required to create, update and delete the policy we are creating.
  • The Lambda code is inline, reading it from a file, which is fine in our case where we do not have any third-party dependencies (AWS dependencies are ok).
  • The Lambda timeout is 5 minutes by default, if your resource creation is extremely long, you can choose to do it asynchronously (see below).
  • The custom resource removal policy is “DESTROY,”which means it will be deleted with the stack — although this doesn’t always have to be the case.
Photo by Fitsum Admasu on Unsplash

Long-Running Resources

In a situation where your resource deployment takes a long time (More than Lambda’s max timeout), you cannot have the event handler wait for it. The solution is to do things asynchronously.

The event handler will initiate the creation (or update or deletion) and return immediately. Another Lambda, Provider’s isCompleteHandler, will be invoked periodically (according to the Provider’s queryInterval) and return a completion indication when it finds that the resource is created.

You can find a working example in the example repo (See this, iot_policy_is_complete_handler.py and iot_policy_resource_async.py), but keep in mind that it’s just an example, IoT policy doesn’t require this since it’s quick to create, update and delete.

Wrapping Up

AWS CDK’s AWSCustomResource is the right tool when your resource lifecycle requires exactly one API call. In all other cases, you can use AWS CDK’s CustomResource to implement your logic in a Lambda and deploy your resources the way you need to. Specifically, we saw how this solves a concrete problem when deploying AWS IoT policies and trying to update them.

--

--

Roy Ben Yosef
CyberArk Engineering

Sr. Software architect at CyberArk’s Technology Office. Into code, architecture and problem solving. Like to build and fix stuff. Usually late at night.