Improved Iterative CloudFormation Infrastructure Development
I love using CloudFormation for provisioning AWS services but one of the more annoying aspects about working with it is that setting up the initial CloudFormation stack is an all-or-none endeavour. This is in contrast to when a stack exists and is being updated, where changes are reversible and the stack is almost always left in a working state. This annoyance is compounded when I write a stack definition with multiple resources and, as it inevitably happens, there is a typo somewhere. CloudFormation dutifully creates all the resources, trips on the error, and rolls back the entire stack to nothingness. Then I need to delete the stack and start again instead of issuing an update or changeset request. It's a time-sink and I wanted to find a better way.
The way I build CloudFormation stacks now is to provision the stack with a dummy resource, just to create the stack itself. Then, once created, I create change sets for each new real resource that I need. This allows CloudFormation to provision the new resource(s) as directed, but if it encounters an error it simply removes the failing resource and the stack remains as it was before the change, ready for the changeset operation to be repeated.
The following is the snippet I start with when building a new stack.
---
AWSTemplateFormatVersion: "2010-09-09"
Description: "Provisions ..."
Resources:
Dummy:
Type: "AWS::CloudFormation::WaitConditionHandle"
And that's it.
The use of a CloudFormation::WaitConditionHandle resource as a stand-in doesn't do anything, nor does it cost anything, so you can provision it without a time or financial cost (hat tip to Kadeem for showing me this trick years ago). However, this Dummy
resource is required because CloudFormation requires at least one resource for a template to be considered valid.
The next step is to create the stack like normal:
aws cloudformation create-stack --stack-name ... --template-body file://infra.cft
Once that completes (it will be fast), I then update the CloudFormation template to remove the Dummy
resource and add the new resources…
---
AWSTemplateFormatVersion: "2010-09-09"
Description: "Provisions ..."
Resources:
ArtifactBucket:
Type: "AWS::S3::Bucket"
Properties:
…and then issue a changeset request to provision it in AWS…
aws cloudformation create-change-set --stack-name ... --template-body file://infra.cft --change-set-name AddBucket
…and then approve the change set.
aws cloudformation execute-change-set --stack-name ... --change-set-name AddBucket
The change set creation and approval steps are typically rolled into a Makefile for ease of maintenance. Sometimes I don't automate the stack creation process in a Makefile because I want that to remain manual (pro-tip: only automate the things you expect to do often).