Extend AWS Amplify with Custom Resources

BLOG ARTICLE

Blog - Extend Amplify

AWS Amplify is an opinionated full stack serverless framework and set of cloud services that enables developers to build web and mobile applications that are highly available, scalable, secure, and cost effective.

Amplify provides framework libraries and UI components for many AWS managed services to accelerate feature development, reducing the amount of boilerplate code needed to integrate these services into your applications.

Supported AWS services are arranged into categories and managed via the Amplify CLI. Developers use opinionated workflows provided by the CLI to add, update, remove to their applications. Currently Amplify includes the following categories and supported AWS services.

While the list above is certainly impressive, what if we want to consume AWS services not currently supported by the framework?

Recently we had to do just that. That is, we needed to integrate AWS Step Functions into an existing Amplify serverless application and still retain the consistent toolchain experience and lifecycle management. Luckily Amplify has an extensibility feature that allows us to describe our custom resources within the framework constructs.

To extend our Amplify based applications with custom resources we start by declaring our custom category within the Amplify toolchain. Amplify categories are declared in the /amplify/backend/backend-config.json file so we give our custom category a name and add resources under it in the same way the Amplify CLI does for the other categories.

{
    “function”: {},
    “auth”: {},
    “api”: {},
    “stepFunction”: {
        “processWorkflow”: {
            “service”: “Step Function”,
            “providerPlugin”: “awscloudformation”,
            “dependsOn”: []
        }
    }
}

 

In the example above, the custom category name is stepFunction and our first resource is named processWorkflow. Amplify will expect the same naming convention for your custom resources as it does for other categories, so keep category and resources names short and non-conflicting.

Next, we replicate the directory structure Amplify uses for the other categories and their backend resources.

/amplify
  |- backend
  |   |- stepFunction
  |   |   |- processWorkflow

 

Under the newly created resource directory, add our CloudFormation template and parameter json files used to describe our custom resource for Amplify to manage for us. We need to ensure these files are unique within our custom category, so it is a good idea to prefix these template files with the resource name as follows.

/amplify
  |- backend
  |   |- stepFunction
  |   |   |- processWorkflow
  |   |   |   - processWorkflowTemplate.json
  |   |   |   - processWorkflowParameters.json

 

Now we can populate our CloudFormation template to describe our Step Function state machines, IAM roles, policies etc as we would outside of Amplify. If we need to reference outputs from other resources, we can declare them as parameters in our template and add them in the dependsOn array in our backend-config.json.

In our case we require the name and ARN of a Lambda function added using the Amplify CLI. Add the output attributes we require in the backend-config.json file.

{
    "function": {
        "emailFunction": {
            "service": "Lambda",
            "providerPlugin": "awscloudformation",
            "build": true,
            "dependsOn": []
        }        
    },
    "auth": {},
    "api": {},
    "stepFunction": {
        "processWorkflow": {
            "service": "Step Function",
            "providerPlugin": "awscloudformation",
            "dependsOn": [
                {
                    "category": "function",
                    "resourceName": "emailFunction",
                    "attributes": [
                        "Name",
                        "Arn"
                    ]
                }                
            ]
        }
    }
}

 

Then add parameters to our custom CloudFormation template. When adding parameters, we need to use the naming convention expected by the Amplify toolchain

              <category><resourceName><attributeName>

{
    "AWSTemplateFormatVersion": "2010-09-09",
    "Description": "Process workflow Step Function",
    "Parameters": {
        "env": {
            "Type": "String"
        },          
        "functionemailFunctionName": {
            "Type": "String
        },             
        "functionemailFunctionArn": {
            "Type": "String
        }
    },
    "Resources": {
        "ProcessWorkflowExecutionRole": {},
        "ProcessWorkflowStateMachine": {}
    },
    "Outputs": {}
}

The Amplify toolchain will then pass the parameters to our CloudFormation Stack during backend deployment.

The last step we need to do is let the CLI know about our custom category and resource by checking out the current environment.

amplify env checkout <env-name>

Adding custom AWS resources to our Amplify projects involves a bit of plumbing but as we have seen above the pattern is quite straight forward and can be used to extend your Amplify projects to integrate with any AWS service that can be described using CloudFormation which is pretty much anything on the AWS platform.