The last point is actually not so bad, since it means that being serious about automation on AWS also means you need to lay off point-and-click in the console. This is ultimately a good thing (point-and-click is fine for learning, automation is good for production). The first two points though, need fixing.

Disclaimer: Amateur at work

Let's be clear: I am no Cloudformation expert. Maybe you're reading this and thinking that I'm being the expert beginner, thinking I have "a better way" and solving the wrong problems. If that's the case please let me know.

Idempotency

I first discovered Cloudformation's lack of idempotency working on a template that involved provisioning an SSL certificate. Running the same template with the same input parameters and tags would always provision a new certificate. This is a bit of a drag, as provisioning certificates involves manually opening an email to click a link.

It turns out though, that Cloudformation can act in an idempotent-ish manner by way of the client request token. This is an arbitrary string you pass to create-stack and update-stack, and Cloudformation will only ever process one request with the same client request token. If you make a second pass with the same token, Cloudformation fails. I guess I would've preferred a silent no-op, but I'll take what I can get. This means that if you build a hash of your template and the parameters (and any tags set on the stack), we can basically make Cloudformation apply templates in an idempotent manner.

The template and parameters can be hashed in any way, I chose shasum, which is readily available:

If you run this twice, it will fail the second time. Success! Kinda. The problem with this approach is that CloudFormation remembers the client request token seemingly forever. That means that the following scenario does not work as expected:

Create a stack with client request token "A"

Change some parameters and update the stack with client request token "B"

Revert changes and "update" stack back to the original version - CloudFormation fails

The last step fails because the client request token was already used. So the once so promising client request token cannot be used after all.

Tags to the resque

We have a hash that uniquely defines a change set, but we cannot use it with client request token, lest our stack will be unable to assume the same state twice. Speaking of change sets, CloudFormation has a first-class notion of a change sets. However, they are not a good solution to quick updates (or not) of stacks as they have side-effects, and are geared more towards reviewing changes before applying.

One possible solution to our idempotency problem is tags. Use the hash we computed before as a tag. Before applying the stack up, read tags from the existing stack and abort if it is already in the desired state.

Upsert

create-stack and update-stack take exactly the same arguments, so there's not much point in needing to branch on them in client code. Let's instead write an apply-stack function that can figure out if the stack needs creating or updating.

describe-stacks fails if you use it with a non-existent stack, so something like this would work:

The wrapper script will roughly parse the arguments to look at stack-name, parameters, tags, template-body, region, and profile, then use those values to describe the stack to figure out if it exists. It will then either create or update the stack, passing in a hash as a tag that ensures that no-op updates are never applied.