Terraform 0.14 – Quick And Easy

Terraform 0.14 – Quick and Easy!

This blog posts covers HashiCorp’s Terraforma simple and essential tool for safely and predictably creating, updating, and improving infrastructure. This is a comprehensive Terraform blog post for both operations, platform, and DevOps engineers. If you have never used Terraform, this is the place to start.

This blog post does not assume you’re already an expert coder or expert system administrator— general knowledge of programming, the command line, and server-based software (e.g., websites) is sufficient. By the end of this blog post, you will have a reliable grasp of one of the most significant aspects of modern development and operations: managing infrastructure as code.

To obtain a strong understanding of terraform, you have to write real Terraform code. Just how to have a strong understanding of how to cook a turkey, you should really cook a turkey, not just read the receipt.  So, to be fair to you, this blog post will show you how to write, run and execute real Terraform code in a real infrastructure environment.

This blog tutorial is going to be compose of two parts:

  • Part 1: Terraform Introduction
  • Part 2: Hands on Keyboard! Create your first EC2 instance using Terraform

To provision your first EC2 instance, you will need the following:

  • WIFI and SSH-enabled Terminal computer
  • Modern Internet browser such as Firefox, Chrome, or Safari
  • Have an AWS account to build your infrastructure. If you don’t have one, you can create an AWS account by visiting this link.

Part One : Terraform Introduction

Terraform is an Open Source Tool that allows you to define your infrastructure as code using a simple, declarative programming language, and to deploy and manage that infrastructure as code.

Terraform is at the heart of DevOps practices. With Terraform you can automate the deployment of your infrastructure to perform automation testing, continue integration and continuous delivery. 

Infrastructure as Code (IasC) is a method of provisioning and managing your infrastructure through the use of code, rather than by a manual process.

Terraform allows you to define  your infrastructure in written scripts also known as source code. 

It can provision and manage infrastructure for more than 100 providers.  The complete list of providers it supports can be found here: List of Providers that Terraform support 

Terraform works by invoking the API of the providers it supports to provision the infrastructure and different providers require different API calls and implementation. For example, to provision an AWS EC2 instance, it calls this AWS EC2 API Call:

resource "aws_instance" "web" { 

ami = data.aws_ami.ubuntu.id 
instance_type = "t3.micro" 
tags = { 
   Name = "HelloWorld" 
   } 

}

To provision a virtual machine from a VMwaare vSphere infrastructure, it calls this vSphere  API Call:

resource "aws_instance" "web" { 

ami = data.aws_ami.ubuntu.id 
instance_type = "t3.micro" 
tags = { 
   Name = "HelloWorld" 
   } 

}resource "vsphere_virtual_machine" "vm" { 

name = "terraform-test" 

resource_pool_id = "${data.vsphere_compute_cluster.cluster.resource_pool_id}" datastore_id = "${data.vsphere_datastore.datastore.id}" 
num_cpus = 2 
memory = 1024 
guest_id = "other3xLinux64Guest" 

network_interface { 
   network_id = "${data.vsphere_network.network.id}" 
   } 

disk { 
   label = "disk0" 
   size = 20 
   } 

}

What is so exciting about Terraform within DevOps?

Using Terraform empowers us to automate the process of provisioning our infrastructure within our DevOps Pipelines. This means that we can create our infrastructure, configure it, manage it and destroy all within our DevOps pipelines. In other words, automatically! This is very valuable because we have the ability to use infrastructure as code.

Building our Infrastructure as Code has many benefits, including:

  • Cost savings 
  • Consistently across environments
  • Increased efficiency

Terraform Language

Another advantage of using Terraform is it’s declarative language.  Also known as non-procedural or very high level language. Declarative programming languages specify what needs to be done rather than how to do it.

For example, If you need to create an EC2 instance on a new Subnet, using declarative language you can script that you need an EC2 (X) instance and an Subnet (Y). And you can specify that the EC2 (X) instance should provision within subnet (Y) even before provisioning subnet (Y).  With other non-declarative languages, you have to script your infrastructure code in the order that it needs to be created. In other words, you have to script the dependencies within infrastructure  objects. 

The biggest advantage of the declarative language is that it is easier to create and debug because you do not need to code the dependencies between the objects that make up your infrastructure, you just need to know their relation. Meaning you need to code that your EC2 instances need to be run within a subnet. Another advantage of declarative format is that it makes your infrastructure easy to organize in a way that you can focus on the objects that you are currently building or debugging. We will see an example on this later when we create our code. 

