Page updated Jan 16, 2024

Use CDK to add custom AWS resources

The Amplify CLI provides the ability to add custom AWS resources with AWS Cloud Development Kit (CDK). Running the amplify add custom command in your Amplify project provides CDK starter stacks along with mechanisms to reference other Amplify-generated resources.

The AWS Cloud Development Kit (AWS CDK) is an open source software development framework to define your cloud application resources using familiar programming languages, such as Typescript.

amplify add custom
1amplify add custom
? How do you want to define this custom resource? … (Use arrow keys or type to filter) ❯ AWS CDK AWS CloudFormation
1? How do you want to define this custom resource? … (Use arrow keys or type to filter)
2❯ AWS CDK
3 AWS CloudFormation

A skeleton CDK stack is generated in the amplify/backend/custom/<resource-name> directory. Edit the cdk-stack.ts file to define the additional AWS resources. Add additional CDK constructs using the package.json. Run amplify build to download all NPM dependencies for this custom resource and synthesize the CDK stack.

To migrate from AWS CDK v1 to v2, refer to the migration documentation.

The following example creates an SNS topic whose notification messages are forwarded via email:

import * as cdk from 'aws-cdk-lib'; import * as AmplifyHelpers from '@aws-amplify/cli-extensibility-helper'; import { Construct } from 'constructs'; import * as sns from 'aws-cdk-lib/aws-sns'; import * as subs from 'aws-cdk-lib/aws-sns-subscriptions'; export class cdkStack extends cdk.Stack { constructor(scope: Construct, id: string, props?: cdk.StackProps, amplifyResourceProps?: AmplifyHelpers.AmplifyResourceProps) { super(scope, id, props); /* Do not remove - Amplify CLI automatically injects the current deployment environment in this input parameter */ new cdk.CfnParameter(this, 'env', { type: 'String', description: 'Current Amplify CLI env name', }); const amplifyProjectInfo = AmplifyHelpers.getProjectInfo(); const snsTopicResourceName = `sns-topic-${amplifyProjectInfo.projectName}-${cdk.Fn.ref('env')}`; const topic = new sns.Topic(this, 'sns-topic', { topicName: snsTopicResourceName, }); topic.addSubscription(new subs.EmailSubscription('<your-email-address>')); new cdk.CfnOutput(this, 'snsTopicArn', { value: topic.topicArn, description: 'The arn of the SNS topic', }); } }
1import * as cdk from 'aws-cdk-lib';
2import * as AmplifyHelpers from '@aws-amplify/cli-extensibility-helper';
3import { Construct } from 'constructs';
4import * as sns from 'aws-cdk-lib/aws-sns';
5import * as subs from 'aws-cdk-lib/aws-sns-subscriptions';
6
7export class cdkStack extends cdk.Stack {
8 constructor(scope: Construct, id: string, props?: cdk.StackProps, amplifyResourceProps?: AmplifyHelpers.AmplifyResourceProps) {
9 super(scope, id, props);
10 /* Do not remove - Amplify CLI automatically injects the current deployment environment in this input parameter */
11 new cdk.CfnParameter(this, 'env', {
12 type: 'String',
13 description: 'Current Amplify CLI env name',
14 });
15
16 const amplifyProjectInfo = AmplifyHelpers.getProjectInfo();
17 const snsTopicResourceName = `sns-topic-${amplifyProjectInfo.projectName}-${cdk.Fn.ref('env')}`;
18 const topic = new sns.Topic(this, 'sns-topic', {
19 topicName: snsTopicResourceName,
20 });
21
22 topic.addSubscription(new subs.EmailSubscription('<your-email-address>'));
23 new cdk.CfnOutput(this, 'snsTopicArn', {
24 value: topic.topicArn,
25 description: 'The arn of the SNS topic',
26 });
27 }
28}

Note: Always append the Amplify environment name to a resource name to ensure correct behavior with Amplify's multi-environment workflows.

