Laptop to Production

Why Terraform

What goes wrong when infrastructure lives in people's heads.

Time
30 minutes
Level
Beginner
Read first
Nothing — you can start here

Part 1: The problem

QuickCart, the first environment

QuickCart’s first network, load balancer and database were built by Maya, by hand, in the AWS console. It took her most of a Tuesday: about a hundred form fields, in the right order.

It worked. Six months later, QuickCart needed a second copy in another region, closer to customers in Europe. Maya had left for a two-week holiday. Leo opened her notes: a short document and a few screenshots.

He clicked through it all again. It took him two days. The new servers couldn’t download updates, and it took another day to find why: one route table was missing a single line. Maya’s notes didn’t mention it, because she’d fixed it on the first day and forgotten.

That’s what building by hand costs. Not the first time, which goes fine. Every time after.

This lesson is about writing infrastructure down as code, and why that fixes both Leo’s two lost days and the typo nobody could review.

Part 2: The idea

Building by clicking in the console has a nickname: ClickOps. It’s the right way to learn, and to explore. The trouble starts when a whole environment exists only because someone clicked it into being.

What goes wrong with clicking

ProblemWhat it looks like at QuickCart
UnrepeatableLeo can’t build Maya’s network again exactly. He can only try
UndocumentedThe real setup is whatever is in AWS right now. Nobody wrote down why each setting is there
UnreviewedOne person clicks, and the change is live. Nobody else looks first
DriftSomeone changes a setting in a hurry. Now dev and production differ, and nobody knows how
Slow to copyA second region, or a staging copy, means all the clicking again, from memory

The idea: write it down as code

Infrastructure as code means you describe your infrastructure in text files. A tool reads the files and builds what they describe.

Terraform is the most widely used tool for this. You write files ending in .tf, like this:

main.tf
resource "aws_ecr_repository" "orders" {
  name = "quickcart-orders"
}
PartWhat it means
resourcesomething Terraform creates and looks after
"aws_ecr_repository"the type: an ECR repository, a private store for container images
"orders"the name your code uses for it
namethe name AWS gives it. Leave it out and Terraform stops with an error, because it’s required

That says: there should be an ECR repository called quickcart-orders. You’ll build exactly this in Your first resource.

Because the files are text, they go into git, like app code. So you get what app developers already rely on:

With codeWhat it gives QuickCart
Git historyWho changed what, when, and why
Pull requestsSomeone reviews a change before it runs
Run it againAn identical copy, as many times as you like
Change one valueThe same environment in another region

Declarative, not step by step

There are two ways to tell a computer what to do.

Imperative: a list of stepsDeclarative: the end result
You write“Create a VPC. Then create a subnet in it.”“There is a VPC. It has this subnet.”
Run it twiceTries to create everything twice, and fails or duplicatesSees it already exists, and does nothing
Something changed by handThe script doesn’t knowSpotted, and put back to match
ExamplesA shell script, clicking through the consoleTerraform

Terraform is declarative. You describe what should exist. Terraform works out the steps.

Key point: Describe what should exist, read the plan, then apply. The code is the truth.

How Terraform knows what to do

Every run compares three things:

ThingWhat it is
Your codeWhat you want to exist
StateTerraform’s record of what it built last time. Terraform state covers it in depth
AWSWhat really exists right now

From the difference, it makes a plan: a list of what it would create, change or destroy.

The loop you’ll use every day

StepCommandWhat happens
1terraform initSet up the folder and download the plugins it needs. Once per folder
2Edit the .tf filesDescribe what you want
3terraform planSee exactly what would change. Nothing changes yet
4terraform applyShows the plan again, asks you to type yes, then makes the changes
5terraform destroyRemove everything this code built. For practice environments

Then back to step 2. Because apply compares first, running it again with no changes does nothing. That’s called idempotent, and it’s why it’s safe to run as often as you like.

What Terraform doesn’t do

It doesn’t make clicking wrong. You’ll still use the console to look at things, to learn, and for the one bucket that holds Terraform’s own state. What changes is where the truth lives: in the code, not in someone’s memory.

Part 3: See it

Build QuickCart’s environment both ways. Then build a second copy in another region, and compare.

By clicking in the console

one form at a time
us-east-1
Nothing built
Time
0 hours
Written down
Nothing yet

By code, with Terraform

files in git, then terraform apply
us-east-1
Nothing built
Time
0 hours
Written down
Nothing yet
Before anything is builtQuickCart needs its environment: a network, a load balancer and a database. Build it once each way.

What to notice: the first build costs about the same either way. The difference only shows on the second copy: clicking is just as slow again and quietly misses a step, while code builds an identical copy by changing one value.

