Beginner's Guide to Infrastructure as Code with Terraform

Dishank KalraDishank Kalra
12 min read

Who am I and why am I writing about this?

Welcome, you've found your way to my corner of the web, where I'm just starting to dive into Terraform, Cloud Services, and DevOps. I'm that newbie who decided to share my learning journey and spam the internet with yet another tech article (laughter is expected!)

I heard the best way to learn is to teach others. Here I am, turning my notes into something useful for you.

This article is written for someone who is an absolute beginner in the cloud space, Development & Operations (DevOps), or Infrastructure as Code (IaC). I promise to keep it simple, straightforward, and free from fancy jargon that makes you feel like you need a PhD just to understand a single sentence.

Why Use IaC Tools? Seriously, Why Do We Even Have This Stuff?

Remember those times when you were just curious about tech and tried out provisioning free resources on cloud platforms like Amazon Web Services (AWS), Azure, or Google Cloud Platform (GCP)? You probably did this using the web interface provided by these cloud services, commonly known as the cloud console.

To get your first server( or cloud resource) up and running, you clicked few buttons and filled out forms, and voila, your first resource was born(or provisioned). If I tell you there's a way to skip all that manual clicking and save a ton of time. Pretty sweet deal, right? But why bother with yet another tool to learn if we can do things manually?

Imagine you work at a company called “BhaiyaTech Inc." with 100 different teams, each needing separate infrastructure for their development, testing, and production needs. They all rely on the system admin (let's call him Dwight) to set up their environments. Dwight is a skilled admin, but it takes him about an hour to manually provision the infrastructure for just one team. That's a whopping 100 hours of Dwight’s time just to set up one type of account!

Plus, manual processes are prone to human errors and inconsistencies in infrastructure setup. Imagine if Dwight accidentally misconfigures a server or forgets a step in the setup process—it could cause delays and headaches for the entire team. Moreover, this manual setup isn't version-controlled and lacks a history of configurations. So, if a team needs to revert to a previous setup due to an issue, it is going to be very difficult.

What if Dwight could write a script just once that replicates the exact infrastructure –no more tedious clicking, fewer errors, and everyone gets their infrastructure set up faster? This is where Infrastructure as Code (IaC) tools come in handy to create and manage the infrastructure reducing configuration errors, and automating mundane tasks of infrastructure management. This means you can manage your entire infrastructure just like you manage application code.

Infrastructure as Code (IaC)

There are two main approaches to Infrastructure as Code (IaC): "Imperative" and "Declarative."

  • In the Imperative approach, you define your configuration as a series of commands executed in a specific order. This means you have to specify each step to achieve your desired outcome. An example of this is using a Bash script with commands to provision resources through any Cloud provider’s CLI.

  • In the Declarative approach, instead of providing step-by-step instructions, you define the desired state of your infrastructure. You describe the resources you want and their properties in configuration files. Tools that use the Declarative approach include Terraform by HashiCorp, AWS CloudFormation, and Azure Resource Manager.

Terraform uses HashiCorp Configuration Language (HCL) and follows the Declarative approach. This means you specify the desired state of your infrastructure, and Terraform automatically determines the current state using a state file. This state file keeps track of your infrastructure's configuration, making it easier to manage and update. (I will discuss more about state file in an upcoming tutorial)

So, why choose Terraform over other options like AWS CloudFormation and Azure Resource Manager?

  • Cross-Platform Compatibility: Unlike CloudFormation and Azure Resource Manager, which are specific to AWS and Azure, Terraform is cloud-agnostic which means it works with multiple cloud providers, including AWS, Azure, Google Cloud, and more, allowing for greater flexibility.

  • Integration with DevOps Tools: Terraform integrates seamlessly with other DevOps tools like Docker and Kubernetes, making it a versatile choice for diverse environments.

  • Pre-Built Modules: Terraform offers many pre-built modules, which are reusable configurations. These modules save time and effort by providing solutions that can be easily integrated into your infrastructure.

  • Thorough Documentation and Strong Community Support provide extensive resources, plugins, and support.

Terraform Key Concepts

Configuration file

Think of a Terraform configuration file as a recipe for a cake. Just like you have a recipe that tells you the ingredients and steps to make a delicious cake, a Terraform configuration file tells Terraform what resources you need and how to set them up in the cloud.

In the world of Terraform, instead of flour and sugar, you have resources like Virtual Machines, databases, and networks. These recipes are written in files with a .tf extension. These files contain the code that defines your desired infrastructure state.

