×

Migrate CloudFormation Custom Resources after AWS adds support

New services do not always deliver support for CloudFormation at the launch. If you work with CloudFormation, you can create a Custom Resource. With the new import functionality, once AWS adds native support for the resource, you can now import this resource into your template and remove the old custom resource.

This blog post describes the 4 stages:

  1. Create the Custom Resource provider; a lambda function
  2. Deploy the resource using the previous Custom Resource provider
  3. Import the custom created resource into your stack
  4. Remove the custom resource from the stack, leaving the imported for future use

In our example we assume SNS Topic is not yet supported by CloudFormation at launch.

Stage 1: Create the Custom Resource provider

The following Lambda function creates or deletes the custom resources. I’ve removed all the pretty code for sake of this simple demo. Deploy this template using the aws cloudformation package and aws cloudformation deploy commands, or the SAM cli.

Transform: 'AWS::Serverless-2016-10-31'
Resources:
  CustomS3Bucket:
    Type: 'AWS::Serverless::Function'
    Properties:
      Runtime: python3.7
      Timeout: 30
      Handler: index.handler
      Policies:
        - arn:aws:iam::aws:policy/AmazonSNSFullAccess
      InlineCode: |
        import base64
        import json
        import cfnresponse
        import boto3

        sns = boto3.client('sns')
        
        def handler(event, context):
          topic_name = event['ResourceProperties']['TopicName']
          response_data = {}
          if event['RequestType'] == 'Create':
            sns.create_topic(
              Name=topic_name
            )
          elif event['RequestType'] == 'Update':
            sns.delete_topic(
              Name=topic_name
            )
            sns.create_topic(
              Name=topic_name
            )
          #else: # delete
          #  uncommented to force a retain
          #  delete(event['PhysicalResourceId'])
          cfnresponse.send(event, context, cfnresponse.SUCCESS, response_data, topic_name)

Stage 2: Deploy SNS using the Custom Resource provider

Resources:
  MyTopic:
    Type: Custom::SNSTopic
    Properties:
      ServiceToken:
        !Sub "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:sns-topic"
      TopicName: mytopic

Stage 3: Import Existing Resource

After a while AWS launches support for your custom created resource. In this example it’s SNS, or maybe even a feature of SNS Topic. To import the resource, use the following template and the web interface (as of this writing, CLI is not supported yet).

Important note: DeletionPolicy: Retain is a mandatory configuration for import to work.

We now have CloudFormation Resources for the same physical resource. That doesn’t matter, in the next stage we will remove the Custom Resource.

Resources:
  MyTopic:
    Type: Custom::TopicCreator
    Properties:
      ServiceToken:
        !Sub "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:sns-topic"
      TopicName: mytopic
  MyTopicImport:
    Type: AWS::SNS::Topic
    DeletionPolicy: Retain
    Properties:
      TopicName: mytopic

This is the current flow. Some confirmation screens are skipped:

Stage 4: Remove the old Custom Resource

Now we can switch back to our default deployment process to update the final stack with the template without any custom resources.

Important note: Make sure the custom resource does NOT delete the physical resource. In this example we simply removed the delete action in the function. But you could also decide to add a property: Retain: True, to skip the delete action.

Resources:
  MyTopicImport:
    Type: AWS::SNS::Topic
    DeletionPolicy: Retain
    Properties:
      TopicName: mytopic

Conclusion

We have learned how to use CloudFormation Custom Resources and the brand new Import functionality, to easier migrate from custom created resources, to native supported CloudFormation resources.

Picture of Martijn van Dongen
Martijn van Dongen
AWS Cloud Evangelist