Nathan Malishev

@nathan.malishev

Your Infrastructure as Code 🌩 CloudFormation Vs Terraform?

Photo by eberhard grossgasteiger on Unsplash

Imagine if all of your infrastructure configurations from AWS, Azure or Google could be replicated faster and more accurately than you could click. This is infrastructure as code.

The insane benefits of using infrastructure as code are:

  • Being able to sleep at night, knowing you can redeploy to a given state safely. If god forbid your infrastructure vanishes
  • Follow coding best practices, check in changes, code review and build your infrastructure as a team

I am going to be taking a look at both Terraform & CloudFormation. I used Terraform extensively at Localz but for the sake of learning I decided to get my hands dirty with CloudFormation.

The two contenders

Terraform is the communities answer to infrastructure as code. It is open source with over 12k stars, is cloud agnostic supporting hundreds of providers. It is often less verbose than CloudFormation & has a great module system.

CloudFormation is developed and maintained by AWS, it is very tightly integrated and only supports AWS. The tight integration lends itself to having a great UI, being able to view all your stacks for a given account is great. It enables cross stack referencing with ease, which is also a massive win for modularity and breaking down that monolith.

Command Line Interface & Work flow

Terraform’s most used command feature init and plan. Since Terraform is cloud agnostic you need to explicitly say where you will store your state file, this is a part of the init command. Running a Terraform plan is used to create an execution plan, against your state file and existing infrastructure.

A simple Terraform execution plan

CloudFormation has a similar process where you create & execute change sets. You will use a combination of create-change-set , execute-change-set and deploy. create-change-set is akin to Terraform’s plan whilst execute-change-set is Terraform’s apply & deploy rolls both commands into one.

The main difference is you will be looking at and resolving issues through CloudFormation’s UI, which is easy to use, even for me 😃.

CloudFormation’s Change set

Both Terraform's plan & CloudFormation’s create-change-set is a great place to have a manual check, in your continuous integration pipeline.

Using Gitlab to wait on a manual inspection of the change set

I found CloudFormations API’s to be a little confused. I believe create-change-set followed by a execute-change-set used to be the de facto but now deploy exists to replace them both. But in some cases especially deploying to production, you would want to follow good practice and inspect the execution plan before you executing it.

I thought in this case you would want to use a create-change-set followed by a execute-change-set but that is annoying as create-change-set explicitly needs to know whether you are creating or updating stack. So this would cause heartache in your continuous integration pipelines. But after further inspection, I believe you run a deploy with a --no-execute-change-set flag, and then re run deploy without the flag. I’m still unsure of the recommended approach.

Misaligned State

I ran a simple experiment where both tools created a simple EC2 instance. I deleted both instances and tried to run both deployment steps again.

Hidden within Terraform’s plan is a refresh which is used to reconcile your state file, with the real world infrastructure. This means Terraform was able to detect the deletion & boot up a new instance. CloudFormation has no such reconciliation and depends on the existing stack. Thus it was adamant nothing had changed. The only fix for CloudFormation was to rename the EC2 resource.

I wouldn’t rely on this as a silver bullet for reconciliation and wouldn’t ever recommend manually playing with any stacks, that are managed by any tool. It will only lead to heartbreak 💔.

Functions & Interpolation

Intrinsic functions is one of my favourite parts of CloudFormation. Terraform does have a lot more functions than CloudFormation, but I prefer the syntax that CloudFormation uses, the lack of quotations, dollar signs & curly braces can make for some more readable code.

I also found the occasional shortcomings of Terraform’s interpolation in certain circumstances.

A simple referencing comparison

Conditional Deployments

Conditional deployments are an important part of any deployment, you want to be deploying from the same stack whether its production or development. But sometimes production just requires extra resources, or vice versa.

Only CloudFormation supports conditionals explicitly, you can tag each resource with a conditional flag. Where as Terraform requires you to make use of their (awesome) count parameter and ternary conditional.

The count parameter is unique to Terraform, so CloudFormation would require more repeated code.

Conditionals Example

