CloudFormation Best-Practices

May 1st, 2019 • 457 Words

You can find plenty of frameworks and tools to provision your AWS resources. Some of them do a great job for a specific purpose, others are more generic. Nevertheless, I do prefer to use native CloudFormation templates as much as possible.

Together with my Makefile Best-Practices you can build a powerful toolbox around your service without managing a complex list of dependencies.

Prefix all the Things

When using Continuous Integration and Continuous Deployment patterns for your service, you might want to spin up your CloudFormation Stack in different environments automatically. Sooner or later, you might end up with resources using the same name. So: Add a prefix or suffix to all your resources.

AWSTemplateFormatVersion: 2010-09-09
Resources:
  User:
    Type: AWS::IAM::User
    Properties:
      UserName: !Sub ${Prefix}-username
Parameters:
  Prefix:
    Type: String

Using prefixes, you can re-use a CloudFormation template for all your environments. Regardless if it’s for production and stable, or if you want to spin up temporary environments having a pullrequest-123 prefix.

$ > aws cloudformation deploy \
    --template-file ./infra.yml \
    --parameter-overrides Prefix=production

Split up Stacks

Of course, you can put all resources into a single CloudFormation Stack. But don’t! As soon as you have resources for storage or state handling and consumers to access the data, split those resources into multiple stacks. Only group resources by their technical context, not the AWS service name.

./example
├── Makefile
├── README.md
└── infra
    ├── api.yml
    ├── domain.yml
    └── storage.yml

I tend to store all CloudFormation templates inside a folder named infra and name them by purpose, not by AWS product names.

Nested Stacks

With multiple CloudFormation Stacks, you cannot avoid dependencies between stacks. Sometimes you need the Name of a resource or an Arn to create an IAM Policy for example.

CloudFormation Stacks can define Outputs and you can use Export to enable access to those outputs by other CloudFormation Stacks. But don’t! Avoid exporting CloudForamtion Stack Outputs whenever possible! Nested CloudFormation Stacks provide a slick approach to handle dependencies between stacks and minimize the needed tooling.

./example
├── Makefile
├── README.md
├── infra.yml
└── infra
    ├── api.yml
    └── storage.yml
AWSTemplateFormatVersion: 2010-09-09
Resources:
  Storage:
    Type: AWS::CloudFormation::Stack
    Properties:
      TemplateURL: ./infra/storage.yml

  API:
    Type: AWS::CloudFormation::Stack
    Properties:
      TemplateURL: ./infra/api.yml
      Parameters:
        DynamoDBStreamArn: !GetAtt Storage.Outputs.DynamoDBStreamArn

With a Nested CloudFormation Stack, you do not need to invest effort and time to come up with tooling that reads the Outputs from one stack and passes it as Parameters to another stack. Just use GetAtt to pass data from one stack to another.

Property Mappings

If your setup uses Continuous Integration and Continuous Deployment, you may want to have different provisioned resources. Together with passing a prefix to my CloudFormation Stack, I prefer to pass an environment identifier, called ENV for example.

Parameters:
  ENV:
    Type: String
  Prefix:
    Type: String

Having two parameters ENV and Prefix, it’s simple to deploy multiple CloudFormation Stacks to one environment; PR15 and PR16 for the dev environment for example. Thanks to CloudFormation Mappings, and the FindInMap function, you can provision resources based on the ENV parameter now:

Mappings:
  ElasticsearchDomainConfiguration:
    prod:
      InstanceType: m4.10xlarge.elasticsearch
    dev:
      InstanceType: t2.micro.elasticsearch

Resources:
  ElasticsearchDomain:
    Type: AWS::Elasticsearch::Domain
    Properties:
      DomainName: !Sub ${Prefix}-es-domain
      ElasticsearchClusterConfig:
        InstanceType:
          Fn::FindInMap: [ElasticsearchDomainConfiguration, !Ref ENV, InstanceType]

As there is no support for fallback values for FindInMap, you must need to configure a mapping configuration for every value of ENV. In this case, there is luckily only prod and dev as environment types.

I plan to write down more Best Practices soonish


  • Makefile Best-Practices

    April 30th, 2019 • 167 Words

    The more projects you work on, the more streamlined your tooling gets. Hopefully. Various services using different languages have different tooling requirements, of course. A sweet Makefile can be the entry to a unified…

  • AppSync GraphQL API with Custom Domain and CloudFormation

    April 7th, 2019 • 165 Words

    With AWS AppSync, it’s easy to run your own serverless GraphQL service API. Thanks to Velocity Mapping Templates, DynamoDB, and AWS Lambda your can aim for an architecture without any maintenance at all. Getting started…

  • Serverless GraphQL with AWS AppSync and Lambda

    June 17th, 2018 • 1009 Words

    Let me be honest with you: GraphQL is the shit! Once you use GraphQL, you will never want to use anything else again. The same is true for a working and maintainable serverless FaaS infrastructure. Combine both…

  • Last Update: 2019-12-08T13:15:37.795Z
    Stresemannstraße 132, 22769 Hamburg
    +49 151 54 64 90 55
    inbox@sbstjn.com