Systems come in all shapes and sizes; virtual machines, cloud instances, containers, serverless, and one thing holds true – you need a way to create them in an automated, yet efficient fashion. Without having the ability to make systems repeatable, organizations come to a grinding halt. The most popular way in the world of DevOps and automation today is with Infrastructure-as-Code (IaC). In this blog post, you’ll learn about two popular Infrastructure-as-Code platforms, the key differences, and how they compare.

Infrastructure-as-Code

The ability to automate systems has gone through several iterations. It started with scripting, using tools like VB Script, Batch, and Bash, and slowly moved towards more dedicated platforms and tools. Organizations like Puppet and Chef were some of the first among the many vendors today that started the Infrastructure-as-Code and Configuration Management movement. At many organizations, you’ll still see scripts being used to configure services and systems, but it’s definitely not as structured and efficient as Infrastructure-as-Code tools, like Terraform. It’s also not as popular as it used to be ever since IaC became a big deal in the tech world.

If you’re new to Infrastructure-as-Code, think of it as a way to provision services and systems with code. You write the code, whether it’s in a configuration language like JSON or YAML, or it could even be written with a programming language like HashiCorp Configuration Language (HCL), Python, or Go. Once the code is written, it can be used to create, replace, update, or delete (CRUD) systems and services in the cloud, in virtualized environments, or on-prem. The way that IaC is able to perform CRUD operations is because the infrastructure code is interacting with public APIs. For example, if you’re creating an EC2 instance in AWS, the infrastructure code is calling upon the public API for EC2.

In the past 1-2 years, a new paradigm has been gaining a ton of traction, Infrastructure-as-Software. Infrastructure-as-Software is a way to define infrastructure code with a general-purpose programming language (JavaScript, Python, etc.) instead of using a configuration language like JSON/YAML, or a human-readable language like HCL.

Challenges Using IaC

Many tools like Terraform are geared towards anyone in the development space or the infrastructure space. The idea is that the code may not be application code, but it should be thought of any treated the same. Code is code, and with that in mind, it must be maintained, created, and deployed the same way as any other code.

The biggest challenge for a lot of vendors creating Infrastructure-as-Code platforms is how to gear products and features towards infrastructure engineers. The reason why is because a lot of infrastructure pros are not developers and don’t have a developer-first mindset. Because of that, a lot of infrastructure engineers aren’t comfortable with the idea of writing code to automate, scale, or deploy services.

Another large challenge is how to turn the developer mindset on. For example, a lot of systems administrators may be more comfortable writing code or a script and save it to their local computer. In the development world, that’s a big no-no. Instead, you would commit and save the code to source control, like GitHub.

Thinking about both challenges and combining them, it’s all about changing the mindset from infrastructure engineers don’t write code at their job to everyone is a developer in today’s world because there are several different types of developers.

CloudFormation

CloudFormation, much like any other Infrastructure-as-Code platform, is a way for developers and infrastructure pros to create, update, and delete services in AWS. Anything from EC2 instances to S3 buckets to Lambda Functions are available to be automated with CloudFormation. You can define your infrastructure code using YAML or JSON.

When you use CloudFormation, a Stack is created. A Stack is a collection of resources that you’re creating with CloudFormation. For example, let’s say you want to create an S3 bucket, an IAM role, and an IAM policy. Even though those are three different services, you can still define all three in the same CloudFormation Stack.

Below are two examples that both create an S3 bucket. The first is in JSON.

{
    "AWSTemplateFormatVersion": "2010-09-09",
    "Resources": {
        "S3Bucket": {
            "Type": "AWS::S3::Bucket",
            "Properties": {
                "AccessControl": "PublicRead",
                "WebsiteConfiguration": {
                    "IndexDocument": "index.html",
                    "ErrorDocument": "error.html"
                }
            },
            "DeletionPolicy": "Retain"
        },
        "BucketPolicy": {
            "Type": "AWS::S3::BucketPolicy",
            "Properties": {
                "PolicyDocument": {
                    "Id": "MyPolicy",
                    "Version": "2012-10-17",
                    "Statement": [
                        {
                            "Sid": "PublicReadForGetBucketObjects",
                            "Effect": "Allow",
                            "Principal": "*",
                            "Action": "s3:GetObject",
                            "Resource": {
                                "Fn::Join": [
                                    "",
                                    [
                                        "arn:aws:s3:::",
                                        {
                                            "Ref": "S3Bucket"
                                        },
                                        "/*"
                                    ]
                                ]
                            }
                        }
                    ]
                },
                "Bucket": {
                    "Ref": "S3Bucket"
                }
            }
        }
    },
    "Outputs": {
        "WebsiteURL": {
            "Value": {
                "Fn::GetAtt": [
                    "S3Bucket",
                    "WebsiteURL"
                ]
            },
            "Description": "URL for website hosted on S3"
        },
        "S3BucketSecureURL": {
            "Value": {
                "Fn::Join": [
                    "",
                    [
                        "https://",
                        {
                            "Fn::GetAtt": [
                                "S3Bucket",
                                "DomainName"
                            ]
                        }
                    ]
                ]
            },
            "Description": "Name of S3 bucket to hold website content"
        }
    }
}

