Terraform & Infrastructure as Code

Reminders

  • Final project guidelines out
    • Proposal due 4/8, earlier is better!
    • Presentations on last class 4/29
    • Final writeup due midnight 4/29
  • Install terraform for lab later today

Agenda

  1. Motivation: Infrastructure as Code
  2. What is Terraform?
  3. Terraform Basics
  4. Managing Kubernetes with Terraform
  5. Terraform Workflow

Infrastructure as Code

Recap

So far, we've used various tools to manage our (mainly local) infrastructure:

  • Docker Compose for local development
  • Helm/Kubernetes for local Kubernetes testing

We're at the final step of deploying our system to production, namely we need to manage our hardware. We used the AWS console to manually spin up instances last week, what could go wrong with this approach?

Problems with Manual Infrastructure

  • Reproducibility

    • Can you recreate your exact setup?
    • What if you need dev/staging/prod?
  • History

    • What changes were made and when?
    • What did infrastructure look like a month ago?
  • Error-Prone

    • Easy to forget steps
    • Typos in console or CLI
  • Collaboration

    • How do you review infra changes?
    • Who changed what?

Solution: Infrastructure as Code

Infrastructure as Code (IaC): Manage infrastructure using configuration files instead of manual processes, similar to how Helm manages Kubernetes manifests as code.

  • Configuration is versioned (Git!)
  • Changes are reviewable (Pull requests!)
  • Environments are reproducible
  • Documentation is code

Infrastructure as Code

Cloud-Specific

  • AWS CloudFormation
  • Azure Resource Manager
  • Google Cloud Deployment Manager

Good for single cloud, deep integration

