PoshJosh's Blog

Terraform - an introduction

October 02, 2019

What is Terraform?

Terraform is used to create, manage, and update infrastructure resources such as physical machines, VMs, network switches, containers, and more. Almost any infrastructure type can be represented as a resource in Terraform.

Install Terraform

To install Terraform, find the appropriate package for your system and download it. Terraform is packaged as a zip archive.

After downloading Terraform, unzip the package. Terraform runs as a single binary named terraform. Any other files in the package can be safely removed and Terraform will still function.

The final step is to make sure that the terraform binary is available on the PATH. See this page for instructions on setting the PATH on Linux and Mac. This page contains instructions for setting the PATH on Windows.

After installing Terraform, verify the installation worked by opening a new terminal session and checking that terraform is available. By executing terraform you should see help output.

Install and Configure AWS CLI

Here are links to install the AWS CLI on various platforms

Quickly Configuring the AWS CLI

For general use, the aws configure command is the fastest way to set up your AWS CLI installation. The following example shows sample values. Replace them with your own values as described in the following sections.

$ aws configure
AWS Access Key ID [None]: AKIAIOSFODNN7EXAMPLE
AWS Secret Access Key [None]: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
Default region name [None]: us-west-2
Default output format [None]: json

When you enter the command aws configure, the AWS CLI prompts you for four pieces of information (access key, secret access key, AWS Region, and output format). These are described in the following sections. The AWS CLI stores this information in a profile (a collection of settings) named default. The information in the default profile is used any time you run an AWS CLI command that doesn’t explicitly specify a profile to use.

Terraform Configuration

The set of files used to describe infrastructure in Terraform is known as a Terraform configuration. You’ll write your first configuration now to launch a single AWS EC2 instance.

A configuration should be in its own directory. Create a directory for the new configuration.

$ mkdir terraform-aws

Change into the directory.

$ cd terraform-aws

Create a file for the configuration code.

$ touch aws-ec2-ubuntu-busybox-httpd.tf

The format of the configuration files is documented here.

Paste the configuration below into aws-ec2-ubuntu-busybox-httpd.tf and save it. Later when you run Terraform, it will load all files in the working directory that end in .tf

/**
 * Run this script thus:
 *
 * terrform init
 * terraform apply -var "server_port=8080"
 */
provider "aws" {
    # profile refers to the user profile we are using to connect to AWS
    # It is available after installing AWS CLI and running command aws configure
    profile = "default"
    region  = "us-east-2"
}

variable "server_port" {
    description = "The port the server will use for HTTP requests"
    type        = number
    default     = 8080
}

variable "aws_ami" {
    description = "The Amazon Machine Image (ami)"
    type        = string
    default     = "ami-0fc20dd1da406780b" # Ubuntu Server 18.04 LTS (HVM) SSD 64bit x86
}

resource "aws_security_group" "security_group_1" {
    name = "webserver-security-group"
    ingress {
        from_port   = var.server_port
        to_port     = var.server_port
        protocol    = "tcp"
        cidr_blocks = ["0.0.0.0/0"]
    }
}

resource "aws_instance" "instance_1" {
    ami           = var.aws_ami
    instance_type = "t2.micro"
    vpc_security_group_ids = [aws_security_group.security_group_1.id]

    # - Write the text "Hello, World" into index/
    # - Run a webserver on specified port using busybox (available on Ubuntu by default)
    # - Use nohup to ensure the web server keeps running even after script exits
    # - & at the end so the web server runs in background; and the script can exit
    # instead of waiting for the webserver
    #
    user_data = <<-EOF
                #!/bin/bash
                echo "Hello, World" > index/
                nohup busybox httpd -f -p "${var.server_port}" &
                EOF

    tags = {
        # The actual name that will be displayed on AWS
        Name = "instance_from_terraform"
    }
}

resource "aws_eip" "ip" {
    vpc      = true
    instance = aws_instance.instance_1.id
}

/**
 * Output variables show up in the console after you run terraform apply
 * command. Users of the terraform code may find this usesful. For example,
 * in this case after deploying a web server, we need an ip address to
 * use in accessing that server
 */
output "public_ip" {
    value       = aws_instance.instance_1.public_ip
    description = "The public IP of the web server"
}

Provider

The provider block is used to configure the named provider, in our case “aws”. A provider is responsible for creating and managing resources. Multiple provider blocks can exist if a Terraform configuration manages resources from different providers. You can even use multiple providers together. For example you could pass the ID of an AWS instance to a monitoring resource from DataDog.

Terraform Providers

provider - profile