There is also the ability, as discussed, to use YAML instead of JSON for a CloudFormation template.

AWSTemplateFormatVersion: 2010-09-09
Resources:
  S3Bucket:
    Type: AWS::S3::Bucket
    Properties:
      AccessControl: PublicRead
      WebsiteConfiguration:
        IndexDocument: index.html
        ErrorDocument: error.html
    DeletionPolicy: Retain
  BucketPolicy:
    Type: AWS::S3::BucketPolicy
    Properties:
      PolicyDocument:
        Id: MyPolicy
        Version: 2012-10-17
        Statement:
          - Sid: PublicReadForGetBucketObjects
            Effect: Allow
            Principal: '*'
            Action: 's3:GetObject'
            Resource: !Join 
              - ''
              - - 'arn:aws:s3:::'
                - !Ref S3Bucket
                - /*
      Bucket: !Ref S3Bucket
Outputs:
  WebsiteURL:
    Value: !GetAtt 
      - S3Bucket
      - WebsiteURL
    Description: URL for website hosted on S3
  S3BucketSecureURL:
    Value: !Join 
      - ''
      - - 'https://'
        - !GetAtt 
          - S3Bucket
          - DomainName
    Description: Name of S3 bucket to hold website content

Although not specifically CloudFormation, there is a newer way to define infrastructure code with the AWS Cloud Development Kit (CDK). The CDK allows you to create infrastructure code with a general-purpose programming language (Infrastructure-as-Software). Although you write the infrastructure code with a programming language, AWS still utilizes CloudFormation on the backend to create an actual Stack.

Terraform

Terraform is an open-source Infrastructure-as-Code tool created by HashiCorp. The big idea behind Terraform was to create a tool and language that was human-readable. Before Terraform, most of the Infrastructure-as-Code tools used configuration languages like JSON and YAML. Although both work great, the tech community wanted a language that they could use that was not only easier to read, but more efficient. Because of this need, HashiCorp created the HashiCorp Configuration Language (HCL).

Terraform uses block syntax with key/value pairs called parameters. For example, below is a piece of code that creates an S3 bucket in AWS. It calls upon the aws_s3_bucket resource, which is the API call to the S3 public API. It then has to key/value pairs (parameters) – bucket and acl. Once the code is run, an S3 bucket called levan-s3-bucket would be created.

resource "aws_s3_bucket" "levan" {
  bucket = "levan-s3-bucket"
  acl    = "private"
}

Because Terraform is cross-platform, you can use it with anything that has a public API. In the cloud, on-prem, network services like Cloudflare, and even to order pizza with the Dominoes Terraform provider! One of the coolest things that engineers like about Terraform is that they can create their own Terraform providers for anything that has a public API. All Terraform providers are written in Go.

Comparison

In this section, you’re going to learn how you can compare both Terraform and CloudFormation for not only production workloads, but to ensure you’re choosing the right option for any environment. When picking a new tool, there are several factors that go into ensuring that it makes sense to implement.

Ease of use

The whole reason that HashiCorp created HCL in the first place was because of ease of use. Configuration languages like JSON and YAML aren’t exactly difficult once you get used to using them, but they are always cumbersome. If you look at the code examples in the sections What is Terraform and What is CloudFormation, you’ll notice that the code is creating the same service – an s3 bucket. However, you’ll see that it takes far more code to create an S3 bucket using JSON and YAML vs using Terraform. JSON and YAML, although great languages, will always be far more verbose than necessary compared to Terraform.

Speed

When it comes to speed, it’s not really about how long it takes code to run in the case of infrastructure code. Both can technically run at the same speed, so it’s more about interacting with the API. If there’s a network delay, for example, the code will take longer to execute from your local computer if you’re using Terraform. If you’re using CloudFormation, the code runs from AWS, so you typically don’t see speed issues. In either case, the speed to run the code isn’t really a factor.

The largest factor when it comes to speed is when CloudFormation or Terraform gets the available resources to interact with services. Believe it or not, Terraform, up until this point, has been faster at supporting new AWS features via infrastructure code.

State Management

State, in an infrastructure code perspective, means the file with the metadata of the environment that you’re automating with Infrastructure-as-Code. CloudFormation and Terraform perform state management a little differently.

With Terraform, you can store a state file either on a local computer or in a remote storage location, like an S3 bucket or an Azure Storage Account. The option solely depends on where you’re running the code from. You can also store a State file in Terraform Cloud.

CloudFormation, on the other hand, automatically stores the state file for you. You don’t have to do anything extra for this to occur. CloudFormation stacks automatically check the provisioned systems and services to detect and configuration drift or other issues.

Cost

When thinking about cost, both solutions themselves are free to use. The only cost that will be associated is the cost of the service. For example, if you’re creating an EC2 instance with Terraform or CloudFormation, you’re not being charged to write the code and run it. You’re being charged for the actual service itself.

Terraform does have a product called Terraform Cloud, but that’s not needed to use and write Terraform code. It’s just an additional enterprise feature if you want a centralized location to run Terraform code and store State file.

Declarative and Immutable

In programming, there are a few key paradigms and protocols on how you define applications. Two of them are the declarative/imperative model and the immutable/mutable model, and both of these protocols are crucial to Infrastructure-as-Code. Declarative expresses logic without describing its workflow. You write the code on what you want the program to do, but you don’t tell it how to do it. Imperative uses statements, which end up changing a program’s state. You tell the program exactly what you want to happen, step-by-step, telling it exactly how to do it.

Declarative is like going to get your haircut, paying for it, but not saying exactly what hairstyle you want. You just tell the stylist that you want to look good and you don’t care how he or she gets you there. Imperative is telling the hairstylist exactly how to cut your hair, snip by snip, to get you to the end result of looking good.

Terraform and CloudFormation is declarative.

The next paradigm is immutable and mutable. Immutable means the object can’t be changed after it’s created, and mutable means the object can be changed after it’s created.

Terraform and CloudFormation is both considered immutable.

Mutable Infrastructure-as-Code

As mentioned in the previous section, They both do have resources that can technically be considered mutable. It all depends on what API Terraform and CloudFormation are interacting with. However, by nature, they are both immutable. When the question well, which resources are immutable and which are mutable? the answer typically depends on the resource that you’re interacting with.

There isn’t a list of resources anywhere that are immutable vs mutable in Terraform, but here’s how you can know:

If the terraform plan command shows a change, that means the resource is being updated (mutable).

If the terraform plan command shows a destroy and/or new, that means the resource is being re-created.

The Comparison Summed Up

Figuring out when and when not to use Terraform or CloudFormation will solely depend on two factors:

  • Ease of use
  • Where you’re deploying

When it comes to ease of use, Terraform is the clear victor. If an engineer can choose between HCL, which was specifically made to be human-readable, vs JSON or YAML, HCL will always be a clear choice. It’s not only easier to read, but much easier to write. HashiCorp Configuration Language is not only the victor with Terraform, but it’s also the direction that a lot of organizations are moving in. For example, Azure Bicep, a new configuration tool, is written in a form of HCL. Microsoft did that so they could help out the folks that didn’t want to use ARM templates because they’re too verbose (written in JSON).

Another huge factor in choosing between Terraform and CloudFormation is the fact that CloudFormation is for AWS only. Because of that, you can’t use it for any other cloud, on-prem environment, or virtualization platform. If an organization is planning on going with a hybrid cloud or multi-cloud model, CloudFormation definitely shouldn’t be the first choice.

Closing Thoughts

Regardless of which option you go with, Terraform or CloudFormation, you’ll always be following the same concept – create systems and services in an automated, repeatable fashion. The goal is to remove manual efforts from a workflow and instead, create automated environments. That way, engineers no longer need to manually click around a UI to create, update, or delete components. Instead, they can focus their time on automating the efforts and performing value-based tasks.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes:

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>