Cloud-Agnostic

  • Terraform (what we'll use)
  • Pulumi
  • Ansible

Work across multiple providers, more flexible

What is Terraform?

Terraform Overview

  • Terraform: Infrastructure as Code tool by HashiCorp
  • Uses declarative configuration files, similar to K8s manifests
    • Written in HCL (HashiCorp Configuration Language)
  • Can manage infrastructure across multiple cloud providers
  • Tracks infrastructure state to know what exists
  • Anything you can spin up, Terraform can manage as code

Terraform vs. Other Tools

  • kubectl/Helm is Kubernetes-specific
    • I specify what pods/deployments I want
  • Terraform can complement kubectl/Helm by managing the infrastructure around Kubernetes (clusters, networks, databases, etc.)
    • I specify what specific hardware I want, and then specify what kubernetes objects I want

Terraform Basics

Core Concepts

Terraform has three main concepts:

  1. Providers: Plugins that interact with APIs (AWS, Kubernetes, etc.)
  2. Resources: Infrastructure objects you want to create/manage
  3. State: Terraform's record of what infrastructure exists

Providers

  • Provider: Plugin that knows how to interact with a specific platform
  • Each provider offers different resource types
# Configure the AWS provider
provider "aws" {
  region = "us-east-1"
}

# Configure the Kubernetes provider
provider "kubernetes" {
  config_path = "~/.kube/config"
}

Resources

  • Resource: A piece of infrastructure (VM, database, K8s deployment, etc.)
  • Syntax: resource "type" "name"
resource "aws_instance" "web_server" {
  ami           = "ami-0c55b159cbfafe1f0"
  instance_type = "t2.micro"

  tags = {
    Name = "MyWebServer"
  }
}

Resources: helm_release

  • Terraform provides a helm_release resource!
resource "helm_release" "nginx" {
    name       = "nginx"
    chart      = "../k8s/charts/nginx"
    namespace  = "default"

    values = [
      file("nginx-values-prod.yaml")
    ]
  }

State Management

  • State is Terraform's record of managed infrastructure
  • Stored in terraform.tfstate file (JSON format)
  • Maps configuration to real resources
    • This aws_instance resource refers to this specific instance ID
  • Tracks metadata and dependencies
  • Terraform uses it to know what exists/what to spin up
  • Location of file is called the backend, i.e. could be S3
  • Contains sensitive data (treat carefully!)

HCL: HashiCorp Configuration Language

# Variables for reusability
variable "instance_type" {
  default = "t2.micro"
}

# Resources reference variables
resource "aws_instance" "app" {
  instance_type = var.instance_type
  ami           = "ami-12345678"
}

# Outputs for useful information
output "instance_ip" {
  value = aws_instance.app.public_ip
}

Terraform Workflow

Terraform Workflow

Terraform has a clear workflow:

  1. Write: Define infrastructure in .tf files
  2. Init: Download provider plugins
  3. Plan: Preview changes before applying
  4. Apply: Create/update infrastructure
  5. Destroy: Remove infrastructure when done

1. Write Configuration

# main.tf
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

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

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

2. Initialize: terraform init

  • Downloads required provider plugins
  • Initializes backend for state storage
  • Prepares working directory

Run this:

  • First time in a new Terraform directory
  • When adding new providers
  • When changing backend configuration

3. Plan: terraform plan

$ terraform plan
  • Shows what Terraform will do
  • Does not make changes (safe to run!)
  • Previews creates, updates, deletes, etc

4. Apply: terraform apply

$ terraform apply
  • Shows the plan again
  • Asks for confirmation
  • Creates/updates infrastructure
  • Updates state file

5. Destroy: terraform destroy

$ terraform destroy
  • Removes all managed infrastructure
  • Asks for confirmation (be careful!)
  • Useful for cleaning up test environments, rarely used in production

Terraform Paradigms

Setting Variables

# Command line
terraform apply -var="environment=prod"

# Variable file
terraform apply -var-file="prod.tfvars"

# Environment variables
export TF_VAR_secret_password=asdfasdf
terraform apply

# Interactive prompt (if not set)
terraform apply
# var.environment
#   Enter a value:

Outputs

# outputs.tf
output "instance_public_ip" {
  description = "Public IP of the web server"
  value       = aws_instance.web.public_ip
}

After terraform apply:

$ terraform output
instance_public_ip = "54.123.45.67"

Terraform Modules

  • Module: Reusable Terraform configuration
  • Similar to helm charts, think of them as a template
  • Useful for common infrastructure components
# Use a module to easily define a new VPC
module "vpc" {
  # "template" definition
  source = "./modules/vpc"

  vpc_cidr = "10.0.0.0/16"
  environment = "prod"
}

# Reference module outputs
resource "aws_instance" "web" {
  subnet_id = module.vpc.public_subnet_id
}

Project Structure

Organize Terraform code clearly:

myapp/terraform
  main.tf           # Main resource definitions
  variables.tf      # Input variables
  outputs.tf        # Output values

  environments/
    dev.tfvars      # Dev environment values
    prod.tfvars     # Prod environment values

  modules/          # Reusable modules
    networking/
    compute/

State Management for Teams

  • Problem: Multiple people can't easily share a local state file
  • Solution: Remote backend (S3, Terraform Cloud, etc.)
terraform {
  backend "s3" {
    bucket = "my-terraform-state"
    key    = "prod/terraform.tfstate"
    region = "us-east-1"

    # Enable locking to prevent concurrent applies
    dynamodb_table = "terraform-locks"
  }
}

Common Patterns

  • Separate Terraform + Helm
    • Terraform creates K8s cluster, VPC, databases
    • kubectl/Helm deploys applications
    • A bit more operational overhead (two tools)
    • Cleaner separation of infra vs application
    • Better for larger orgs, platform team manages terraform, app teams manage helm
  • Terraform with Helm
    • Everything in Terraform (infrastructure + K8s resources/Helm charts)
    • Easier to manage (just use terraform apply)
    • Infra deployment may affect application deployment
    • Larger infra may be slower
    • Better for smaller teams

Lab: Terraform & AWS