Do not use AWS CloudFormation

Several years ago I actually cared about the differences between AWS CloudFormation and Terraform. Namely, that Terraform did not provide wait conditions and helper scripts that made sense in 2017.

In 2017, I was a bit apprehensive picking up a new tool that didn’t seem as “AWS-native” as CloudFormation was, and I was all-in on AWS

Things are different now. AWS services have evolved quite a bit since then and most companies I lend my services to don’t even run EC2 instances anymore. Many clients I have are running containers or serverless (Lambda) based workloads.

In 2021, you should not use AWS CloudFormation.

Why?

First and mostly, indirection.

Let’s start with the way CloudFormation works, compared to how Terraform works. With CloudFormation, you make requests through one API — the CloudFormation API. This API takes a JSON or YAML-formatted document, validates it, and then passes calls to the other APIs on your behalf. If you are creating an RDS cluster, CloudFormation will call the RDS REST endpoint for you.

With Terraform, your local executable makes rest calls to each service’s REST API for you, meaning no intermediary sits between you and the service you’re controlling. Want an RDS instance? Terraform will make calls directly to the RDS API.

This is *most* important while troubleshooting. With CloudFormation, you’ll get back whatever CloudFormation reports from the target service. Most of the time, the error messages from CloudFormation are not helpful. With Terraform, the error messages you get are usually much more informative, and mirror the error messages you would get from the service, itself. These error messages are easier to search for online, sometimes appearing in AWS’s own documentation.

CloudFormation being a layer of indirection makes it difficult to work with in multi-region/multi-account scenarios. With CloudFormation you have to create Stack Sets and IAM policies that allow the CloudFormation service to impersonate other roles. The prerequisite steps you have to take to use CloudFormation across multiple accounts also must be taken just to have CloudFormation spin up resources in multiple regions within the same account.

With Terraform, you need to use valid credentials that grant access to multiple accounts. For instance, if you were spinning up CloudTrails in multiple accounts within an organization, then sure: you have to either impersonate a role, or use direct credentials in each account with the required access. But operating in multiple regions across an account is as simple as overloading a provider:

provider “aws” {
alias = “us-east-1”
region = “us-east-1”
}

provider “aws” {
alias = “us-east-2”
region = “us-east-2”
}

Obviously this is a much lower barrier to orchestrating your architecture in more than one region at once.

Logic

CloudFormation’s pseudo parameters and intrinsic functions are cruel jokes. By contrast, Terraform offers a rich set of data sources and transforming data with Terraform is a breeze.

Even considering older versions of Terraform’s shortcomings, the Hashicorp DSL that Terraform employs is vastly more capable than AWS’s JSON/YAML implementation.

Speed

AWS CloudFormation is S-L-O-W. Maybe it’s because it mostly executes actions sequentially (or at least in a severely rate-limited fashion, and maybe I don’t care about the difference). It is not uncommon to wait for hours at a time for AWS CloudFormation to exit whatever transitive state it’s in and free up your stack. Until then, development on that stack is blocked.

Sync vs Async

AWS CloudFormation transactions are asynchronous, while Terraform’s interactions are almost always done synchronously. Wrapping CI/CD tooling around AWS CloudFormation is, as a result, a difficult matter of polling stack updates until the right stack state bubbles up from an aws cloudformation CLI query.

Terraform, on the other hand, will occupy your shell until the directly-involved AWS service coughs up an error. No additional tooling is required. Terraform will just relay the error message from the affected service indicating what you’ve done wrong.

AWS’s concept of stack events is just awful, too.

Portability

If you learn AWS CloudFormation, then guess what: you can’t take your skills with you. If you put forth the effort to learn Terraform, then you can take your skills to any other cloud (or any other provider. There are hundreds of providers that are useful outside of the major cloud ecosystems.

Useful providers you can run on your own laptop include TLS key generators and random string generators. These two local providers, alone, make SSH key generation and password generation a trivial matter. AWS CloudFormation can’t do either of these things.

Importing Existing Resources

I can’t even begin to tell you how to import resources with AWS CloudFormation, and neither can AWS’s engineers. Chime in if you can.

Service Quotas

The list of quotas for the AWS CloudFormation service is just hilarious. Meanwhile (for good or ill) I’ve had no problem spinning up over 1,000 resources with Google Cloud Platform from a single Terraform state.

What about CDK?

I really want the AWS CDK to show some promise. Constructs are exciting and could seriously change the game of Infrastructure as Code. But CDK transpiles into CloudFormation templates. For that reason alone I can’t recommend it.

Same goes for the Serverless Framework. Its tight coupling with CloudFormation means you would do well to avoid it. Perhaps the serverless.tf toolkit is worth its salt?

Personally, I haven’t encountered an Infrastructure-as-Code challenge that requires tools like CDK and Pulumi. I’m not convinced you need a full-blown programming language to declare your infrastructure, but I am convinced that some developers are bored. Either of these tools is going to offer you something to do so you can learn Typescript or Python; even if they’re solutions looking for problems to solve.

What about the new partial rollback feature?

Just a couple of weeks ago, AWS announced that they would support leaving partially failed stacks online so you can diagnose what’s going on. Yes, this feature is helpful. No, it doesn’t make AWS CloudFormation much better to use.

So I should never use CloudFormation?

The short answer is, almost never. The longer answer is that there are some individuals who have written truly portable AWS CloudFormation stacks that do hard work for you, like spin up lambdas or even clusters of servers. These people are doing God’s work. You’ll know you’ve found a well-written, portable CloudFormation stack by whether you can control its placement with simple parameters specifying which subnets or VPCs you wish to target.

If you find one of these, and you don’t have to do the hard work of spinning up these resources yourself, then use the aws_cloudformation_stack resource in Terraform and supply just enough configuration to put your stack’s resources in the right place.

TL;DR

If you’re new to DevOps and Infrastructure-as-Code and you have a team to lead, do yourself a favor and pick Terraform over AWS CloudFormation. For many reasons including speed, ease of development, and portability, you’ll be glad you did.