Chapter 1: Foundations
Topic 06

Your First Configuration

Workflow

This is the loop every Terraform user runs hundreds of times a day: write a .tf file, terraform init to download providers, terraform plan to preview changes, terraform apply to make them, and terraform destroy to tear them down. Understanding what each step reads and writes — especially that apply creates a state file — is the foundation everything later builds on.

The single most important habit in this whole loop is reading the plan before applying it. The plan is Terraform telling you exactly what it is about to do; skipping it is how people accidentally destroy and recreate a database they meant only to tweak.

The core loop
init — download providers
plan — preview the diff
apply — make it real
destroy — tear down

Writing the Configuration

A configuration is one or more .tf files in a directory. The minimal one below requires the AWS provider, configures a region, and declares a single S3 bucket. There is no "create" instruction — you declare the bucket that should exist, and Terraform works out that it needs to create it because it does not exist yet.

main.tf — a minimal configuration
terraform {
  required_providers {
    aws = { source = "hashicorp/aws", version = "~> 6.0" }
  }
}

provider "aws" {
  region = "us-east-1"
}

resource "aws_s3_bucket" "logs" {
  bucket = "my-app-logs-7f3a"
}

init — Prepare the Working Directory

terraform init reads the configuration, downloads the providers it declares into a local .terraform/ directory, writes the dependency lock file that records exactly which provider versions were chosen, and initializes the backend where state will live. You run it once when you start, and again whenever you add a provider or module. Until you run it, no other command will work, because the providers are not present.

plan — Preview the Changes

terraform plan refreshes the current state from the real world, compares it to your configuration, and prints exactly what it would do — without changing anything. Each line is marked: + to create, ~ to update in place, - to destroy, and -/+ to destroy and recreate. The plan for our bucket shows a single create and changes nothing yet.

terraform plan — a preview, nothing changes
# aws_s3_bucket.logs will be created
+ resource "aws_s3_bucket" "logs" {
    + bucket = "my-app-logs-7f3a"
    + id     = (known after apply)
  }

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

apply — Make It Real

terraform apply shows the same plan, asks for confirmation, then executes it — calling the AWS APIs to create the bucket — and records the result in terraform.tfstate. That state file is the new, critical artifact: it maps aws_s3_bucket.logs to the real bucket so the next plan can compute a precise diff. In automation you pair plan -out=tfplan with apply tfplan so apply executes exactly the plan you reviewed.

destroy — Clean Up

terraform destroy plans and executes the removal of everything in state — the clean way to tear down a sandbox so you stop paying for it. It is the inverse of apply, and it operates on what is in state. That detail matters: deleting the .tfstate file by hand does not delete your infrastructure, it just makes Terraform forget the bucket exists, leaving it orphaned in AWS for you to clean up by hand.

Common Mistakes
  • Skipping plan and applying directly, then being surprised when apply destroys and recreates a resource you only meant to tweak.
  • Running apply without reading the plan's destroy lines — the summary count (1 to destroy) is the warning you ignored.
  • Editing the configuration and forgetting to re-run init after adding a provider or module, then getting a "provider not installed" error.
  • Deleting the .tfstate file to "start over" while resources still exist in AWS, orphaning them so you must clean up by hand.
  • Assuming apply on a saved plan re-plans against current reality — it executes the frozen plan, which is the point but surprises people.
Best Practices
  • Always run and read terraform plan before apply, treating the destroy and replace lines as the thing to verify.
  • Use plan -out=tfplan then apply tfplan in any non-trivial workflow so you apply exactly what you reviewed.
  • Never hand-delete the state file; use destroy to remove resources or state rm to forget them deliberately.
  • Start every experiment in a throwaway directory or account so destroy fully cleans up.
  • Re-run init after adding any provider or module, before the next plan.
Comparable tools Pulumi up and destroy mirror plan-and-apply CloudFormation change sets are the analog of plan Ansible has no plan or destroy — it does not track state

Knowledge Check

What does terraform plan do?

  • Refreshes state, compares it to the config, and prints what it would change — without changing anything
  • Downloads the providers, installs the required plugins, and initializes the configured backend before any planning starts
  • Applies the changes after a confirmation prompt
  • Deletes all resources currently tracked in state

You delete the terraform.tfstate file while the S3 bucket still exists in AWS. What happens?

  • The bucket keeps running but Terraform forgets it exists, orphaning it
  • The bucket is automatically deleted from AWS
  • Terraform recreates the state from AWS on the next plan automatically
  • Nothing changes, because state is just a cache that is rebuilt every run

When must you re-run terraform init?

  • After adding a new provider or module to the configuration
  • Before every single plan, without exception
  • Only the very first time you ever use Terraform on the machine
  • After every apply, to refresh the state file

Why use plan -out=tfplan followed by apply tfplan in automation?

  • So apply executes exactly the plan that was reviewed, not a freshly recomputed one
  • It is the only way to run Terraform without internet access, since the saved file bundles every provider plugin offline
  • It skips the refresh step to make apply faster
  • It encrypts the plan so it cannot be read

You got correct