Terraform Syntax

Terraform has several declaration types

  • Data
  • Providers
  • Resource
  • Variables

DATA

This declaration type retrieves resources from the existing infrastructure. 

For example, if you just need to create an EC2 instance in your existing AWS VPC, the “data” type element, can retrieve the AWS VPC in your AWS Environment by using its AWS ID.

This is useful because once you have your VPC ID, you can specify within your infrastructure code to provision a new EC2 instance in the VPC that you just retrieve from your AWS environment. Instead of creating a complete new VPC just to provision your instance. 

It is important to note that although the “data” type retrieves the infrastructure information from your existing infrastructure environment, it does not “own” those infrastructure objects. Meaning, when you issue a Terraform “destroy” command, it will delete the EC2 instance from your infrastructure environment that you provision with Terraform, but not the VPC that you retrieve with your “data” declaration. The reason for that is that items retrieved from the “data” declarations are not provisioned by Terraform, therefore, Terraform does not delete them. 

Here is an example of how you would declare a data type to retrieve an AMI image and a VPC infrastructure object to use for your EC2 instance:

Filename: data.tf

data "aws_ami" "instance_ami" {
  owners = ["self"]
  filter {
    name = "name"
    values = [var.my_image_id]
  }
}


data "aws_vpc" "selected" {
  id = "vpc-442aaf21"
}

PROVIDERS

Identify the provider of the infrastructure. Terraform support API calls for more than 100 providers. 

For example, one of those providers is AWS. So, in order to provision infrastructure in AWS, we need to specify that the provider is AWS. Likewise, if the provider is vSphere, then you would specify the provider as the vSphere.  

It is important to note that because each infrastructure provider implements their own API, each provider has different API implementations. What this means is that the terraform code to provision an EC2 AWS Instance, will not work to provision an vSphere VM. As you might remember earlier, I mention that Terraform invokes the infrastructure provider API to provision the infrastructure within that provider. Different  API methods require different input and output parameters, therefore when you build the terraform code, the infrastructure code is specific to each provider’s API.

Filename: providers.tf


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

RESOURCE

Declaration to provision an infrastructure component within the specified provider.  This is the most widely used terraform declaration. This is how everything is created within terraform.  

For example, you can create an AWS EC2 instance, VPC, Subnets, Load Balancers, AutoScaling, TargetGroups and much more. In essence, terraform can create any resource the AWS API supports. Similar to how terraform can build any resource that the vSphere API supports. After all, Terraform works by invoking the provider’s APIs. 

It is important to note that this will be a resource that terraform will “own”, what this means is that when you execute the Terraform “destroy” command, Terraform  will destroy this infrastructure object. As opposed to infrastructure objects retrieve via the Terraform declaration “data”.

VARIABLES

These are the most common declaration types with software, script or infrastructure code programming. Variables are used to represent data, such as an VPC ID, or an EC2 instance ID or simply names that we want to use for our infrastructure objects. 

Variables enable your code to be flexible because they can be set before or during the executing of your code. Once a variable “value” is set, the variable is replaced with the value or the real data during the code execution. This makes it possible for the same program to process different sets of data without changing your code. 

Every variable has a name  called “variable name” and a data type. A variable data type indicates the type of information the variable holds. 

For example: 

  • A variable type “string” holds characters, letters and strings. 
  • A variable type: integer, holds numbers.
  • A variable type: list , holds a list of items.

Here are one examples of how variables are define in terraform:

Filename: variables.tf

variable "region" {
  description =  "Identify the AWS region to use"
  type = string
  default = "us-west-1"
}
variable "vpc_id" {
  description =  "VPC to use for testing."
  type = string  
  default = "vpc-442aaf21"
}

variable "created_by" {
  description =  "Tag to identify who created the resource"
  type = string
  default = "DevOpsToday.net"
}

In the first variable declaration, the variable name is “region”. It’s description or purpose is to  “Identify the AWS region to use” It’s type is a string and the default value to use  if this variable is not defined within the terraform code files, is the zone: us-west-1. The similar concept follows for the remainder declarations.

Terraform Files Extension

Terraform Files Extension: .tf

Terraform files have a “.tf” extension. This “.tf” file extension tells terraform that this is a file that it should execute when you issue a Terraform “execute” command. 

Files that do not have the “.tf” extension are ignored by Terraform and not executed.  This file convention makes it easy to organize and debug your infrastructure code. 

For example:

You can include all of your “Data” resources in a file and name it “Data.tf”