So, while your cake recipe might not always give you a perfect cake like in the cookbook’s picture, a Terraform configuration file ensures your cloud infrastructure is set up exactly the way you need it, every time you run it.

Provider

In Terraform, a provider is a plugin that allows Terraform to interact with various cloud platforms like AWS, Azure, Google Cloud Platform (GCP), IBM Cloud, and many others. Think of a provider as a bridge between Terraform and the cloud service you want to use. It's the first thing you define in any Terraform configuration file because it tells Terraform where your infrastructure will be created.

In Terraform, a block is a fundamental element used to define various components within a configuration file. Let's look at how to define provider block in a Terraform configuration file.

  1. The block type with red circle is provider block. It is used to define which cloud platform (provider) you want to use.

  2. The string with yellow circle is provider name. Here, "aws" specifies that we are configuring Terraform to interact with AWS.

  3. Within the provider block, you specify configuration parameters specific to the chosen cloud provider. Like in this case, region = "us-west-2" tells Terraform to operate in the US West region of AWS.

Let’s look at examples of defining a cloud provider like AWS, GCP, and IBM Cloud.

# Example-1: AWS Provider
provider "aws" {
  region = "us-west-2"
} 
# Here, the provider block defines AWS as the cloud platform and sets the region to US West. 
# This tells Terraform that you will be creating and managing infrastructure on AWS in this specific region.
# Example-2: GCP Provider
provider "google" {
  project = "my-gcp-project"
  region  = "us-central1"
}
# Here, the provider block defines GCP as the cloud platform, specifies the project name, and sets the region to US Central. 
# This tells Terraform that you will be creating and managing infrastructure on GCP in this specific region with provided project name.
# Example-3: IBM Cloud Provider
provider "ibm" {
  region = "us-south"
}
# Here, the provider block defines IBM Cloud as the cloud platform and sets the region to US South. 
# This tells Terraform that you will be creating and managing infrastructure on IBM Cloud in this specific region.

Resources

A resource is any cloud infrastructure component like a virtual machine, database, or network component that you want to create and manage using Terraform. These resources are defined in Terraform configuration files using resource blocks. Each resource block specifies the type of resource, its configuration, and its desired state.

Let's look at how to define resources in a Terraform configuration file

resource "<PROVIDER>_<RESOURCE_TYPE>" "<RESOURCE_NAME>" {
  <PARAMETER_1> = "<VALUE_1>"
  <PARAMETER_2> = "<VALUE_2>"
  ...
}
  • <PROVIDER>: The cloud provider (e.g., aws, google, ibm).

  • <RESOURCE_TYPE>: The type of resource (e.g., instance, storage_bucket, database).

  • <RESOURCE_NAME>: A name you assign to the resource.

  • <PARAMETER_1>, <PARAMETER_2>, ...: Configuration parameters for the resource.

Let’s look at examples of defining a virtual machine in AWS, GCP, and IBM Cloud.

provider "aws" {
  region = "us-west-2"
}

resource "aws_instance" "example" {
  ami           = "ami-0c55b159cbfafe1f0"
  instance_type = "t2.micro"
}
provider "google" {
  project = "my-gcp-project"
  region  = "us-central1"
}

resource "google_compute_instance" "example" {
  name         = "example-instance"
  machine_type = "f1-micro"
  zone         = "us-central1-a"
}
provider "ibm" {
  ibmcloud_api_key = "your-ibm-cloud-api-key"
  region           = "us-south"
}

resource "ibm_is_instance" "example" {
  name    = "example-vsi"
  profile = "bx2-2x8"
  image   = "ibm-centos-7-9-minimal-amd64-3"
}

How does Terraform Knows Resource Types?

Terraform uses providers to abstract the underlying cloud provider's API meaning Terraform uses providers to understand how to create and manage resources for different cloud services. Think of a provider as a translator that tells Terraform how to talk to a specific cloud provider.

For example, in AWS, a virtual machine is called an EC2 instance, while in IBM Cloud, it's called a Virtual Server Instance. By specifying the provider at the start of the file, Terraform knows how to map each resource type to the correct API calls for that provider.

Variable

Just like in any programming language, variables in Terraform are used to take input from the user or display results of the variable. This makes our infrastructure code more flexible and reusable.

Variable Block

To take values that can be passed into your configurations, Terraform uses a variable block. The variable block is where you define a variable that Terraform can use as input at runtime to customize deployments. Here's how you define a variable block in Terraform:

