CloudFormation Best-Practices

May 1st, 2019 573 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.


  • AWS re:Invent 2019 Recap & Videos

    December 15 th, 2019 364 Words

    I was able to attend the AWS re:Invent 2019 conference. A week full of learning about current and new technologies, services, and general approaches is definitely overwhelming. There is no much content available, during the conference, and as videos and slide decks afterwards. I tried to list my favourite talks. There are way too much of them.

  • AWS Single Sign-On and Multi-Account Cloud Setup

    German August 16 th, 2019
  • GraphQL with AWS AppSync and AWS Lambda

    German July 2 nd, 2019
  • Makefile Best-Practices

    April 30 th, 2019 235 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 tooling interface.

  • AppSync GraphQL API with Custom Domain and CloudFormation

    April 7 th, 2019 272 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.