You can include all of your “VPC” resources, such as the VPC ID, Subnets and Routing table declarations  in a file and name it “VPC.tf”

You can include all of your “EC2” code declarations in a file named “EC2.tf”


Filename: ec2.tf

resource "aws_security_group" "web_security_group" {
  vpc_id=data.aws_vpc.selected.id
  name = "access-https-${var.ec2_instance_security_group}"
  ingress {
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }
 
   tags = {
    created_by   = var.created_by
  }

}


resource "aws_instance" "test-instance" {
  ami           = data.aws_ami.instance_ami.id
  instance_type = "t2.micro"
  key_name   = "DevOps"
  vpc_security_group_ids = [aws_security_group.web_security_group.id]

  tags = {
    created_by   = var.created_by
  }
}

This file convention make it easier to debug terraform code, because if you are still coding your VPC environment, and you are not ready to work on coding your EC2 instance, you can just change the file extension of your EC2.tf to EC2.ignore or EC2.anything and terraform will not execute your EC2 file, even if it includes terraform code.

Terraform Files Extension: .tfvars

Another terraform file extension is .tfvars. This file extension identifies a terraform file that includes variable values. As we said earlier, variables hold specific data that we want to use during the code execution.

Filename: terraform.tfvars

region = "us-west-1"
my_image_id = "devops-import-server-1"

Terraform Files Extension: .statefile

Another terraform file extension is .tfvars. This file extension identifies a terraform file that includes variable values. As we said earlier, variables hold specific data that we want to use during the code execution.

Terraform Commands

In this section, we will discuss the different Terraform commands and how to use them to create, manage and update your infrastructure.

Terraform init: (terraform init)

This is the first command you have to execute. It initializes the terraform working directory containing your terraform configuration files. Based on the provider that you specify in your configuration files, it downloads the associated APIs. 

For example: If  you specified that the provider is “aws” in your provider.tf file, as in the example above, then when you execute the command “terraform init”, it will automatically download the API support files for the AWS API. 

If you fail to run this command, terraform will fail to provision any infrastructure because it has no available APIs to call.

Terraform Plan: (terraform plan)

This command creates an execution plan describing what it will build to reach the desired state based on the terraform configuration files. This command evaluates the configuration files for execution readiness. It verifies that the syntax is correct and it provides an execution plan. It does not provision the infrastructure objects, instead it creates a plan that can be reviewed and evaluated by others, or simply executed with the terraform apply command. 

This command is useful if other team members need to review the plan before executing, or if you want to ensure that your code is ready to be executed and does not have any syntax errors.

Terraform Apply: (terraform apply)

This command provisions your infrastructure objects in the specified providers environment. This command uses the terraform plan created by the “terraform plan” command.

Terraform “apply” evaluates the current state of the target infrastructure environment before creating new infrastructure objects. If the desired infrastructure objects have not been created before, it creates new ones. On the other hand, if the infrastructure objects requested in your configuration files were created on a previous run, it destroys them and re-creates them anew using the latest configuration files. In this manner, Terraform ensures that the deployed infrastructure matches the declared infrastructure in the code.

For example:

Let’s say you already created a new test-instance using this configuration:

resource "aws_instance" "test-instance" {
  ami           = data.aws_ami.instance_ami.id
  instance_type = "t2.micro"
  key_name   = "DevOps"
  vpc_security_group_ids = [aws_security_group.web_security_group.id]

  tags = {
    created_by   = var.created_by
  }
}

And now you changed the configuration file to  instance_type = “t2.medium” because it turns out you need a bigger EC2 instance. Now, you configuration file looks like this:

resource "aws_instance" "test-instance" {
  ami           = data.aws_ami.instance_ami.id
  instance_type = "t2.medium"
  key_name   = "DevOps"
  vpc_security_group_ids = [aws_security_group.web_security_group.id]

  tags = {
    created_by   = var.created_by
  }
}

Terraform apply will evaluate the current state of the infrastructure, learn that there is already an EC2 name “test-instance” up and running. So, instead of creating a new “test-instance”, it will destroy the current one and recreate it using the new configuration, which uses a bigger instance type.

Terraform Destroy: (terraform destroy)

This command, like the name says: it destroys the infrastructure objects owned by terraform. In other words, the infrastructure objects that were provisioned with the terraform “apply” command. 

And that concludes Part One! Part Two is all about hands-on-keyboard, where you will be creating your first EC2 instance. I hope you enjoy this Terraform introduction.

Leave a Comment

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

8 + 2 =