Getting Started With Terraform For Infrastructure Provisioning 🛠️
Tags: devops, terraform, cloud, architecture
Published: 2024-06-19 16:59:59

Infrastructure as Code (IaC) is a modern approach to managing and provisioning computing resources through machine-readable configuration files rather than physical hardware or interactive configuration tools. This method allows for more consistent and scalable infrastructure management, enabling automation and reducing the risk of human error.


### Why Terraform?

Terraform, developed by [Hashicorp](https://www.hashicorp.com/) is one of the most popular IaC tools due to its open-source nature, flexibility, and support for multiple cloud platforms. It lets users define and provision data center infrastructure using a high-level configuration language.

In this article, you will learn how to use Terraform as an IaC tool.


## Understanding the Basics

### What is Terraform?

Terraform is an open-source Infrastructure as Code (IaC) tool that allows you to define and provision infrastructure using a high-level configuration language called **Hashicorp Configuration Language (HCL). **Terraform manages resources such as virtual machines, storage, and networking for various cloud providers through a **_declarative approach_**, where you simply define the desired state of your infrastructure and Terraform ensures that it matches that state.


### Overview of Architecture and Workflow


![Terraform Workflow](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/iczoijy7aexoy49wr30d.png)


<strong><center>FIG 1.1</center></strong>


![Terraform Architecture](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zgzumg4clh46feqpvzbt.png)


<strong><center>FIG 1.2</center></strong>

A typical Terraform workflow as shown in ***FIG 1.1*** involves:



1. Writing configuration files to define the desired state.
2. Initializing the configuration directory with `terraform init`.
3. Creating an execution plan with `terraform plan`.
4. Applying the changes with `terraform apply`.
5. Managing the infrastructure state and making updates as needed.
6. Destroying the infrastructure with `terraform destroy` if required.


### Key Components


#### Providers

Providers are responsible for managing the lifecycle of resources. They offer a set of resources and data sources that Terraform can manage. Each provider requires a configuration to define the credentials and regions where Terraform would operate. Examples of providers include AWS, Azure, GCP, etc.

Example configuration

```hcl
provider "aws" {
  region = "us-east-1"
}
```

#### Resources

Resources are the fundamental building blocks of Terraform configurations. They represent components of your infrastructure, such as virtual machines, databases, or networking components. Each resource is defined with a type, name, and a set of properties.

Example resource definition:

```hcl
resource "aws_instance" "example" {
  ami           = "ami-0c55b159cbfafe1f0"
  instance_type = "t2.micro"
}
```

#### Modules

Modules are reusable configurations that help organize and structure your code. They allow you to group multiple resources and encapsulate complex infrastructure patterns.

Example module usage

```hcl
module "vpc" {
  source = "terraform-aws-modules/vpc/aws"
  version = "2.77.0"
  name = "my-vpc"
  cidr = "10.0.0.0/16"
  azs             = ["us-east-1a", "us-east-1b"]
  public_subnets  = ["10.0.1.0/24", "10.0.2.0/24"]
  private_subnets = ["10.0.3.0/24", "10.0.4.0/24"]
}
```


## Setting up your Environment

Visit the [Terraform downloads page](https://developer.hashicorp.com/terraform/install) and download or install the version of Terraform concerning your operating system.

For Linux systems (Ubuntu/Debian) follow this:

**STEP 1**

```shell
wget -O- https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg

echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list

sudo apt update && sudo apt install terraform
```

**STEP 2**

Verify the installation with this

```shell
Terraform –version
```

**STEP 3**

For this article, we will be provisioning our infrastructure using AWS.

Use [this link](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html) to install the **_aws cli_** for your operating system

**STEP 4**

Run `aws configure` to set your variables after installing the aws cli. Terraform needs to be able to use these credentials for infrastructure provisioning. 


## Writing Your First Configuration


### Basic Configuration File

Terraform configuration files are written in Hashicorp Configuration Language (HCL). The basic structure as shown in **FIG 1.2 **above includes


#### **Provider Configuration:**

Specifies the cloud provider and its settings.

```hcl
provider "aws" {
  region = "us-east-1"
}
```


#### **Resource Definition:**

Declares the resources to be managed.

```hcl
resource "aws_instance" "example" {
  ami           = "ami-0c55b159cbfafe1f0"
  instance_type = "t2.micro"
}
```

**_resource:_** This tells Terraform that this is a resource block.

**_aws_instance:_** This is the actual resource we want to provision in aws.

**_example:_** This is a specific name given to the resource so that we can reference it elsewhere in our terraform code


#### Variables:

Defines variables for dynamic values.

```hcl
variable "ami_name" {
  type        = string
  description = "The name of the machine image (AMI) to use for the server."
  default = “ubuntu-local”
}
```

**_variable:_** This indicates a variable block.

**_ami_name:_** This is the name of the variable. Hence, it can be referenced anywhere in the terraform code.

**_description:_** The description should explain the variable and what value is expected.

**_default:_** If present, the variable is considered optional and the default value will be used if no value is set when calling the module or running Terraform.


#### Outputs: 

Specify outputs to display after applying the configuration.

```hcl
output "instance_id" {
  value = aws_instance.example.id
}
```


### Writing your first `main.tf` file

Create a directory for your terraform config files

```sh
mkdir terraform-practise
```

Create the `main.tf` file in this directory. This file would contain your Terraform configuration

```hcl
provider "aws" {
  region = "us-east-1"
}

resource "aws_instance" "example" {
  ami           = "ami-0c55b159cbfafe1f0"
  instance_type = "t2.micro"
}
```

Next, we initialize the directory

```sh
terraform init
```


### Understanding the Initialization Process and Its Output


* **Provider Plugins:** Terraform downloads the necessary provider plugins specified in your configuration. In our case aws.
* **Backend Initialization:** Terraform sets up the backend for storing the state of your infrastructure.
* **Directory Structure:** Terraform creates a `.terraform` directory to store the provider plugins and state files.

A sample output would look like this

```sh

Initializing the backend...

Initializing provider plugins...

- Finding latest version of hashicorp/aws...

- Installing hashicorp/aws v3.27.0...

- Installed hashicorp/aws v3.27.0 (signed by HashiCorp)

Terraform has been successfully initialized!

```


## Terraform Commands

To view the entire list of commands, do `terraform –help`. Here are some commands and their uses


* <strong><code>terraform init</code></strong>: Initializes a Terraform working directory by downloading the necessary provider plugins and setting up the backend for state management. This should be run first before any other terraform command.
* <strong><code>terraform fmt</code></strong>: This command is used to ensure the configuration files are properly formatted and easy to read. It helps maintain a consistent style across the files.
* <strong><code>terraform validate</code></strong>: Checks whether the configuration is valid.
* <strong><code>terraform plan</code></strong>: This command is used to preview the changes that Terraform will make to the infrastructure. It helps in verifying the resources that will be created, modified, or destroyed.
* <strong><code>terraform apply</code></strong>: This command executes the actions proposed in the execution plan. It prompts for approval before making any changes unless the <code>-auto-approve</code> flag is used.
* <strong><code>terraform destroy</code></strong>: This command is used to terminate and remove all resources defined in the Terraform configuration. It prompts for approval before making any changes unless the <code>-auto-approve</code> flag is used.
* <strong><code>terraform show</code></strong>: This command is used to inspect the current state of the infrastructure managed by Terraform. It can also show the details of a specific plan file.
* <strong><code>terraform refresh</code></strong>: This command reconciles the state file with the real-world resources to detect any drift between the two.


## Practical Example: Setting Up an EC2 Instance with a VPC

In this example, we will provision an EC2 instance within a VPC. This configuration will include


* Creating a VPC
* Subnets
* An internet gateway
* Route tables
* EC2 instance with appropriate security groups

For this demonstration, we will create a new folder in our main directory named ***learn-terraform-aws.***

```sh

mkdir learn-terraform-aws

cd learn-terraform-aws
```

We would then create our `main.tf` file responsible for holding our terraform configurations

```shell
touch main.tf
```

First, we will define the VPC resource in the `main.tf` file

```hcl
provider "aws" {
  region = "us-east-1"
}

resource "aws_vpc" "main" {
  cidr_block = "10.0.0.0/16"

  tags = {
    Name = "main-vpc"
  }
}
```

Next, we define the public and private subnets within the VPC

```hcl
resource "aws_subnet" "public" {
  vpc_id            = aws_vpc.main.id
  cidr_block        = "10.0.1.0/24"
  availability_zone = "us-east-1a"
  tags = {
    Name = "public-subnet"
  }
}

resource "aws_subnet" "private" {
  vpc_id            = aws_vpc.main.id
  cidr_block        = "10.0.2.0/24"
  availability_zone = "us-east-1a"
  tags = {
    Name = "private-subnet"
  }
}
```

After this, we create our internet gateway to allow access to the VPC

```hcl
resource "aws_internet_gateway" "gw" {
  vpc_id = aws_vpc.main.id
  tags = {
    Name = "main-gateway"
  }
}
```

Define a route table and associate it with the public subnet

```hcl
resource "aws_route_table" "public" {
  vpc_id = aws_vpc.main.id
  
  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.gw.id
  }
  tags = {
    Name = "public-route-table"
  }
}

resource "aws_route_table_association" "public" {
  subnet_id      = aws_subnet.public.id
  route_table_id = aws_route_table.public.id
}
```

Now we create our EC2 Instance

```hcl
resource "aws_instance" "web" {
  ami           = "ami-0c55b159cbfafe1f0"
  instance_type = "t2.micro"
  subnet_id     = aws_subnet.public.id
  tags = {
    Name = "web-server"
  }
}
```

You would realize that we are simply declaring the state of our infrastructure like we would if we were to go to the AWS console. Also, The specific name of the resource blocks for our file is being reused as and when necessary. For instance, in the `aws_instance` resource, we made use of `aws_subnet.public.id` which is a named resource in our configuration telling terraform which resource to get this id from.


### Variables and Outputs

Add variables and outputs to make the configuration more flexible and provide useful information after the infrastructure is provisioned.

Create a `variables.tf` file and add this code

```hcl

variable "aws_region" {

  description = "The AWS region to deploy resources"

  default     = "us-east-1"

}

variable "instance_type" {

  description = "Type of EC2 instance"

  default     = "t2.micro"

}

```

Now create an `outputs.tf` file and add this code as well

```hcl

output "instance_id" {

  value = aws_instance.web.id

}

output "public_ip" {

  value = aws_instance.web.public_ip

}

```

Now ensure that all three files are in the same directory and run the following commands


STEP 1. Initialize the directory

```shell
terraform init
```

STEP 2. Create an Execution Plan

```shell
terraform plan
```

STEP 3. Apply the configuration

```shell
terraform apply
```

STEP 4. After `terraform apply` is complete, you will see the instance ID and public IP of the newly created EC2 instance.


## Reference Links
* [Terraform Documentation](https://developer.hashicorp.com/terraform/docs)

* [Infrastructure as Code (IaC)](https://aws.amazon.com/what-is/iac/)


## Conclusion
In this article, we've explored the foundational aspects of using Terraform for infrastructure provisioning. From understanding the core concepts and basic commands to diving into practical scenarios like setting up an EC2 instance with a VPC, we've covered essential topics that will help you get started with Terraform.

Reach out to me via [LinkedIn](https://www.linkedin.com/in/angelotheman) , [X](https://x.com/angelotheman) via [Email](mailto:kwabenaatwumasi@gmail.com).


Happy Learning 🚀