ssprkd.io
Terraform Essentials
Lesson 1 of 7

Core Concepts

Declare the infrastructure you want and let Terraform's providers do the API work — plan previews every change before apply makes it real.

Watch

Speed
📖 Read this walkthrough — every command, and why
Terraform Core Concepts — declare, plan, apply, track

Mental model: Terraform is declarative infrastructure as code. You describe the end
state you want in HCL; Terraform figures out how to reach it. You don't write step-by-step
commands — you declare resources, and each resource maps to a real thing that a provider
knows how to manage. Providers are plugins that make the real API calls. The core loop is:
write HCL -> init (fetch providers) -> plan (preview the diff) -> apply (make it real) ->
state (track what exists).

The config for this walkthrough declares two resources — the desired state, not the steps:

    resource "local_file" "note" {
      filename = "./note.txt"
      content  = "..."
    }

    resource "random_pet" "name" {
      length = 2
    }


1 · terraform init  —  reads the config and downloads the provider plugins

    terraform init
      Initializing provider plugins found in the configuration...
      - Installing hashicorp/local v2.9.0...
      - Installing hashicorp/random v3.9.0...
      Initializing the backend...
      Terraform has been successfully initialized!

    What each part does:
    - init inspects your config, sees it needs the local and random providers, and installs them.
    - Providers are versioned plugins (hashicorp/local v2.9.0, hashicorp/random v3.9.0). They
      are the code that actually creates, reads, updates, and deletes real resources.
    - It also initializes the backend — where state is stored (local by default).


2 · terraform plan  —  the execution plan, a preview diff. It changes NOTHING.

    terraform plan

    Plan compares your desired config against current state and the real world, then prints
    the diff. Everything is marked + because nothing exists yet:

      # random_pet.name will be created
        + id     = (known after apply)
        + length = 2

      # local_file.note will be created
        + filename             = "./note.txt"
        + content              = (known after apply)
        + content_sha256       = (known after apply)
        + id                   = (known after apply)

      Plan: 2 to add, 0 to change, 0 to destroy.

    What each part does:
    - "# random_pet.name will be created" — the resource address (type.name) and the action.
    - + is create, ~ is update in place, - is destroy (you'll see those on later runs).
    - (known after apply) means the value is computed by the provider at apply time, so plan
      can't show it yet (an id, a hash, generated content).
    - "Plan: 2 to add, 0 to change, 0 to destroy" is the summary. Read this line first.


3 · terraform apply  —  execute the plan; providers make the real API calls

    terraform apply

    Apply builds the dependency graph, then walks it. Independent resources run in parallel;
    dependent ones wait. Watch each resource go Creating... then Creation complete:

      random_pet.name: Creating...
      random_pet.name: Creation complete after 0s [id=close-glider]
      local_file.note: Creating...
      local_file.note: Creation complete after 0s [id=fe3768923dd35f1ef683189ae035f90a2034dac6]

      Apply complete! Resources: 2 added, 0 changed, 0 destroyed.

    What each part does:
    - The provider translates each declared resource into a real API call and creates it.
    - [id=close-glider] is the real resource id assigned at creation — that's the pet name the
      random provider generated. Values that were "known after apply" now have concrete values.
    - "Apply complete! Resources: 2 added, 0 changed, 0 destroyed" is the outcome summary.


4 · state  —  Terraform records everything it manages; state is the source of truth

    terraform state list
      local_file.note
      random_pet.name

    terraform output
      filename = "./note.txt"
      pet      = "close-glider"

    What each part does:
    - Every managed resource is written to the state file by its address (local_file.note,
      random_pet.name). State is how Terraform knows what already exists.
    - The next plan reads state (not a fresh discovery from scratch) to compute the diff, so it
      already knows these two resources are there and shows "No changes" until config drifts.


Providers as plugins
    hashicorp/local  v2.9.0   manages files on disk (local_file.note -> ./note.txt)
    hashicorp/random v3.9.0   generates values (random_pet.name -> close-glider)
    Providers are downloaded by init, pinned to a version, and do all the real work at apply.

The dependency graph
    Terraform doesn't run resources top-to-bottom in file order. It builds a graph from the
    references between resources, creates independent ones in parallel, and orders dependent
    ones automatically. Here random_pet.name and local_file.note are independent, so both are
    created without you sequencing them.

Notes
    - plan is safe: it never mutates real infrastructure. Only apply does.
    - apply re-runs plan and asks for confirmation before making changes (unless -auto-approve).
    - Resource address = <type>.<name> (random_pet.name). That address is stable across init,
      plan, apply, and state — it's how every command refers to the resource.

Gotchas
    - Don't hand-edit the file Terraform manages. Editing note.txt out-of-band is drift: the
      next plan sees the file no longer matches state and plans to restore it to the configured
      content. Change the config, not the managed resource.
    - Never edit terraform.tfstate by hand — it's Terraform's source of truth. Use terraform
      state commands (or import) instead.
    - (known after apply) in a plan is normal for computed values (ids, hashes); it is not an
      error and doesn't mean something is missing.
    - Re-running init after adding a provider or changing versions is required — a missing or
      stale plugin surfaces at init, not apply.

Verify
    terraform plan
      No changes. Your infrastructure matches the configuration.
    A clean plan right after apply confirms real world, config, and state all agree.

Takeaway
    Declare in HCL -> init to fetch providers -> plan to preview the diff -> apply to make it
    real -> state to track it. Declare, plan, apply — that's infrastructure as code.