API & Automation·7 min read·

Terraform Basics: Providers, State, and Plan

Start with Terraform by wiring a provider, declaring resources, and learning how plan plus state turn infrastructure changes into a safe diff.

NB

Netbay Engineering

Netbay Engineering

On this page

Terraform is the tool teams reach for when they want to treat infrastructure itself as code that can be reviewed, diffed, and rolled back, rather than the result of a string of API clicks. It is declarative: you write the end state, and Terraform figures out the steps to reach it. This post walks the three core pieces — providers, state, and the plan command — using a cloud server as the running example.

Providers: The Bridge to an API

A provider is the plugin that translates Terraform's generic resource vocabulary into calls against a real API. Each cloud provider ships one, and you declare which you need and roughly which version:

hcl
terraform {
  required_providers {
    hcloud = {
      source  = "hetznercloud/hcloud"
      version = "~> 1.40"
    }
  }
}

resource "hcloud_ssh_key" "deploy" {
  name       = "deploy key"
  public_key = file("~/.ssh/id_ed25519.pub")
}

resource "hcloud_server" "web" {
  name        = "web01"
  server_type = "cx21"
  image       = "ubuntu-24.04"
  location    = "fsn1"
  ssh_keys    = [hcloud_ssh_key.deploy.id]
}

Two things stand out. First, resources reference each other by symbolic name — the server pulls the ssh key's id via hcloud_ssh_key.deploy.id — so Terraform can order creation automatically. Second, everything is data plus references; there is no step-by-step procedure here, only the shape of the world you want. Before the first resource exists you will also need API credentials in environment variables, which the provider documentation spells out.

State: Terraform's Memory

After you apply for the first time, Terraform writes a state file recording what actually exists and its resource IDs. That file is the bridge between your code and reality: plan compares the configuration against state, not against a fresh API query. This is why deleting the state file is effectively forgetting you own infrastructure — the running resources still bill you while Terraform thinks they never existed. Treat state as precious data, and keep it out of git, a topic covered fully in the next post.

Plan and Apply

The workflow is deliberately small and safe:

bash
terraform init
terraform plan -out plan.tfplan
terraform apply plan.tfplan
terraform show plan.tfplan

init downloads the provider plugin; plan computes a diff between configuration and state and writes it to a file; apply executes exactly that diff; show renders the recorded diff for later review. The plan shows, in plain text, what will be created, changed, or destroyed, with a count at the end — the reviewable artifact that lets a colleague approve an infrastructure change the way they would approve a code change. If the plan surprises you, destroy nothing: just run terraform plan again after fixing the configuration, which is exactly how you experiment without breaking anything.

Terraform workflow terraform init provider plugins terraform plan diff vs state terraform apply execute plan file state file state is written on every apply

Takeaway

Learn the trio as a unit: provider declares the API, state remembers reality, plan makes change reviewable. Everything else — workspaces, modules, backends — builds on these three, and most problems beginners hit trace back to one of them. To get real API responses into your practice session, pair this with a provider you can afford to create and destroy repeatedly; for plain VPS territory, Netbay's own API and dashboard on netbayhosts.in let you keep a scratch instance alive for exactly that kind of experimenting.

Keep reading

Follow along on a real VPS

Deploy Linux in under 60 seconds

These guides are written against Ubuntu, Debian, and RHEL-family images — the same ones on NetBay.

Deploy an instance