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:
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.
Terraformās most used command feature [init](https://www.terraform.io/docs/commands/init.html)
and [plan](https://www.terraform.io/docs/commands/plan.html)
. 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](https://docs.aws.amazon.com/cli/latest/reference/cloudformation/create-change-set.html)
Ā , [execute-change-set](https://docs.aws.amazon.com/cli/latest/reference/cloudformation/execute-change-set.html)
and [deploy](https://docs.aws.amazon.com/cli/latest/reference/cloudformation/deploy/index.html)
. 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.
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](https://www.terraform.io/docs/commands/refresh.html)
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 š.
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 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
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.
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
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
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.
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!
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!