Introduction to Terraform

Evan Greer
6 min readAug 3, 2020

--

This week I explored Terraform as part of my exploration of development and operations topics. Terraform is a tool for automating the deployment of infrastructure across multiple providers. In this blog post, I will explain the basics of Terraform and how I deployed my first configuration file.

Introduction

Terraform allows you to build, change and version infrastructure. Configuration files describe what components are needed to run an application. Terraform generates an execution plan describing what it will do to reach the desired state and executes it to build the desired infrastructure. As the configuration changes, Terraform is able to determine what changed and create incremental execution plans which can be applied.

Infrastructure as Code

Terraform uses infrastructure as code. There are several benefits of doing this. Infrastructure as code allows for automated deployment, consistent environments, a repeatable process, reusable components, and documented architecture.

Infrastructure as code refers to using software to provision infrastructure. The goal is to achieve consistent and predictable environments. Infrastructure looks exactly as the configuration file specifies. Infrastructure as code should be stored in a versioned source code repository. Terraform is declarative in that it has a predefined way of implementing infrastructure based on inputs. Terraform is idempotent in that nothing will change in the environment if a configuration does not change and is applied. Terraform is a push model in that the configuration is pushed to the target environment.

Deploying a Terraform Configuration

In this section, I am going to go through the example of deploying a Terraform configuration from the Terraform: getting started course by Ned Bellavance. Terraform automates infrastructure. Automation and maintenance of infrastructure is built on several areas. As an initial example, we are going to be focusing on one area, the provisioning of resources using Terraform.

Terraform is comprised of several components. The terraform executable is written in Go and is downloaded from the Terraform website. Configuration is contained in terraform files. Terraform combines the terraform files in a directory into a single configuration. Terraform uses plugins to interact with providers and provision resources from these providers. Terraform has a state file that has the current state of the configuration. When the environment is updated, Terraform compares the new configuration to the Terraform state file. Terraform then makes the changes so that state matches the desired configuration.

To use a provider such as AWS, we need credentials to log in. Terraform provides the ability to store information in variables. The credentials can be stored in variables instead of directly in the configuration files. Credentials is a required property for the provider. The provider is defined using the credentials and the region.

variable "aws_access_key" {}
variable "aws_secret_key" {}
variable "aws_region" {
default = "us-east-1"
}
provider "aws" {
access_key = "var.access_key"
secret_key = "var.secret_key"
region = "var.aws_region"
}

We can get information about resources that exist in AWS. We can pull the data source for information about the provider. A server is going to be created to host the web and database components called a resource. The output file gives the public IP address of the web server.

data "aws_ami" "alx" {
most_recent = true
owners = ["amazon"]
filters {}
}
resource "aws_instance" "ex" {
ami = "data.aws_ami.alx.id"
instance_type = "t2.micro"
}
output "aws_public_ip" {
value =
"aws_instance.ex.public_dns"
}

The terraform configuration file is a .tf file. The configuration file is written in HashiCorp configuration language (HCL), a domain specific language created for HashiCorp products. Variables are first defined as follows:

variable "aws_access_key" {}
variable "aws_secret_key" {}
variable "private_key_path" {}
variable "key_name" {}
variable "region" {
default = "us-east-1"
}

The key name refers to a key pair that exists in AWS so that we can ssh into this instance once its been created. The path to the private key is specified that corresponds to the key pair in AWS. The provider is defined and the access key, secret key, and region are fed to the provider.

provider "aws" {
access_key = var.aws_access_key
secret_key = var.aws_secret_key
region = var.region
}

Next, in the configuration file, we specify the data to be pulled from the provider. We specify using amazon linux for our EC2 instance and want to get the ami (Amazon Machine Image) id that corresponds to the most recent version of amazon linux.

data "aws_ami" "aws-linux" {
most_recent = true
owners = ["amazon"]
filter {
name = "name"
values = ["amzn-ami-hvm*"]
}
filter {
name = "root-device-type"
values = ["ebs"]
}

filter {
name = "virtualization-type"
values = ["hvm"]
}
}

The next section is the resources section. The first resource defined is the aws default vpc resource. This makes use of the default vpc in the region. The next resource defined is the aws security group. This allows us to connect to the instance that is running nginx. This also opens up port 80 to access the web server that going to be running on this instance. The aws security group references the default vpc resource. There are two ingress rules to allow port 22 and port 80 from the outside and one egress rule to allow traffic from the instance to the internet.

resource "aws_security_group" "allow_ssh" {
name = "nginx_demo"
description = "Allow ports for nginx demo"
vpc_id = aws_default_vpc.default.id
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
egress {
from_port = 0
to_port = 0
protocol = -1
cidr_blocks = ["0.0.0.0/0"]
}
}

The aws instance is then defined. The resource type is “aws_instance” and is named “nginx”. The ami is pulled from the data source defined earlier. The connection block allows us to ssh into this resource. The provisioner allows us to remotely execute an inline script.

resource "aws_instance" "nginx" {
ami = data.aws_ami.aws-linux.id
instance_type = "t2.micro"
key_name = var.key_name
vpc_security_group_ids = [aws_security_group.allow_ssh.id]

connection {
type = "ssh"
host = self.public_ip
user = "ec2-user"
private_key = file(var.private_key_path)
}
provisioner "remote-exec" {
inline = [
"sudo yum install nginx -y",
"sudo service nginx start"
]
}
}

The final component is the output. This specifies what we want Terraform to output when the configuration has been instantiated. The value output is the public dns property of the nginx instance created.

output "aws_instance_public_dns" {
value = aws_instance.nginx.public_dns
}

The variables are given values through the .tfvars file. In the .tfvars file, the values are defined for the variables in the configuration file. The aws access key, the aws secret key, the key name, and the private key path are defined as follows:

aws_access_key = ""
aws_secret_key = ""
key_name = "name_of_existing_key_pair"
private_key_path = "private_key_path\private_key_file.pem"

Once the configuration file and variable values are set up, the configuration can be deployed on Terraform. After installation and putting Terraform in the path variable, Terraform is run using “terraform”. If terraform is run with no arguments, it will give a list of potential commands.

Terraform deployment follows a cycle. The first step is to get the AWS provider pluggin. The following command initializes the configuration by looking at the configuration file and downloads the pluggin.

terraform init

The next step is to load the variables from the .tfvars file. We need to specify the file where to store the plan. Terraform plan takes a look at the existing environment, looks at what you want to do in the configuration file, and then figures out what needs to be done to make reality match the configuration.

terraform plan -out m3.tfplan

The plan tells us what exactly it is going to do. The command “apply” is used to create the resources from the plan.

terraform apply "m3.tfplan"

The output is the public dns to connect to the EC2 instance. The current state of the configuration is stored in a state file. The state file is stored locally. The public dns can be used in the browser to confirm the configuration is running on an amazon ami. To destroy the configuration after the configuration is tested, we run the following:

terraform destroy

Conclusion

Terraform is a tool for automating infrastructure deployment. Terraform uses infrastructure as code to allow for repeatable and consistent deployment. Terraform uses a configuration file to define Terraform components use in the infrastructure deployment. I would like to acknowledge the Terraform: Getting Started course by Ned Bellavance on Plural Sight that I used as the basis for this blog post and initial exploration of Terraform.

--

--

Evan Greer
Evan Greer

Written by Evan Greer

Flatiron School Software Engineering Immersive Graduate, Denver, Colorado

No responses yet