The profile attribute here refers to the AWS Config File in ~/.aws/credentials on MacOS and Linux or %UserProfile%\.aws\credentials on a Windows system. It is HashiCorp recommended practice that credentials never be hardcoded into *.tf configuration files. We are explicitly defining the default AWS config profile here to illustrate how Terraform accesses sensitive credentials.

Variable

Terraform allows you to define input variables, which may be referenced throughout the configuration. If you define a variable without a default value, then you need to provide the value when you call apply e.g $ terraform apply -var "server_port=8080". If you do not, you will be prompted by terraform to enter the required variable before proceeding with the apply.

You could also set the variable via an environment variable named TF_VAR_ where is the name of the variable you’re trying to set:

$ export TF_VAR_server_port=8080
$ terraform apply

To use the value from an input variable in your Terraform code, you can use a new type of expression called a variable reference, which has the following syntax:

var.<VARIABLE_NAME> e.g var.server_port

To use a reference inside of a string literal, you need to use a new type of expression called an interpolation, which has the following syntax:

"${...}" e.g "${var.server_port}"

Resource

The resource block defines a piece of infrastructure. A resource might be a physical component such as an EC2 instance, or it can be a logical resource such as a Heroku application.

Output

Output variables show up in the console after you run terraform apply command. Users of the terraform code may find this usesful. For example, in this case after deploying a web server, we need an ip address to use in accessing that server.

Some Terraform Commands

The first command to run for a new configuration — or after checking out an existing configuration from version control — is terraform init. Subsequent commands will use local settings and data that are initialized by terraform init.

Terraform uses a plugin-based architecture to support hundreds of infrastructure and service providers. The terraform init command downloads and installs providers used within the configuration, which in this case is the aws provider.

init

In the same directory as the terraform configuration .tf file you created, run the following command.

$ terraform init

Terraform downloads the aws provider and installs it in a hidden subdirectory of the current working directory.

If you are copying configuration snippets or just want to make sure your configuration is syntactically valid and internally consistent, the built in terraform validate command will check and report errors within modules, attribute names, and value types.

Note: The commands shown in this guide apply to Terraform 0.11 and above. Earlier
versions require using the terraform plan command to see the execution plan before
applying it. Use terraform version to confirm your running version.

apply

In the same directory as the terraform configuration .tf file you created, run the following command.

$ terraform apply

This output shows the execution plan, describing which actions Terraform will take in order to change real infrastructure to match the configuration. The output format is similar to the diff format generated by tools such as Git

After this, Terraform is all done! You can go to the EC2 console to see the created EC2 instance. (Make sure you’re looking at the same region that was configured in the provider configuration!)

show

You can inspect the current state using the following command

$ terraform show

Terraform also wrote some data into the terraform.tfstate file. This state file is extremely important; it keeps track of the IDs of created resources so that Terraform knows what it is managing. This file must be saved and distributed to anyone who might run Terraform. It is generally recommended to setup remote state when working with Terraform, to share the state automatically.

destroy

The terraform destroy command terminates resources defined in your Terraform configuration. This command is the reverse of terraform apply in that it terminates all the resources specified by the configuration. It does not destroy resources running elsewhere that are not described in the current configuration.

When you are done here run terraform destroy

Modifying Infrastructure with Terraform

Infrastructure is continuously evolving, and Terraform was built to help manage and enact that change. As you change Terraform configurations, Terraform builds an execution plan that only modifies what is necessary to reach your desired state. By using Terraform to change infrastructure, you can version control not only your configurations but also your state so you can see how the infrastructure evolved over time.

In our case, if we change the value of the default ami in the aws_ami section as follows:

variable "aws_ami" {
    description = "The Amazon Machine Image (ami)"
    type        = string
    default     = "ami-08cec7c429219e339" # Ubuntu Server 16.04 LTS (HVM), SSD Volume Type (64-bit x86)
}

And apply it as follows:

$ terraform apply

From the console output, the prefix -/+ means that Terraform will destroy and recreate the resource, rather than updating it in-place. While some attributes can be updated in-place (which are shown with the ~ prefix), changing the AMI for an EC2 instance requires recreating it. Terraform handles these details for you, and the execution plan makes it clear what Terraform will do.

Additionally, the execution plan shows that the AMI change is what required your resource to be replaced. Using this information, you can adjust your changes to possibly avoid destroy/create updates if they are not acceptable in some situations.

Once again, Terraform prompts for approval of the execution plan before proceeding. Answer yes to execute the planned steps:

Terraform will first destroy the existing instance and then created a new one in its place. You can use terraform show again to see the new values associated with this instance.

References


