Terraform Introduction

Nathan Luong | April 15, 2024 13

What

  • A tool for building, changing, and versioning infrastructure safely and efficiently
  • Enables applications software best practices to infrastructure
  • Compatible with many clouds and services What is Terraform

Installation

  1. brew install terraform
  2. Install aws-cli
  3. Create an IAM role and user for the Terraform script to run

Terraform Architecture

Terraform Architecture

Common Patterns

  • Used with Ansible to manage configuration, while Terraform doing the Provisioning
  • Used with Server Templating Tool
  • Used with Orchestration Tools, like Kubernetes, to provision kubernetes clusters

Terraform Commands

High Level

  • terraform init: Init the backend
  • terraform plan: Query AWS, compare the current state of AWS with the terraform file
  • terraform apply: Provisioning the changes
  • terraform destroy: Destroy the ongoing AWS infrastructure

More In depth

terraform init

  • Creates a .terraform directory inside of the current working directory
  • It downloads the cloud provider onto the local file system and their metadata
  • It downloads other Terraform Modules

terraform plan

  • Compare desired state with the actual state
  • Create a terraform plan that can get the infrastructure from the desired state to the actual state
  • Derived the AWS API calls necessary from the Terraform plans.

terraform apply

  • Apply the Terraform plans (API Calls) onto the existing infra and apply infra changes

State File

  • Terraform's representation of the world
  • JSON file containing information about every resource and data object
  • The file can be stored locally or remotely. In an enterprise setting, its common to store remotely

⚠️

Warning

  • The state file contains sensitive informations, including passwords, DB access, ...
  • It is important to hide, encrypt, or have limited access to the file

Managing State Files

Local Backend

  • Simple to get started
  • Sensitive values in plain text
  • Un-collaborative
  • Manual

Remote Backend

  • Use Terraform Cloud, or self-managed with S3
  • Sensitive Data Encrypted
  • Collaboration possible
  • Automation possible (Github Actions)
  • Increased Complexity
Remote Backend Terraform Cloud
  • Free up to 5 users
terraform{
	backend "remote"{
		organization = "devops-directive"
		workspace {
			name = "terraform-course"
		}
	}
}
Remote Backend with AWS S3 and DynamoDB
terraform {
	backend 's3' {
		bucket         = "devops-directive-tf-state"
		key            = "tf-infra/terraform.tfstate"
		region         = "us-east-1"
		dynamodb_table = "terraform-state-locking"
		encrypt        = true
	}
}

Variables in HCL

Input Variables (var.<name>)

Used as input arguments for functions

variable "instance_type" {
	description = "ec2 instance type"
	type          = string
	default      = "t2.micro"
}

Local Variables (local.<name>)

Used as a repetitive variable within a scope

locals {
	service_name = "My Service"
	owner            = "DevOps Directive"
}

Output Variables (output.<name>)

Take the value of a returned function call

output "istance_ip_addr"{
	value = aws_instance.instance.public_ip
}

AWS Examples

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 3.0"
    }
  }
}

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

resource "aws_instance" "example" {
  ami               = "ami-011899242bb902164" # Ubuntu 20.04 LTS // us-east-1
  instance_type = "t2.micro"
}

Simple Architecture

Example AWS App
terraform{
 backend "s3"{
  bucket               = "devops-directive-tf-state"
  key                    = "03-basics/webapp/terraform.tfstate"
  region                = "us-east-1"
  dynamodb_table = " terraform-state-locking"
  encrypt              = true
 }

 required_providers {
  aws = {
   source = "harshicorp/aws"
   version = "~> 3.0" #????
  }
 }
}

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

resource "aws_instance" "instance_1" {
 ami = "ami-..."
 instance_type = "t2.micro"
 security_groups = [aws_security_group.instance.name]
 user_data       = <<-EOF
                    #!/bin/bash
                    echo "Hello, World 1" > index.html
                    python3 -m http.server 8080 &
                    EOF
}
resource "aws_instance" "instance_1" {
 ami = "ami-..."
 instance_type = "t2.micro"
 security_groups = [aws_security_group.instance.name]
 user_data       = <<-EOF
                    #!/bin/bash
                    echo "Hello, World 1" > index.html
                    python3 -m http.server 8080 &
                    EOF
}

resource "aws_instance" "instance_2" {
 ami = "ami-..."
 instance_type = "t2.micro"
 security_groups = [aws_security_group.instance.name]
 user_data       = <<-EOF
                    #!/bin/bash
                    echo "Hello, World 2" > index.html
                    python3 -m http.server 8080 &
                    EOF
}
...