Terraform — Lesson 1: Your First Run
PrototypeThe lab terminal is a high-fidelity simulation of the Terraform CLI — it behaves like the real CLI for everything in this lesson. A real apply against AWS would need a cloud account; this lesson needs none.
Your First Terraform Run
Clicking works—until you must do it again
Imagine you build a server by clicking through a cloud dashboard. It works perfectly. A week later, a teammate needs the same setup in another region. Which options did you choose? Which security rule came first? One forgotten click can leave the second server different from the first.
That is the problem Terraform solves. Terraform is an infrastructure as code tool: you describe the infrastructure you want in human-readable configuration files instead of rebuilding it from memory. Those files can be reviewed, versioned, reused, and shared like application code.
Infrastructure can be compute, storage, networks, DNS records, or features in other services. Terraform manages those objects through their APIs and follows the same configuration throughout their lifecycle.
Never let the crew start before you see the plan
A repeatable file solves one problem, but it creates another: what if a change in that file replaces the wrong server or removes something important? You need to see the consequences before anything happens.
Terraform turns that rhythm into three steps: Write → Plan → Apply.
Write what you want. Plan to see what will change. Apply only after you review it. The plan compares your configuration with existing infrastructure and shows what will be created, updated, or destroyed. Apply then performs the approved operations in dependency order.
One tool must speak to many platforms
A server may live in AWS, a DNS record somewhere else, and a repository in GitHub. Their APIs do not all speak the same language. Terraform needs a way to communicate with each one without becoming a completely different tool every time.
In Terraform, that interpreter is called a provider. A provider is a plugin that translates Terraform configuration into API calls for a platform such as AWS, Azure, Google Cloud, Kubernetes, or GitHub.
The individual object in the work order—a virtual machine, DNS record, or repository—is called a resource. Terraform tracks and manages each resource through its provider.
How does Terraform know what already exists?
Now imagine you return to the project next month. The configuration says what should exist, and the cloud contains what actually exists. Terraform must know which real object belongs to which line of configuration before it can safely calculate the next change.
Terraform calls that ledger state. State maps your configuration to real-world objects, helping Terraform decide what already exists and what must change to make reality match the files.
Practice the workflow without a cloud bill
Your first practice run should not require AWS credentials, a provider download, or the risk of creating something chargeable. You need the Terraform lifecycle without the external infrastructure.
For this lesson, that simulator is the built-in terraform_data resource. It follows Terraform’s normal resource lifecycle but performs no other real-world action. It accepts an input, stores the value in state, and exposes it as output after apply.
That lets you practice the real init → plan → apply → destroy sequence with zero cost and zero setup.
The order matters more than memorizing commands
Imagine walking into a workshop and switching on every machine at random. Even if you know each button, the job will go wrong. Terraform commands also have a sequence: prepare the workspace, inspect the proposed work, approve it, read the result, and clean up.
terraform initPrepare the working directory. Run it first after writing a new configuration; it is safe to run again.terraform planCalculate and show the changes Terraform proposes.terraform applyShow a plan, ask for approval, and perform the approved changes.terraform outputRead output values from state.terraform destroyPlan and remove all resources managed by the current configuration.validate · fmt · state · versionCheck validity, format files, inspect state, and identify the CLI version.Write what you want. Plan to see what will change. Apply only after you review it. Keep that sentence in mind; you will follow it in the lab.
Tip: use ↑/↓ for history · type help · edit main.tf below, then Save.
Variables & Outputs: Stop Hardcoding
One copied blueprint becomes three drifting snowflakes
In Lesson 1, the resource contained input = "Hello from Terraform!". That worked for one run. Now imagine three environments—development, staging, and production—and each needs a different greeting. Will you copy main.tf three times? Will you edit the code whenever a value changes?
That is how configuration rot begins: one blueprint drifts into three snowflakes, and soon nobody knows which copy is safe to change.
In Terraform, those blanks are input variables. Declare a variable once, then refer to it as var.name wherever the value belongs. The configuration stays stable while the input changes.
variable "greeting" {
type = string
description = "Greeting for this environment"
default = "Hello from development!"
}
resource "terraform_data" "hello" {
input = var.greeting
}
A variable block has a name and can include a type, description, and default. If there is no default, Terraform requires a value before it can build a plan.
Keep environment values outside the blueprint
Replacing a hardcoded value with a variable is useful, but typing every answer by hand soon becomes its own source of mistakes. You need a repeatable set of answers for each environment.
main.tf; keep the development answers in one file and production answers in another. Terraform automatically loads terraform.tfvars and files ending in .auto.tfvars.# terraform.tfvars greeting = "Hello from staging!"
For explicitly named files such as dev.tfvars or prod.tfvars, pass the file with -var-file. The lesson lab keeps the first step small and uses the automatically loaded terraform.tfvars.
When two answers exist, Terraform needs a winner
Suppose the variable has a default, terraform.tfvars supplies another value, and the command line supplies a third. Terraform cannot use all three. It follows a precedence order.
From lower to higher precedence, the main sources are:
- The
defaultin the variable block TF_VAR_environment variablesterraform.tfvars, thenterraform.tfvars.json, then*.auto.tfvarsfiles-varand-var-fileoptions, in the order supplied on the command line
The lab simulates the path you will use today: default → terraform.tfvars → -var. Each step overrides the one before it.
Give repeated calculations a useful name
Variables bring values into a configuration. But sometimes the configuration needs to combine or transform those values. Repeating the same expression in several places makes the blueprint noisy and easier to break.
Terraform calls these named internal expressions local values. Declare them in a locals block and reference one with the singular form local.name.
locals {
full_greeting = "${var.greeting} Welcome to Lesson 2."
}
output "greeting" {
value = local.full_greeting
}
Outputs carry the resolved answer forward
You already used an output in Lesson 1. An output exposes a value after apply. The only change now is that the value can be built from a variable or a local instead of a hardcoded string.
The workflow has not changed: Write → Plan → Apply. Write one reusable configuration. Plan with the values for this run. Apply only after you review the resolved result.
Same configuration, different values per environment. You will repeat that pattern in the lab until it feels natural.
Same starting point as Lesson 1 — nothing new assumed.
Tip: use ↑/↓ for history · type help · terraform console opens the expression console.