Tutorials › Terraform

Metals Simple Deploy · Terraform Tutorial

Terraform: Building This Project's Cloud Infrastructure

Terraform's job in this project is to automate the creation of every Azure resource the app needs — and to automate any changes to that infrastructure later — so nobody has to click through the Azure Portal by hand. Everything Terraform builds is described in the plain-text files inside the terraform/ folder. Codey will walk through what's in that folder on this page, and each file gets its own detailed page after that.

Codey the Sr Developer, standing with a pointer, ready to walk through the guide
Folder → terraform/ Files with resources → 8 .tf files One-time setup → terraform/bootstrap/ Runs from → your terminal, or GitHub Actions Manual alternative → Manual Deployment tutorial

What problem does Terraform solve? start here

Before this project used Terraform, creating the Azure resources for this app meant clicking through the Azure Portal, or running a long list of one-off script commands, by hand. That works, but it's slow, easy to get wrong, and hard to repeat exactly the same way twice — especially months later, or for a teammate who wasn't there the first time.

Codey pointing to the right
infrastructure as code, not clicks
The goal of Terraform, in one sentencebig idea

Terraform lets you describe the cloud infrastructure you want — a database, a web server, a security identity — as plain text files, and then it automates the work of creating, updating, or removing that infrastructure to match what the files say.

This is called “Infrastructure as Code.” The infrastructure itself lives in version-controlled files, the same way application code does, instead of only existing as a set of manual clicks someone remembers making.

Why that mattersbenefits
  • Repeatable. Running the same Terraform files always produces the same result, whether it's the first run or the fiftieth.
  • Reviewable. Infrastructure changes go through the same pull-request review as application code, instead of happening silently in a portal.
  • Safe to change. Terraform shows you a preview of exactly what it's about to create, change, or delete — before it touches anything.
  • Easy to tear down. The same files that built the environment can remove it cleanly when it's no longer needed.

Terraform remembers what it built the shopping list analogy

Think of Terraform like a very detailed shopping list, plus a robot shopper who reads it.

Codey typing on a laptop
a list, and a memory of what's already in the cart
The shopping list analogyELI5

You write down exactly what you want — “one resource group, one database, one registry, two web apps” — in files. Terraform reads the list, checks what already exists in Azure, and then goes and creates (or fixes, or removes) whatever is different, so reality matches the list. You never have to remember the individual steps yourself; you just keep the list up to date.

Terraform remembers what it builtstate

After it runs, Terraform saves a record — called state — of everything it created. That's how it knows, the next time it runs, what already exists and what still needs to change. In this project, that record is stored safely in an Azure Storage account instead of on any one person's laptop, so anyone on the team (or the GitHub Actions robot) can run Terraform and get the same, up-to-date picture.

The folder, file by file structure

Every file below lives inside terraform/. None of them do anything by themselves — Terraform reads all the .tf files in the folder together, as one combined description of the infrastructure.