Written byChinomso IkwuagwuExcélsior

Limited conversations with distributed systems.

Modifying legacy applications using domain driven design (DDD)

Gherkin Best Practices

Code Review Best Practices

Hacking Cypress in 9 minutes

Some common mistakes when developing java web applications

How to make a Spring Boot application production ready

SQL JOINS - A Refresher

Add Elasticsearch to Spring Boot Application

Add entities/tables to an existing Jhipster based project

CSS 3 Media Queries - All over again

Maven Dependency Convergence - quick reference

Amazon SNS Quick Reference

AWS API Gateway Quick Reference

Amazon SQS Quick Reference

AWS API Gateway Quick Reference

AWS Lambda Quick Reference

Amazon DynamoDB - Quick Reference

Amazon Aurora

Amazon Relational Database Service

AWS Database Services

AWS Security Essentials

Amazon Virtual Private Cloud Connectivity Options

Summary of AWS Services

AWS Certified Solutions Architect - Quick Reference

AWS CloudFront FAQs - Curated

AWS VPC FAQs - Curated

AWS EC2 FAQs - Curated

AWS Achritect 5 - Architecting for Cost Optimization

AWS Achritect 4 - Architecting for Performance Efficiency

AWS Achritect - 6 - Passing the Certification Exam

AWS Achitect 3 - Architecting for Operational Excellence

AWS Achitect 2 - Architecting for Security

AWS Achitect 1 - Architecting for Reliability

Amazon DynamoDB Accelerator (DAX)

Questions and Answers - AWS Certified Cloud Architect Associate

Questions and Answers - AWS Certified Cloud Architect Associate

AWS Connectivity - PrivateLink, VPC-Peering, Transit-gateway and Direct-connect

AWS - VPC peering vs PrivateLink

Designing Low Latency Systems

AWS EFS vs FSx

AWS Regions, Availability Zones and Local Zones

AWS VPC Endpoints and VPC Endpoint Services (AWS Private Link)

AWS - IP Addresses

AWS Elastic Network Interfaces

AWS Titbits

Jenkins on AWS - Automation

Jenkins on AWS - Setup

Jenkins on AWS - Best practices

Introduction to CIDR Blocks

AWS Lamda - Limitations and Use Cases

AWS Certified Solutions Architect Associate - Part 10 - Services and design scenarios

AWS Certified Solutions Architect Associate - Part 9 - Databases

AWS Certified Solutions Architect Associate - Part - 8 Application deployment

AWS Certified Solutions Architect Associate - Part 7 - Autoscaling and virtual network services

AWS Certified Solutions Architect Associate - Part 6 - Identity and access management

AWS Certified Solutions Architect Associate - Part 5 - Compute services design

AWS Certified Solutions Architect Associate - Part 4 - Virtual Private Cloud

AWS Certified Solutions Architect Associate - Part 3 - Storage services

AWS Certified Solutions Architect Associate - Part 2 - Introduction to Security

AWS Certified Solutions Architect Associate - Part 1 - Key services relating to the Exam

AWS Certifications - Part 1 - Certified solutions architect associate

AWS Virtual Private Cloud (VPC) Examples

Curated info on AWS Virtual Private Cloud (VPC)

Notes on Amazon Web Services 8 - Command Line Interface (CLI)

Notes on Amazon Web Services 7 - Elastic Beanstalk

Notes on Amazon Web Services 6 - Developer, Media, Migration, Productivity, IoT and Gaming

Notes on Amazon Web Services 5 - Security, Identity and Compliance

Notes on Amazon Web Services 4 - Analytics and Machine Learning

Notes on Amazon Web Services 3 - Managment Tools, App Integration and Customer Engagement

Notes on Amazon Web Services 2 - Storages databases compute and content delivery

Notes on Amazon Web Services 1 - Introduction

AWS Auto Scaling - All you need to know

AWS Load Balancers - How they work and differences between them

AWS EC2 Instance Types - Curated

Amazon Web Services - Identity and Access Management Primer

Amazon Web Services - Create IAM User

Preparing Jenkins after Installation

Jenkins titbits, and then some

Docker Titbits

How to Add Chat Functionality to a Maven Java Web App

Packer - an introduction

Terraform - an introduction

Versioning REST Resources with Spring Data REST

Installing and running Jenkins in Docker

Automate deployment of Jenkins to AWS - Part 2 - Full automation - Single EC2 instance

Automate deployment of Jenkins to AWS - Part 1 - Semi automation - Single EC2 instance

Introduction to Jenkins

Software Engineers Reference - Dictionary, Encyclopedia or Wiki - For Software Engineers