Part 4: Do it

Free — everything runs on your laptop. Nothing is created in AWS.

You’ll install Terraform and run the whole loop — init, plan, apply, apply again, change, destroy — with a built-in resource that lives only in Terraform’s memory. No AWS account needed. About 15 minutes.

  1. Install Terraform

    With Homebrew, on macOS, Linux or Windows (inside WSL), Terraform comes from HashiCorp’s own collection. Set up your computer installs Homebrew. First add the collection:

    Terminal
    brew tap hashicorp/tap
    PartWhat it means
    brew tapadd another collection of software that Homebrew can install from
    hashicorp/tapHashiCorp’s own collection

    Then install Terraform from it:

    Terminal
    brew install hashicorp/tap/terraform
    PartWhat it means
    brew installinstall a program
    hashicorp/tap/terraformTerraform, from HashiCorp’s collection

    Check it worked:

    Terminal
    terraform version
    PartWhat it means
    terraform versionprint the version of Terraform installed
    You should see
    Terraform v1.16.4
    on linux_amd64

    Yours may be newer. Anything from 1.10 upwards works for this course. The second line names your system: darwin_arm64 on a Mac with an Apple chip, linux_amd64 on most Linux and Windows computers.

    On Windows or Linux, follow the install page on HashiCorp’s developer site, then run terraform version the same way.

  2. Make a folder and describe something

    Make a folder for this lesson:

    Terminal
    mkdir quickcart-hello
    PartWhat it means
    mkdirmake a directory
    quickcart-helloits name
    Terminal
    cd quickcart-hello
    PartWhat it means
    cdchange directory. Every later command runs in here
    quickcart-hellothe folder you just made

    Create main.tf in that folder, in any text editor:

    main.tf
    terraform {
      required_version = ">= 1.10.0"
    }
    
    resource "terraform_data" "environment" {
      input = {
        app    = "quickcart"
        region = "us-east-1"
        zones  = 2
      }
    }
    
    output "environment" {
      value = terraform_data.environment.output
    }

    terraform_data is a resource built into Terraform. It creates nothing outside Terraform: it just stores the values you give it. That makes it perfect for practising the loop without an AWS account.

    SettingWhat it doesIf you leave it outWhen you’d change it
    terraform → required_versionRefuse to run on a Terraform older than 1.10Any version runs it, and an old one may fail later with a confusing errorWhen your code starts relying on a newer feature
    resource "terraform_data" "environment"Declare one resource. terraform_data is the type; environment is the name your code uses for it——
    inputThe values to store. Here, a small description of an environmentThe resource stores nothing, and has nothing to showChange it to see how a plan shows an update
    output "environment"Print a value after applyNothing is printed after applyAdd one for anything you’ll want to see or use elsewhere
    valueWhat the output prints: the stored values, read back from the resourceRequired — an output without a value is an errorPoint it at whatever you want to see
  3. Initialise the folder

    Terminal
    terraform init
    PartWhat it means
    terraform initset up this folder: find and download any plugins the code needs
    You should see
    Initializing the backend...
    
    Initializing provider plugins...
    - terraform.io/builtin/terraform is built in to Terraform
    
    Terraform has been successfully initialized!

    terraform_data is built in, so there’s nothing to download. With AWS, this is where the AWS plugin would be downloaded.

  4. Read the plan

    Terminal
    terraform plan
    PartWhat it means
    terraform plancompare the code with what exists, and show what would change. Changes nothing
    You should see
    Terraform used the selected providers to generate the following execution
    plan. Resource actions are indicated with the following symbols:
      + create
    
    Terraform will perform the following actions:
    
      # terraform_data.environment will be created
      + resource "terraform_data" "environment" {
          + id     = (known after apply)
          + input  = {
              + app    = "quickcart"
              + region = "us-east-1"
              + zones  = 2
            }
          + output = (known after apply)
        }
    
    Plan: 1 to add, 0 to change, 0 to destroy.
    
    Changes to Outputs:
      + environment = (known after apply)

    Read it top to bottom:

    LineWhat it tells you
    + createthe key: + means something will be created
    # terraform_data.environment will be createdwhich resource, and what will happen to it
    (known after apply)a value that doesn’t exist until it’s created, like an ID
    Plan: 1 to add, 0 to change, 0 to destroy.the summary. Always read this line

    It also warns you that the plan wasn’t saved. That’s fine here: you’ll use saved plans later, in pipelines.

  5. Apply it, then apply again

    Terminal
    terraform apply
    PartWhat it means
    terraform applymake the plan again, show it, and carry it out once you type yes

    It shows the same plan, then asks. Type yes:

    You should see
    Do you want to perform these actions?
      Terraform will perform the actions described above.
      Only 'yes' will be accepted to approve.
    
      Enter a value: yes
    
    terraform_data.environment: Creating...
    terraform_data.environment: Creation complete after 0s [id=2af1f034-bd2c-4555-3989-3189330c5de7]
    
    Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
    
    Outputs:
    
    environment = {
      "app" = "quickcart"
      "region" = "us-east-1"
      "zones" = 2
    }

    Your ID will be different. A new file has appeared in the folder: terraform.tfstate. That’s Terraform’s record of what it built.

    Now run exactly the same command again:

    Terminal
    terraform apply
    PartWhat it means
    terraform applythe same command, with no change to the code
    You should see
    terraform_data.environment: Refreshing state... [id=2af1f034-bd2c-4555-3989-3189330c5de7]
    
    No changes. Your infrastructure matches the configuration.
    
    Terraform has compared your real infrastructure against your configuration
    and found no differences, so no changes are needed.
    
    Apply complete! Resources: 0 added, 0 changed, 0 destroyed.

    Nothing was built twice. A script would have tried to create it again. Terraform compared, found everything matching, and did nothing. This is what makes it safe to run as often as you like.

  6. Change the code, and read the plan

    QuickCart decides to run in three zones. In main.tf, change zones = 2 to zones = 3. The whole file now reads:

    main.tf
    terraform {
      required_version = ">= 1.10.0"
    }
    
    resource "terraform_data" "environment" {
      input = {
        app    = "quickcart"
        region = "us-east-1"
        zones  = 3
      }
    }
    
    output "environment" {
      value = terraform_data.environment.output
    }
    Terminal
    terraform plan
    PartWhat it means
    terraform plancompare the changed code with what was built
    You should see
    Terraform used the selected providers to generate the following execution
    plan. Resource actions are indicated with the following symbols:
      ~ update in-place
    
    Terraform will perform the following actions:
    
      # terraform_data.environment will be updated in-place
      ~ resource "terraform_data" "environment" {
            id     = "2af1f034-bd2c-4555-3989-3189330c5de7"
          ~ input  = {
              ~ zones  = 2 -> 3
                # (2 unchanged attributes hidden)
            }
          ~ output = {
              - app    = "quickcart"
              - region = "us-east-1"
              - zones  = 2
            } -> (known after apply)
        }
    
    Plan: 0 to add, 1 to change, 0 to destroy.

    ~ means change in place. The line zones = 2 -> 3 shows the old value, an arrow, and the new one. Only what you changed is planned: the two unchanged values are hidden.

    You don’t need to apply this. The point was to see how Terraform describes a change.

  7. Clean-up step: Clean up

    Terminal
    terraform destroy
    PartWhat it means
    terraform destroyplan the removal of everything this folder built, and carry it out once you type yes

    It shows a plan full of - lines and asks. Type yes:

    You should see
    Plan: 0 to add, 0 to change, 1 to destroy.
    
    Do you really want to destroy all resources?
      Terraform will destroy all your managed infrastructure, as shown above.
      There is no undo. Only 'yes' will be accepted to confirm.
    
      Enter a value: yes
    
    terraform_data.environment: Destroying... [id=2af1f034-bd2c-4555-3989-3189330c5de7]
    terraform_data.environment: Destruction complete after 0s
    
    Destroy complete! Resources: 1 destroyed.

    Nothing existed outside your laptop, so nothing else needs cleaning up. You can delete the quickcart-hello folder, or keep it to experiment with.

Part 5: Check yourself

QuickCart's network was built by hand and works. Why is that still a problem?

What does "declarative" mean for Terraform code?

You run terraform apply twice in a row with no changes to the code. What does the second run do?

Why does terraform plan exist as a separate step from apply?

Someone fixes an outage by changing a setting in the console, and doesn't update the code. What happens at the next apply?

Part 6: Cheat sheet

On one card
Idea or commandWhat it means
ClickOpsBuilding by clicking in the console. Fine for learning; unrepeatable for real environments
Infrastructure as codeInfrastructure described in text files, kept in git, built by a tool
DeclarativeDescribe the end result; the tool works out the steps
IdempotentRunning it again with no changes does nothing
terraform initSet up a folder and download plugins. Once per folder
terraform planShow what would change. Changes nothing
terraform applyShow the plan, ask for yes, make the changes
terraform destroyRemove everything this code built
+ ~ -Create, change in place, destroy
The ruleEvery fix goes in the code. A console-only fix gets undone

Finished the build and the quiz? Mark it done to fill in its stop on the route map.