Manage your cloud infrastructure
as code using Terraform

Nic Wortel
@nicwortel

About me

  • Software Consultant, Trainer, Coach
  • PHP, Symfony, DevOps, Kubernetes, DDD, Agile
  • Squad Leader / Instructor (reservist) at Royal NL Army

nicwortel.nl     @nicwortel

Goal of this talk

After this talk, you will...

  • Understand the benefits of Infrastructure as Code
  • Understand the core Terraform workflow
  • Understand how Terraform uses state
  • Understand simple Terraform configurations
Data center
Server room
Server - Do not turn off
The cloud
There is no cloud
AWS Console

Cloud infrastructure challenges

  • (Error-prone) manual configuration
  • Documentation
  • Tracking history

Infrastructure as Code

  • Describe infrastructure using configuration files
  • Track the configuration files in Git
  • Automate provisioning and configuration

What is Terraform?

  • Infrastructure as Code tool by HashiCorp
  • Supports many cloud provider APIs
  • Human-readable, declarative configuration
  • Uses state to track managed resources
  • Open source Business Source License
  • OSS fork: OpenTofu

Alternative tools

  • Vendor-specific
    • AWS CloudFormation
    • Azure Resource Manager
    • Google Cloud Deployment Manager
  • Pulumi
  • Ansible

Building infrastructure

Providers

Providers are plugins that Terraform uses to interact with infrastructure providers

Terraform Registry

Provider examples

  • AWS
  • Azure
  • Google Cloud
  • DigitalOcean
  • Cloudflare
  • Kubernetes
  • Helm
  • GitHub
  • GitLab
  • Datadog
  • Okta
  • OneLogin
  • etc.

Not-so-live demo: spinning up an EC2 instance

Requiring the AWS provider


                              terraform {
                                required_providers {
                                  aws = {
                                    source  = "hashicorp/aws"
                                    version = "~> 4.16"
                                  }
                                }
                              }
                        
Terraform init

Configuring the AWS provider


                              variable "aws_access_key" {
                                type = string
                                sensitive = true
                              }
                              
                              variable "aws_secret_key" {
                                type = string
                                sensitive = true
                              }
                              
                              provider "aws" {
                                region = "eu-central-1"
                                access_key = var.aws_access_key
                                secret_key = var.aws_secret_key
                              }
                        

                            # terraform.tfvars
                            aws_access_key = "xxxxx"
                            aws_secret_key = "xxxxxxx"
                        

Defining the EC2 resource


                              resource "aws_instance" "vm" {
                                ami = "ami-06dd92ecc74fdfb36"
                                instance_type = "t2.micro"
                              
                                tags = {
                                  Name = "DemoInstance"
                                }
                              }
                        
Terraform plan
Terraform demo 1
AWS Console EC2 instance

Importing existing infrastructure

State


                            resource "aws_instance" "vm" {
                              ami = "ami-06dd92ecc74fdfb36"
                              instance_type = "t2.micro"

                              tags = {
                                Name = "DemoInstance"
                              }
                            }
                        

                            $ terraform import aws_instance.vm i-07d8d81553bbf38be
                        

Making changes


                            resource "aws_instance" "vm" {
                           -  ami = "ami-0aa6457dc2d115893" # 20.04 LTS
                           +  ami = "ami-06461d2b867abebf0" # 22.04 LTS
                              instance_type = "t2.micro"
                            
                              tags = {
                                Name = "DemoInstance"
                              }
                            }
                      

                            $ terraform plan

                            Terraform will perform the following actions:

                            # aws_instance.vm must be replaced
                            -/+ resource "aws_instance" "vm" {
                                ~ ami       = "ami-0aa6457dc2d115893" -> "ami-06461d2b867abebf0" # forces replacement
                                ~ id        = "i-07d8d81553bbf38be" -> (known after apply)
                                ~ public_ip = (known after apply)
                            }
                        
Terraform demo 2

Lifecycle Meta-Argument


                            resource "aws_instance" "vm" {
                              ami = "ami-06461d2b867abebf0"
                              instance_type = "t2.micro"

                              tags = {
                                Name = "DemoInstance"
                              }

                           +  lifecycle {
                           +    create_before_destroy = true
                           +  }
                            }
                      
Create before destroy

Destroying infrastructure

Data sources


                              data "aws_ami" "ubuntu" {
                                most_recent = true
                                owners      = ["amazon"]
                              
                                filter {
                                  name   = "name"
                                  values = [
                                    "ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"
                                  ]
                                }
                              }
                        

                              resource "aws_instance" "vm" {
                             -  ami           = "ami-06461d2b867abebf0"
                             +  ami           = data.aws_ami.ubuntu.id
                                instance_type = "t2.micro"
                              
                                tags = {
                                  Name = "DemoInstance"
                                }
                              }
                        

Dependencies


                              terraform {
                                required_providers {
                                  aws = {
                                    source  = "hashicorp/aws"
                                    version = "~> 4.16"
                                  }
                             +    cloudflare = {
                             +      source  = "cloudflare/cloudflare"
                             +      version = "~> 3.0"
                             +    }
                                }
                              }
                        
Terraform init

                             +variable "cloudflare_api_token" {
                             +  type      = string
                             +  sensitive = true
                             +}
                              
                              provider "aws" {
                                region = "eu-central-1"
                              
                                access_key = var.aws_access_key
                                secret_key = var.aws_secret_key
                              }

                             +provider "cloudflare" {
                             +  api_token = var.cloudflare_api_token
                             +}
                        

                             # terraform.tfvars
                             aws_access_key       = "xxxx"
                             aws_secret_key       = "xxxx"
                            +cloudflare_api_token = "xxxx"
                        

                              resource "aws_instance" "vm" {
                                # ...
                              }

                              data "cloudflare_zone" "nicwortel" {
                                name = "nicwortel.nl"
                              }
                              
                              resource "cloudflare_record" "demo" {
                                zone_id = data.cloudflare_zone.nicwortel.id
                                name    = "demo"
                                type    = "A"
                                value   = aws_instance.vm.public_ip
                                proxied = true
                              }
                        

Replacing the dependency


                              data "aws_ami" "ubuntu" {
                                most_recent = true
                                owners      = ["amazon"]
                              
                                filter {
                                  name = "name"
                                  values = [
                             -      "ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"
                             +      "ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*"
                                  ]
                                }
                              }
                        

Recap

  • What is Infrastructure as Code?
  • What is Terraform?
  • Building infrastructure
  • Providers
  • Making changes
  • State
  • Destroying infrastructure
  • Data sources
  • Dependencies

Want to learn more?

Terraform tutorials

Terraform cheat sheet

Terraform cheat sheet

Questions?

Cheat sheets

nicwortel.nl/cheat-sheets

Please give me feedback!

Let me know how to improve this talk