variable "<VARIABLE_NAME>" {
  description = "<DESCRIPTION>"
  type        = <TYPE>
  default     = <DEFAULT_VALUE>
}
  • <VARIABLE_NAME>: Provide name of the variable

  • <DESCRIPTION>: Provide a clear explanation of what the variable is used for. This is especially useful for documentation purposes and for anyone else who might be reading or using your Terraform configuration.

  • <TYPE>: The type parameter defines the data type of the variable. Terraform supports several types, including string, number, bool, list, and map

  • <DEFAULT_VALUE>: The default parameter specifies a default value for the variable. If no value of variable is provided by the user at runtime, Terraform will use this default value. This makes your configuration more robust by providing fallback values.

Let's look at an example of variable block where we define a variable instance_type with a description, type, and default value.

variable "instance_type" {
  description = "Type of EC2 instance"
  type        = string
  default     = "t2.micro"
}

Output Block

To share the value of specific parameters of the infrastructure that has been successfully created by Terraform, we use the output block. This block helps you print the result of your configurations. It's like the variable block, but instead of defining inputs, you’re defining outputs to be display information.

In this example, the output block will display the ID of the EC2 instance that was created. This can be useful for referencing these values later or for use in other Terraform modules.

output "instance_id" {
  description = "The ID of the EC2 instance"
  value       = aws_instance.my_instance.id
}

Organizing Variables and Outputs

You can define these input and output variables in a single configuration file but for better organization, you can create separate dedicated configuration files named variables.tf and outputs.tf. Terraform automatically recognizes and processes variables defined in variables.tf and displays output values defined in outputs.tf as part of its workflow.

Terraform Lifecycle

When it comes to provisioning infrastructure, three magical words aren't "I love you"—they're "init, plan, apply." These commands, along with destroy form the core of the Terraform lifecycle, enabling you to create, manage, and delete your infrastructure effortlessly. Let’s dive into each of these commands and understand their roles.

Initializing Your Environment

The journey begins with terraform init. This command sets up the Terraform environment, ensuring that everything is in place for your infrastructure as code (IaC) operations. It installs the necessary modules and providers required to manage your desired resources.

In this output, Terraform fetches and installs the AWS provider plugin, preparing your environment to interact with AWS resources.

Initializing the backend...

Initializing provider plugins...
- Finding latest version of hashicorp/aws...
- Installing hashicorp/aws v3.42.0...
- Installed hashicorp/aws v3.42.0 (signed by HashiCorp)

Terraform has been successfully initialized!

Visualizing the Future

Next is terraform plan, this command generates an execution plan, showing you what Terraform will do when you apply your configurations. It's like a sneak peek into the future, listing all the infrastructure changes that will be made.

In this output, items with a plus sign (+) will be created, items with a minus sign (–) will be deleted, and items with a tilde sign (~) will be modified in place. This summary allows you to review and confirm the changes before they happen.

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  + create
  - destroy
  ~ update in-place

Terraform will perform the following actions:

  # aws_instance.example will be created
  + resource "aws_instance" "example" {
      + ami           = "ami-123456"
      + instance_type = "t2.micro"
      ...
    }

Making It Real

With your plan in hand, it’s time to execute it using terraform apply. This command applies the changes defined in your plan, provisioning the infrastructure in your cloud provider. This is where the magic happens—your configurations come to life.

In this output, Terraform prompts for your confirmation before proceeding, then creates the resources as specified in your plan.

Terraform will perform the following actions:
  + aws_instance.example

Plan: 1 to add, 0 to change, 0 to destroy.
Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

aws_instance.example: Creating...
aws_instance.example: Creation complete after 42s [id=i-1234567890abcdef0]

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

Cleaning Up

Finally, when you no longer need your infrastructure, terraform destroy comes into play. This command decommissions all the resources that have been provisioned, tearing down your infrastructure safely.

Similar to terraform plan, this command first generates a plan for resource destruction. It then asks for your confirmation before proceeding to dismantle the infrastructure, ensuring that nothing is removed accidentally.

These commands together form a robust lifecycle, making infrastructure management as simple as saying "I love you" to your cloud resources.


Grab your coffee and terraform your way back here for the next blog or tutorial, where we'll dive into real infrastructure. That's all for now from this blog — thanks for reading, and see you soon!

12
Subscribe to my newsletter

Read articles from Dishank Kalra directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Dishank Kalra
Dishank Kalra