Conditionally deploying parts of your CloudFormation stack with the Serverless Framework

What do you do when you want to customize which resources are deployed via CloudFormation?

While working on putting the finishing touches on v1 of Hook Relay, we decided to try using AWS Timestream, AWS' recently-released time series database. This led to a problem with deploying our stack, since we are using the Serverless Framework to deploy the Lambda functions we are using to multiple AWS regions.

The problem was that Timestream isn't yet available in all the regions we are using, so the CloudFormation stack that is being generated by the framework isn't valid in those regions that don't have Timestream. There isn't a built-in way to exclude a portion of the resources block (the portion that creates the Timestream database and table) when deploying to specific regions, so we had to get a little creative. Here's our solution:

serverless.yml

service: hook-relay

provider:
  name: aws

custom:
  resources:
    us-east-1: ${file(./resources/timestream.yml)}

resources:
  - ${file(./resources/main.yml)}
  - ${self:custom.resources.${self:provider.region}, ''}

resources/timestream.yml

Resources:
  tsdb:
    Type: AWS::Timestream::Database
    Properties:
      DatabaseName: "hook-relay-${self:custom.stage}"

  tsdeliveries:
    Type: AWS::Timestream::Table
    Properties:
      DatabaseName: !Ref tsdb
      TableName: deliveries

As you can see, we use a combination of custom variables, file includes, default values, and the ability to define multiple Resources blocks to get what we want. First we define a custom variable resources.us-east-1 that yanks in the content of the timestream.yml file, which has the CloudFormation definition of the resources that should only be created in that one region. Then we include the contents of that variable in the resources block by interpolating the current region as part of the variable name, with an empty string as the default fallback when that custom variable doesn't exist (e.g., when deploying to eu-central-1 via a command line option to sls deploy). Finally, we include the contents of resources/main.yml in the resources block. This file has another CloudFormation configuration that defines the resources that should be present in each region, such as DynamoDB tables, SQS queues, etc.

With that, whenever you run sls deploy, regardless of the region you deploy to, you'll get the AWS resources you want, without the ones you don't. Thanks to Cameron Childress for providing essential tips that led to this solution.

author photo

Benjamin Curtis

Ben has been developing web apps and building startups since '99, and fell in love with Ruby and Rails in 2005. Before co-founding Honeybadger, he launched a couple of his own startups: Catch the Best, to help companies manage the hiring process, and RailsKits, to help Rails developers get a jump start on their projects. Ben's role at Honeybadger ranges from bare-metal to front-end... he keeps the server lights blinking happily, builds a lot of the back-end Rails code, and dips his toes into the front-end code from time to time. When he's not working, Ben likes to hang out with his wife and kids, ride his road bike, and of course hack on open source projects. :)


“We’ve looked at a lot of error management systems. Honeybadger is head and shoulders above the rest and somehow gets better with every new release.”
Michael Smith
Try Error Monitoring Free for 15 Days