Skip to content

Modeling Additional Environment Resources with AWS CloudFormation

Additional AWS resources, referred to as "addons" in the CLI, are any additional AWS services that a service or environment 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.

You can define additional resources for a workload (such as a Load Balanced Web Service or a Scheduled Job). The lifecycle of workload addons will be managed by the workload and will be deleted once the workload is deleted.

Alternatively, you can define additional shareable resource for an environment. Environment addons won't be deleted unless the environment is deleted.

This page documents how to create environment-level addons. For workload-level addons, visit Modeling Additional Workload Resources with AWS CloudFormation.

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 add other resources?

For other types of addons, you can add your own custom CloudFormation templates:

  1. You can store the custom templates in your workspace under copilot/environments/addons directory.
  2. When running copilot env deploy, the custom addons template will be deployed along with your environment stack.
Sample workspace layout with environment addons
.
└── copilot
    └── environments
        ├── addons  # Store environment addons.
        │   └── mys3.yaml
        ├── dev
        └── prod      

What does an addon template look like?

An environment addon template can be any valid CloudFormation template that satisfies the following:

  • Includes at least one Resource.
  • The Parameters section includes App, Env.

you can customize your resource properties with Conditions or Mappings.

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

Writing the Parameters section

There are a few parameters that Copilot requires you to define in your templates.

Parameters:
    App:
        Type: String
    Env:
        Type: String

Customizing the Parameters section

If you'd like to define parameters in addition to the ones required by Copilot, you can do so with a addons.parameters.yml file.

.
└── addons/
    ├── template.yml
    └── addons.parameters.yml # Add this file under your addons/ directory.
  1. In your template file, add the additional parameters under the Parameters section.
  2. In your addons.parameters.yml, define the values of those additional parameters. They can refer to values from your environment stack.
Examples: Customize addon parameters

# In "environments/addons/my-addon.yml"
Parameters:
  # Required parameters by AWS Copilot.
  App:
    Type: String
  Env:
    Type: String
  # Additional parameters defined in addons.parameters.yml
  ClusterName:
    Type: String
# In "environments/addons/addons.parameters.yml"
Parameters:
    ClusterName: !Ref Cluster

Writing the Conditions and the Mappings sections

Often, you want to configure your addon resources differently depending on certain conditions. For example, you could conditionally configure your DB resource's capacity depending on whether it is deploying to a production or a test environment. To do so, you can use the Conditions section and the Mappings section.

Examples: Configure addons conditionally
Mappings:
    MyAuroraServerlessEnvScalingConfigurationMap:
        dev:
            "DBMinCapacity": 0.5
            "DBMaxCapacity": 8   
        test:
            "DBMinCapacity": 1
            "DBMaxCapacity": 32
        prod:
            "DBMinCapacity": 1
            "DBMaxCapacity": 64
Resources:
    MyCluster:
        Type: AWS::RDS::DBCluster
        Properties:
            ScalingConfiguration:
                MinCapacity: !FindInMap
                    - MyAuroraServerlessEnvScalingConfigurationMap
                    - !Ref Env
                    - DBMinCapacity
                MaxCapacity: !FindInMap
                    - MyAuroraServerlessEnvScalingConfigurationMap
                    - !Ref Env
                    - DBMaxCapacity
Conditions:
  IsProd: !Equals [!Ref Env, "prod"] 

Resources:
  MyCluster:
    Type: AWS::RDS::DBCluster
    Properties:
      ScalingConfiguration:
          MinCapacity: !If [IsProd, 1, 0.5]
          MaxCapacity: !If [IsProd, 8, 64]

Writing the Outputs section

You can use the Outputs section to define any values that can be consumed by other resources; for example, a service, a CloudFormation stack, etc.

Environment addon: Connecting to your workloads

A value from an environment addon can be referenced by a workload addon or a workload manifest. To do so, you should first export the value from the environment addon using the Outputs section.

Example: Export values from environment addons
Outputs:
    MyTableARN:
        Value: !GetAtt ServiceTable.Arn
        Export:
            Name: !Sub ${App}-${Env}-MyTableARN  # This value can be consumed by a workload manifest or a workload addon.
    MyTableName:
        Value: !Ref ServiceTable
        Export:
            Name: !Sub ${App}-${Env}-MyTableName

It is important that you add the Export block. Otherwise, your workload stack or your workload addons won't be able to access the value. You will use Export.Name to reference the value from your workload-level resources.

Consideration: Namespace your Export.Name

You can specify any name you like for Export.Name. That is, it doesn't have to be prefixed with !Sub ${App}-${Env}; it can simply be MyTableName.

However, within an AWS region, the Export.Name must be unique. That is, you can't have two exports named MyTableName in us-east-1.

Therefore, we recommend you to namespace your exports with ${App} and ${Env} to decrease the chance of name collision. In addition, this makes it clear which application and environment the value is managed under.

With the namespace, for example, say your application's name is "my-app", and you deployed the addons with environment test, then the final export name would be rendered to my-app-test-MyTableName.

Referencing from a workload addon

In your workload addons, you can reference a value from your environment addons, as long as that value is exported. To do so, use the Fn::ImportValue function with that value's export name to import it from an environment addon.

Example: An IAM policy to access an environment-level DynamoDB table
Parameters:
  App:
    Type: String
    Description: Your application's name.
  Env:
    Type: String
    Description: The environment name your service, job, or workflow is being deployed to.
  Name:
    Type: String
    Description: Your workload's name.
Resources:
  MyTableAccessPolicy:
    Type: AWS::IAM::ManagedPolicy
    Properties:
      Description: Grants CRUD access to the Dynamo DB table
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Sid: DDBActions
            Effect: Allow
            Action:
              - dynamodb:* # NOTE: Scope down the permissions in your real application. This is done so that this example isn't too long!
            Resource: 
              Fn::ImportValue:                # <- We import the table ARN from the environment addons.
                !Sub ${App}-${Env}-MyTableARN # <- The export name that we used.
Referencing from a workload manifest

You can also reference a value from your environment addons in a workload manifest for variables, secrets and security_groups, as long as that value is exported. To do so, use from_cfn fields in workload manifests with that value's export name.

Examples: using from_cfn
name: db-front
type: Backend Service
variables:
  MY_TABLE_NAME:
    from_cfn: ${COPILOT_APPLICATION_NAME}-${COPILOT_ENVIRONMENT_NAME}-MyTableName
name: db-front
type: Backend Service

secrets:
  MY_CLUSTER_CREDS:
    from_cfn: ${COPILOT_APPLICATION_NAME}-${COPILOT_ENVIRONMENT_NAME}-MyClusterSecret
name: db-front
type: Backend Service

security_groups:
    - from_cfn: ${COPILOT_APPLICATION_NAME}-${COPILOT_ENVIRONMENT_NAME}-MyClusterAllowedSecurityGroup

Examples

Environment Addons Walk-through

See our v1.25.0 blog post for a detailed walk-through!