CrossStack Referencing

One of CloudFormations most powerful features is being able to so easily cross stack reference. This makes it extremely easy to break up the infrastructure monolith!

Cross stack referencing is a breeze

In the above example, the Networking Stack exports its DbSubnet1, as ${branch}-DbSubnet1. Which in my EC2 Stack I simply reference that Subnet using the Fn::ImportValue: !Sub ${branch}-DbSubnet1 . CloudFormation is also smart enough to know that the EC2 stack relies on the Networking Stack and will refuse to delete it until its dependencies are gone.

Modules

Exclusive to Terraform modules are a very powerful way to break down your Terraform deployments. A Terraform module is essentially a collection of Terraform files & resources that define a set piece of infrastructure. It allows for inputs & outputs. To use the module, you simply pass in your inputs as variables and under the hood Terraform will pull in the module and execute the Terraform code as usual.

Reuse of a terraform module

Above is an example, where I define many instances in any region by simply using a reusable module. Without this module, I would need to define each instance and the associated Terraform code required to set up an instance in a new region.

The reason why this is not the same as a CloudFormation stack cross reference, is that the Terraform state file would still contain all of the state. Where as with CloudFormation, the state files would be split between each stack making it more manageable.

CloudFormation uses nested stacks to accomplish the same task. The issue with nested stacks are that if a child stack fails the entire stack will. This makes for deployment pains & nightmares. Approaching CloudFormation using a layered cake approach and cross stack referencing is the way to go.

CloudFormation with a nested stack
CloudFormation nested stack web UI

User Data & CloudFormation Init

CloudFormation also features CloudFormation init, which is AWS’s way of taking your user data script and turning it into state based configuration. It is extremely powerful and shouldn’t be overlooked. It allows you to run stack updates against instances and reconcile the state. Where as previously using user data you would have to terminate and create a new instance.

A slice of CloudFormation Init

Error Handling

Maybe I was never very good at Terraform, but I have been preferring the way CloudFormations handles errors. The CloudFormation UI is really good and gives a good overview of your stack. I found that with Terraform errors, while they pointed you in the right direction it was still hard to gain an overall idea of what had happened.

The CloudFormation Events UI traces your stack deployment, so you can easily see what error’d out.

Support

You would think CloudFormation would support every AWS feature in existence, but unfortunately you are wrong. Because open source is the best, Terraform seems to have more AWS features.

Take for example, RDS with IAM auth, this feature has been released for over a year and is still in the CloudFormation backlog. Where as Terraform had a contributor gazoakley, make a simple PR and Terraform supports RDS IAM auth well before CloudFormation.

Another more important example, there is no Key Secrets Manager CloudFormation support. Where as terraform has its own resources for secrets manager since April.

Open source software is my favorite

We can’t forget that Terraform is cloud agnostic, a big win. But I wouldn’t take it too literally as from what I have seen cloud providers are pretty good at sinking their tiny hooks into you through features, or specific codes like ARNs.

But because Terraform is cloud agnostic, it can really be applied to anything that has state and an API. For example Kong API Gateway can be managed with Terraform. Thus you can apply your skills you’ve learnt else where in your stack!

Summary

I enjoy both Terraform & CloudFormation as they are both a great step in the right direction.

My Pros & Cons

CloudFormation has a great UI for both debugging and general overview of everything thats happening. Cross referencing is powerful & makes separating stacks easy, whilst I personally really enjoy the syntax. The lack of support is my real gripe, as it’s not open source the thought of having to use custom resources or shell scripts to get certain features makes me cringe. 😢

Terraform is great because of it’s vibrant community of open sources, it’s simple module paradigm & it’s cloud agnostic. Terraform can be hard to debug through the cli and often you end up with a large monolith repo that includes all of your infrastructure. You can break it down into modules, but it doesn’t quite have the same separation of concerns you can get with CloudFormation.

Thanks for reading! If you enjoyed it be sure to give it a clap and check out both Terraform & CloudFormation!

More by Nathan Malishev

Topics of interest

More Related Stories