On this page
Why clicking in consoles stops working
Your first cloud resources come from clicking. You open the AWS console, click through a wizard, create a server. It works, and clicking feels productive. Then reality arrives: you need an identical staging environment and can't remember the 14 settings you picked. A teammate asks how the network is configured and the only honest answer is "let me click around and check." Something breaks and nobody knows what changed, because clicks leave no record. Clicking doesn't scale, doesn't repeat, and doesn't remember.
The fix is to describe your infrastructure in text files, commit them to Git, and let a tool build exactly what the files say. That's Infrastructure as Code (IaC), and Terraform is the most widely used tool for it. This article gets you from zero to understanding the whole core loop, and writing real Terraform you can run.
Who this is for
Anyone who's created cloud resources by clicking and sensed there must be a better way. No Terraform experience needed. Knowing roughly what a cloud server or network is helps, but we explain as we go. Examples use AWS; Terraform works with every major provider.
What Infrastructure as Code actually is
Infrastructure as Code means writing down what your infrastructure should look like in files you can version, review, and reuse, then having a tool make reality match the files.
The mental shift is from *doing* to *describing*. You don't perform the steps to create a server. You write down "I want one server, this size, in this network," and Terraform figures out the steps. If the server doesn't exist, it creates it. If it already matches, it does nothing. The analogy that makes it click:
Because the recipe is a text file, it goes in Git. Now your infrastructure has history, code review, and a single source of truth. "What changed?" becomes git log. "Make me another environment" becomes copying a folder.
Console clicks vs Terraform
| Clicking the console | Terraform | |
|---|---|---|
| Repeatable | No, you redo it by hand | Yes, same files, same result |
| Record of what exists | In your memory | In Git, readable by anyone |
| Code review before changes | Impossible | It's a pull request |
| Make a second environment | Click everything again | Copy the folder, change a variable |
| Undo a change | Hope you remember | Revert the commit, re-apply |
| Good for | Quick experiments, learning | Anything you'll keep |
Pro tip
It's fine to click around when you're exploring and learning. The rule of thumb: the moment a resource matters enough that you'd be upset if it vanished, it belongs in code.
The four words that are all of Terraform
Terraform looks big, but the day-to-day vocabulary is tiny. Learn these four and you can read almost any Terraform file.
- 1
Provider
The plugin that knows how to talk to a specific cloud, aws, azurerm, google. You declare which provider(s) you're using and where.
- 2
Resource
One thing you want to exist, a server, a network, a database. The bulk of your code is resource blocks.
- 3
Variable
An input you can change without editing the logic, region, instance size, environment name. This is what makes one set of files reusable across environments.
- 4
Output
A value Terraform prints after it builds, the server's IP, the database endpoint, so you (or other tooling) can use it.
Your first real Terraform
Let's write something that runs. Start by declaring the provider, which cloud, and which region. The region comes from a variable so the same files work anywhere.
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
provider "aws" {
region = var.region # value comes from variables.tf
}Now declare inputs as variables. Giving them defaults makes them optional; leaving a default off makes Terraform ask (or require) a value.
variable "region" {
description = "AWS region to deploy into"
type = string
default = "eu-west-1"
}
variable "environment" {
description = "Environment name, e.g. dev or prod"
type = string
default = "dev"
}Then the actual resource. This is a real, free-tier-friendly S3 bucket, a good first resource because it's simple and hard to break. Notice how it reads the variable to tag itself.
resource "aws_s3_bucket" "app_data" {
bucket = "thesimplifiedtech-${var.environment}-app-data"
tags = {
Environment = var.environment
ManagedBy = "terraform" # so humans know not to click-edit it
}
}Finally, an output so Terraform tells you what it made instead of making you go look.
output "bucket_name" {
description = "Name of the created bucket"
value = aws_s3_bucket.app_data.bucket
}The plan / apply loop
Terraform has exactly one workflow you'll run thousands of times. init once to download the provider, then the loop: plan to preview, apply to make it real.
# Once per project: download providers, set up the working dir
terraform init
# The safety net: shows EXACTLY what will be created/changed/destroyed.
# Changes nothing. Read this every single time.
terraform plan
# Make reality match the files. Asks you to type 'yes' first.
terraform apply
# When you're done with everything: tear it all down cleanly
terraform destroyterraform plan is the feature that makes Terraform safe to learn on. It's a dry run, it reads your files, checks what currently exists, and prints the diff: + for create, ~ for change, - for destroy. Nothing happens until you run apply and type yes.
Always read the plan, especially the minus signs
A `-` (destroy) or `-/+` (replace) in a plan means a resource is about to be deleted and recreated. On a database, that's data loss. The plan is telling you before it happens, but only if you read it. Never apply a plan you didn't look at.
State: how Terraform remembers
Here's the concept beginners trip over. How does Terraform know your bucket already exists so it doesn't make a second one? It keeps a state file, a record of every resource it created and its real-world ID. On the next plan, it compares three things: your files (desired), the state (last known), and the real cloud (actual).
This is also idempotency in action: run apply ten times with no file changes and Terraform does nothing nine of those times, because state already matches your files. "Make it so" is safe to repeat, that's the whole promise of IaC.
State is precious, and a secret
The state file (terraform.tfstate) can contain sensitive values and is the source of truth Terraform trusts. Two rules: never commit it to Git (it can hold secrets), and for any team, store it in remote state (an S3 bucket with locking) so two people can't apply at once and corrupt it. Solo learning on your laptop is fine; teams need remote state from day one.
Common mistakes that cost hours
- Running apply without reading the plan. The plan literally tells you what's about to be destroyed. Skipping it is how people accidentally delete production databases.
- Editing resources in the console after Terraform made them. Now reality and state disagree. Next
applymay revert your click, or get confused. If it's in Terraform, change it in Terraform. - Committing the state file or .tfvars secrets to Git. State can contain secrets; tfvars often hold passwords. .gitignore both before your first commit.
- No remote state on a team. Two people apply at once with local state and corrupt it. Use a remote backend with locking the moment more than one person touches the code.
- Hardcoding values that should be variables. Region, sizes, and names hardcoded means you can't reuse the files for a second environment. If it changes between environments, make it a variable.
- Not pinning provider versions. Without a version constraint, a provider upgrade can change behaviour under you. Pin with
~>and upgrade deliberately.
Where to go next
The whole article in 6 lines
- **IaC** = describe infrastructure in version-controlled files; a tool makes reality match them.
- Terraform's vocabulary is tiny: **provider** (which cloud), **resource** (a thing), **variable** (input), **output** (result).
- The loop is **init โ plan โ apply**. `plan` is a free dry run, always read it.
- **State** is how Terraform remembers what it built; it powers idempotency and must never be committed.
- Don't click-edit what Terraform made, change it in code, or state and reality drift.
- Variables turn one set of files into many environments; that reuse is the whole point.
Reading Terraform is easy; the fluency comes from running the loop yourself until plan and apply feel automatic. Go build something real:
- Run the full init/plan/apply loop in the browser: the Terraform Lab, then the broader Infrastructure as Code Lab.
- Go deeper with the interactive lesson: Infrastructure as Code.
- Apply Terraform to a real network, build a VPC from scratch in Cloud Networking Fundamentals: VPC.
- See where IaC fits the bigger picture: the Cloud Engineer path.
Create that S3 bucket with Terraform today, change the environment variable, and apply it again to make a second one. The moment you watch one variable spin up a whole new environment, you'll never want to click a console again.
Want to go deeper?
This article covers concepts taught hands-on in the Cloud Engineer and DevOps career paths, with real terminal labs, production scenarios, and structured lessons.