When creating custom resources using Amplify CLI, the CLI may require additional permissions outside the Amplify managed policy, AdministratorAccess-Amplify. Please refer to the Amplify IAM Policy documentation for additional information regarding the necessary permissions for Amplify CLI and for more information on providing additional permissions to your Amplify CLI IAM user refer to AWS IAM User documentation.

Reference Amplify project and environment name

Amplify provides you helper functions to get Amplify project information such as the project name and the current Amplify environment name.

Reference Amplify project name

Use the AmplifyHelpers.getProjectInfo() function to retrieve the project name:

import * as AmplifyHelpers from '@aws-amplify/cli-extensibility-helper'; // ... const projectName = AmplifyHelpers.getProjectInfo().projectName;
1import * as AmplifyHelpers from '@aws-amplify/cli-extensibility-helper';
2
3// ...
4
5const projectName = AmplifyHelpers.getProjectInfo().projectName;

Reference Amplify environment name

There are two access patterns for the environment for two different use cases. Using the right access mechanism for the right use case is critical to ensure multi-environment workflows function properly.

If you want to use the environment name as a variable for a resource, use cdk.Fn.ref('env'):

const role = new iam.Role(this, 'CustomRole', { assumedBy: new iam.AccountRootPrincipal(), roleName: `${roleResourceNamePrefix}-${cdk.Fn.ref('env')}` // Reference to Amplify "env" name })
1const role = new iam.Role(this, 'CustomRole', {
2 assumedBy: new iam.AccountRootPrincipal(),
3 roleName: `${roleResourceNamePrefix}-${cdk.Fn.ref('env')}` // Reference to Amplify "env" name
4})

If you want to apply conditional logic based on the current environment name, use AmplifyHelpers.projectInfo():

import * as AmplifyHelpers from '@aws-amplify/cli-extensibility-helper'; // ... const envName = AmplifyHelpers.getProjectInfo().envName; if (envName === "prod") { // ... } else { // ... }
1import * as AmplifyHelpers from '@aws-amplify/cli-extensibility-helper';
2
3// ...
4
5const envName = AmplifyHelpers.getProjectInfo().envName;
6
7if (envName === "prod") {
8 // ...
9} else {
10 // ...
11}

Reference Amplify-generated resources

The CDK stack for custom AWS resources can reference Amplify-generated resources' CloudFormation outputs. To reference another resource, first add the resource as a dependency.

import { AmplifyDependentResourcesAttributes } from "../../types/amplify-dependent-resources-ref"; const dependencies: AmplifyDependentResourcesAttributes = AmplifyHelpers.addResourceDependency(this, amplifyResourceProps.category, amplifyResourceProps.resourceName, [{ category: "function", // api, auth, storage, function, etc. resourceName: "<resource-name>" // find the resource at "amplify/backend/<category>/<resourceName>" } /* add more dependencies as needed */] );
1import { AmplifyDependentResourcesAttributes } from "../../types/amplify-dependent-resources-ref";
2
3const dependencies: AmplifyDependentResourcesAttributes = AmplifyHelpers.addResourceDependency(this,
4 amplifyResourceProps.category,
5 amplifyResourceProps.resourceName,
6 [{
7 category: "function", // api, auth, storage, function, etc.
8 resourceName: "<resource-name>" // find the resource at "amplify/backend/<category>/<resourceName>"
9 } /* add more dependencies as needed */]
10);

Then use the cdk.Fn.ref function to create a dynamic reference of the dependencies' outputs.

const myFunctionArn = cdk.Fn.ref(dependencies.function.<resource-name>.Arn)
1const myFunctionArn = cdk.Fn.ref(dependencies.function.<resource-name>.Arn)

The dependencies variable has the auto-generated AmplifyDependentResourcesAttributes type which includes all Amplify-generated resource output. Run amplify build to regenerate AmplifyDependentResourcesAttributes if you don't see your category, resource, or parameters.