Skip to content

Modeling with AWS CloudFormation

Additional AWS resources, referred to as "addons" in the CLI, are any additional AWS services that a service manifest does not integrate by default. For example, an addon can be a DynamoDB table, an S3 bucket, or an RDS Aurora Serverless cluster that your service needs to read or write to.

How do I add an S3 bucket, a DDB Table, or an Aurora Serverless cluster?

Copilot provides the following commands to help you create certain kinds of addons:

  • storage init will create a DynamoDB table, an S3 bucket, or an Aurora Serverless cluster.

You can run copilot storage init from your workspace and be guided through some questions to help you set up these resources.

How to do I add other resources?

For other types of addons, you can add your own custom CloudFormation templates according to the following instructions.

Let's say you have a service named webhook in your workspace:

└── copilot
    └── webhook
        └── manifest.yml
And you want to add a custom DynamoDB table to webhook. Then under the webhook/ directory, create a new addons/ directory and add a CloudFormation template for your instance.
└── copilot
    └── webhook
        ├── addons
        │   └── mytable-ddb.yaml
        └── manifest.yaml
Typically each file under the addons/ directory represents a separate addon and is represented as an AWS CloudFormation (CFN) template. For example, if we want to also add an S3 bucket addon to our service then we could either run storage init or create our own custom, separate mybucket-s3.yaml file.

When your service gets deployed, Copilot merges all these files into a single AWS CloudFormation template and creates a nested stack under your service's stack.

What does an addon template look like?

An addon template can be any valid CloudFormation template.
However, by default, Copilot will pass the App, Env, and Name Parameters; you can customize your resource properties with Conditions or Mappings if you wish to.

Connecting addon resources to your workloads

Here are several possible ways to access addon Resources from your ECS task or App Runner instance:

  • If you need to add additional policies to your ECS task role or App Runner instance role, you can define an IAM ManagedPolicy addon resource in your template that holds the additional permissions, and then output it. The permission will be injected into your task or instance role.
  • If you need to add a security group to your ECS service, you can define a Security Group in your template, and then add it as an Output. The security group will be automatically attached to your ECS service.
  • If you'd like to inject a secret to your ECS task, you can define a Secret in your template, and then add it as an Output. The secret will be injected into your container and can be accessed as an environment variable as capital SNAKE_CASE.
  • If you'd like to inject any resource value as an environment variable, you can create an Output for any value that you want to be injected as an environment variable to your ECS tasks. It will be injected into your container and accessed as an environment variable as capital SNAKE_CASE.

Writing a template

When writing your own template, you must:

  • Include the Parameters section with App, Env, Name.
  • Include at least one Resource.

Here is an example template for a DynamoDB table addon:

# You can use any of these parameters to create conditions or mappings in your template.
    Type: String
    Description: Your application's name.
    Type: String
    Description: The environment name your service, job, or workflow is being deployed to.
    Type: String
    Description: The name of the service, job, or workflow being deployed.

  # Create your resource here, such as an AWS::DynamoDB::Table:
  # MyTable:
  #   Type: AWS::DynamoDB::Table
  #   Properties:
  #     ...

  # 1. In addition to your resource, if you need to access the resource from your ECS task 
  # then you need to create an AWS::IAM::ManagedPolicy that holds the permissions for your resource.
  # For example, below is a sample policy for MyTable:
    Type: AWS::IAM::ManagedPolicy
        Version: '2012-10-17'
          - Sid: DDBActions
            Effect: Allow
              - dynamodb:BatchGet*
              - dynamodb:DescribeStream
              - dynamodb:DescribeTable
              - dynamodb:Get*
              - dynamodb:Query
              - dynamodb:Scan
              - dynamodb:BatchWrite*
              - dynamodb:Create*
              - dynamodb:Delete*
              - dynamodb:Update*
              - dynamodb:PutItem
            Resource: !Sub ${ MyTable.Arn}

  # 1. You need to output the IAM ManagedPolicy so that Copilot can add it as a managed policy to your ECS task role.
    Description: "The ARN of the ManagedPolicy to attach to the task role."
    Value: !Ref MyTableAccessPolicy

  # 2. If you want to inject a property of your resource as an environment variable to your ECS task,
  # then you need to define an output for it.
  # For example, the output MyTableName will be injected in capital snake case, MY_TABLE_NAME, to your task.
    Description: "The name of this DynamoDB."
    Value: !Ref MyTable

On your next release, Copilot will include this template as a nested stack under your service!


We recommend following Amazon IAM best practices while defining AWS Managed Policies for the additional resources, including:

Customizing the Parameters section

AWS Copilot always requires the App, Env, and Name parameters to be defined in your template. However, if you'd like to define additional parameters that refer to resources in your service stack you can do so with a addons.parameters.yml file.

└── addons/
    ├── template.yml
    └── addons.parameters.yml # Add this file under your addons/ directory.

In your addons.parameters.yml, you can define additional parameters that can refer to values from your workload stack. For example:

  ServiceName: !GetAtt Service.Name
Finally, update your template file to refer to the new parameter:
  # Required parameters by AWS Copilot.
    Type: String
    Type: String
    Type: String
  # Additional parameters defined in addons.parameters.yml
    Type: String