File / folderWhat it's for, in plain terms
versions.tfThe label on the toolbox. Says which version of Terraform and which Azure toolkit (“provider”) this project needs, so everyone uses matching tools. (dig deeper)
backend.tfThe address label for Terraform's memory. Tells Terraform to keep its record of what it built in a shared Azure location, not just on one laptop. (dig deeper)
variables.tfA fill-in-the-blank form. Lists every piece of information Terraform needs from a person (or from GitHub Actions) before it can run — like the database password or your Azure subscription. (dig deeper)
main.tfThe foundation. Creates the resource group — the labeled folder inside Azure that every other resource in this project gets placed into — and a few shared names used everywhere else. (dig deeper)
database.tfBuilds the PostgreSQL database server and the empty database inside it, plus the security rules that decide who's allowed to connect. (dig deeper)
app_service.tfBuilds the container registry, the shared hosting plan, and the two container web apps (API and UI) — and tells each one how to reach the database, the registry, and each other. (dig deeper)
github_oidc.tfBuilds a secure digital ID badge that lets GitHub Actions prove who it is to Azure, so it can deploy the app without ever storing a password. (dig deeper)
outputs.tfThe receipt. After everything is built, this prints back the important details — like the app's web address — so people (and GitHub) know where to find things. (dig deeper)
bootstrap/main.tfA separate, one-time setup. Before any of the files above can run, something has to create the safe storage location for Terraform's memory in the first place — that's this file's only job. (dig deeper)
terraform.tfvars.exampleA blank copy of the fill-in-the-blank form from variables.tf. You copy it to terraform.tfvars and fill in your own answers; it is never committed to Git.
backend.hcl.exampleA blank copy of the address label for shared memory. Copied to backend.hcl and filled in with the real storage account name.
.terraform.lock.hclA receipt that pins the exact version of the Azure toolkit that was downloaded, so every run uses the identical version instead of silently drifting over time.
scripts/Small helper programs — for example, a Python script that loads the starting database tables, and a script that prints the IDs the application deployment needs.
tests/Automated checks that catch mistakes in the .tf files before they're ever run against real Azure resources.
README.mdThe instruction manual: the exact commands to run, and in what order, to set this all up.

Note: Terraform also creates a local .terraform/ folder and a terraform.tfstate file the first time it runs. Those are working files Terraform manages for itself — you won't normally edit them by hand, and this project keeps its real state remotely rather than relying on the local copy.

A sensible reading order where to start

Terraform doesn't care what order the files are in — it reads them all as one unit. But as a human learning the project, it helps to read them in the order infrastructure actually gets described: setup first, then the foundation, then the resources that sit on top of it, then the receipt at the end.

Codey holding up a sticky note
nine files, one sensible order
  1. versions.tf — what tools this project expects.
  2. backend.tf — where Terraform keeps its memory.
  3. variables.tf — what information you need to supply.
  4. main.tf — the resource group everything else lives inside.
  5. database.tf — the PostgreSQL server and database.
  6. app_service.tf — the registry and the two container web apps.
  7. github_oidc.tf — the deployment identity for GitHub Actions.
  8. outputs.tf — what Terraform reports back when it's done.
  9. bootstrap/main.tf — the separate one-time setup that comes before all of the above.

How it actually gets run quick preview

Whether it's run from a laptop or from GitHub Actions, Terraform is always driven by the same handful of commands. Each file's tutorial page will refer back to these.

CommandWhat it does
terraform initDownloads the Azure provider and connects to the shared state storage. (dig deeper)
terraform fmtAuto-formats the files so they're easy to read and consistent. (dig deeper)
terraform validateChecks the files for syntax and structural mistakes. (dig deeper)
terraform planPreviews exactly what would be created, changed, or destroyed — without touching anything yet. (dig deeper)
terraform applyActually creates or updates the Azure resources to match the plan. (dig deeper)
terraform destroyRemoves everything Terraform created, cleanly. (dig deeper)
terraform outputPrints the values from outputs.tf after a run. (dig deeper)
terraform testRuns this project's automated configuration tests, with no real Azure resources involved. (dig deeper)

In this project, the day-to-day plan / apply / destroy steps are triggered through the Terraform infrastructure GitHub Actions workflow rather than run by hand every time — see terraform/README.md for the full setup. This tutorial focuses on what the .tf files themselves mean, which is the same either way.

Pick a command to learn go deeper

The table above only glosses over what each command does. Each page below covers one command in detail: what it actually does, where to run it from, what has to be true before you run it, what will be true after it succeeds, and the errors people run into most often.

Codey inspecting something with a magnifying glass
eight commands, one at a time

Pick a file to learn go deeper

Each page below covers one file: its purpose, a fuller explanation, the full code, and a breakdown of what each piece means.

Codey pointing to the left
pick any card below
Codey giving a thumbs up

Nine files, one folder — pick a card above and Codey will walk you through it.