Skip to main content
RunBook Academy

← All labs in Terraform

Lab · foundation · ~25 min

Lab: First Terraform Configuration

C · Simulation

Objectives

  • Install Terraform CLI and verify the version
  • Write a first Terraform configuration that creates a local file
  • Run the daily workflow: init, validate, plan, apply
  • Inspect the resulting state
  • Destroy the resource and verify the cleanup

Prerequisites

Objective

By the end of this lab, you will have:

  • Installed the Terraform CLI and verified the version.
  • Written a first Terraform configuration that uses the hashicorp/local provider to create a file on disk.
  • Run the daily workflow: init, validate, plan, apply, destroy.
  • Inspected the resulting state file.
  • Verified the cleanup.

The lab uses the local provider, which is a provider that creates files on the local filesystem. No cloud account is required. The lab is disposable: every step has a corresponding cleanup step.

Architecture

A single Terraform configuration that creates one file on the local filesystem:

+-----------------------+
| Terraform             |
|         ↓             |
| hashicorp/local       |
|         ↓             |
| local_file.hello.txt  |
|         ↓             |
| $HOME/rb-academy-lab/  |
|   └── hello.txt       |
+-----------------------+

Requirements

  • A Linux or macOS workstation with shell access.
  • The Terraform CLI 1.9.x or later installed.
  • A writable directory (e.g. under $HOME).

Verify the Terraform version:

terraform version

Expected output (truncated):

Terraform v1.9.x
on linux_amd64

If the version is older, see the Terraform install guide.

Scenario

You have a fresh workstation. You want to create a file hello.txt in a working directory. You have decided to manage the file with Terraform as a first exercise.

The file is empty. The configuration is small. The lesson is the workflow, not the resource.

Tasks

Task 1: Create the working directory

mkdir -p ~/rb-academy-lab
cd ~/rb-academy-lab

Verify the directory is empty:

ls -la ~/rb-academy-lab

Task 2: Write the configuration

Create a file main.tf:

terraform {
  required_version = ">= 1.9.0"
  required_providers {
    local = {
      source  = "hashicorp/local"
      version = "~> 2.5"
    }
  }
}

resource "local_file" "hello" {
  filename = "${path.module}/hello.txt"
  content  = "Hello from Terraform!\n"
}

The configuration:

  • Pins Terraform to 1.9.x or later.
  • Pins the local provider to the 2.5.x series.
  • Declares one resource, local_file.hello, which creates a file hello.txt in the working directory.

Task 3: Format the configuration

terraform fmt

Verify the file is unchanged (the configuration is already formatted):

git diff main.tf

Task 4: Initialise the working directory

terraform init

The output:

Initializing the backend...
Initializing provider plugins...
- Installing hashicorp/local v2.5.x...
- Installed hashicorp/local v2.5.x
Terraform has been successfully initialized!

What init did:

  • Created a .terraform/ directory.
  • Downloaded the hashicorp/local provider.
  • Created a .terraform.lock.hcl file with the lock.

Verify:

ls -la ~/rb-academy-lab

Expected:

.terraform/
.terraform.lock.hcl
main.tf

Task 5: Validate the configuration

terraform validate

Expected output:

Success! The configuration is valid.

The validation is a sanity check. The actual resource creation happens in the apply step.

Task 6: Plan the apply

terraform plan

Expected output:

Terraform will perform the following actions:

  # local_file.hello will be created
  + resource "local_file" "hello" {
      + content             = "Hello from Terraform!\n"
      + content_base64      = (known after apply)
      + directory_permission = "0777"
      + file_permission      = "0777"
      + filename             = "./hello.txt"
      + id                   = (known after apply)
    }

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

Study the plan output:

  • The plan proposes to create one resource.
  • The content argument matches the configuration.
  • The id is (known after apply) — the provider will assign it during the apply.
  • The file_permission and directory_permission are defaults (the providers 0777).

Task 7: Save the plan

terraform plan -out=hello.tfplan

The plan is saved to hello.tfplan. The file is in the working directory.

Verify the file exists:

ls -la hello.tfplan

Task 8: Inspect the saved plan

terraform show hello.tfplan

The output is the same as the plan output but read from the saved file.

terraform show -json hello.tfplan | head -50

The JSON output is machine-readable. The CI pipeline can parse it.

Task 9: Apply the saved plan

terraform apply hello.tfplan

The output:

local_file.hello: Creating...
local_file.hello: Creation complete after 0s [id=abc123def456]

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

What apply did:

  • Created the file hello.txt.
  • Updated the state file.
  • Saved the state file.

Verify the file exists:

cat ~/rb-academy-lab/hello.txt

Expected:

Hello from Terraform!

Task 10: Inspect the state

terraform state list

Expected:

local_file.hello

The state has one resource.

terraform state show local_file.hello

Expected:

# local_file.hello:
resource "local_file" "hello" {
  content             = "Hello from Terraform!\n"
  content_base64      = "SGVsbG8gZnJvbSBUZXJyYWZvcm0hCg=="
  directory_permission = "0777"
  file_permission      = "0777"
  filename             = "./hello.txt"
  id                   = "abc123def456"
}

The state records every attribute the provider returned.

terraform show

The output is a human-readable summary of the state.

Task 11: Verify idempotency

terraform plan

Expected output:

No changes. Your infrastructure matches the configuration.

The plan is empty. The configuration matches the state.

Task 12: Apply without a saved plan

terraform apply

The plan is computed at apply time. The plan is empty. The apply is a no-op.

The output:

No changes. Your infrastructure matches the configuration.

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

Task 13: Modify the resource

Edit main.tf to change the content:

resource "local_file" "hello" {
  filename = "${path.module}/hello.txt"
  content  = "Hello from Terraform! Updated.\n"
}

Run the plan:

terraform plan

Expected output:

# local_file.hello will be updated in-place
~ resource "local_file" "hello" {
    ~ content             = "Hello from Terraform!\n" -> "Hello from Terraform! Updated.\n"
    ~ content_base64      = "SGVsbG8gZnJvbSBUZXJyYWZvcm0hCg==" -> "SGVsbG8gZnJvbSBUZXJyYWZvcm0hIFVwZGF0ZWQuCg=="
    # (5 unchanged attributes hidden)
  }

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

The plan proposes to update the resource in place. The content attribute changes; the other attributes are unchanged.

Apply:

terraform apply

Verify:

cat ~/rb-academy-lab/hello.txt

Expected:

Hello from Terraform! Updated.

Task 14: Destroy the resource

terraform destroy

The apply is unconfirmed:

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

local_file.hello: Refreshing state... [id=abc123def456]

# local_file.hello will be destroyed
- resource "local_file" "hello" {
    - content             = "Hello from Terraform! Updated.\n" -> null
    - content_base64      = "SGVsbG8gZnJvbSBUZXJyYWZvcm0hIFVwZGF0ZWQuCg==" -> null
    - directory_permission = "0777" -> null
    - file_permission      = "0777" -> null
    - filename             = "./hello.txt" -> null
    - id                   = "abc123def456" -> null
  }

Do you really want to destroy all resources?
  Terraform will destroy all the resources described above, all the
  data they contain, and all the provider configurations.
  There is no undo. Only 'yes' will be accepted to confirm.

  Enter a value: yes

Type yes to confirm.

The output:

local_file.hello: Destroying... [id=abc123def456]
local_file.hello: Destruction complete after 0s

Destroy complete! Resources: 0 destroyed.

Verify the file is gone:

ls -la ~/rb-academy-lab

Expected:

.terraform/
.terraform.lock.hcl
hello.tfplan
main.tf

The hello.txt file is gone. The state is empty.

Validation

The lab is successful if:

  • The Terraform CLI version is 1.9.x or later.
  • The hello.txt file is created and removed by Terraform.
  • The state file matches the configuration.
  • The plan is empty after the apply.
  • The destroy removes the file.

Expected Outcome

At the end of the lab:

+---------------------------------+
| ~/rb-academy-lab/                |
|   .terraform/                    |
|   .terraform.lock.hcl            |
|   hello.tfplan                   |
|   main.tf                        |
|   terraform.tfstate              |
+---------------------------------+

The hello.txt file is gone. The state is empty. The configuration is the only source of truth for what Terraform believes exists.

Troubleshooting

terraform init fails with provider download error

Error: Failed to install provider

Verify the network connection:

curl -fsSL https://registry.terraform.io/.well-known/terraform.json

If the registry is unreachable, check the corporate firewall or proxy settings.

terraform apply fails with permission denied

The local provider writes to the current working directory. If the working directory is unwritable, the apply fails.

ls -ld ~/rb-academy-lab
chmod 755 ~/rb-academy-lab

The state file is locked

A previous apply may have crashed. The lock file is in the .terraform/ directory. Delete the lock file and try again:

rm -rf .terraform/
terraform init

Cleanup

The end state is a working directory with the configuration and the state. To fully clean up:

cd ~/rb-academy-lab
terraform destroy   # if any resources remain
rm -rf .terraform .terraform.lock.hcl hello.tfplan terraform.tfstate

The main.tf is the only artefact worth keeping. Move it to a permanent location (e.g. ~/rb-academy-configs/first-terraform/).

What You Learned

You learned the daily Terraform workflow:

  • terraform fmt enforces standard formatting.
  • terraform init downloads providers and prepares the working directory.
  • terraform validate performs static checks.
  • terraform plan produces the operational artefact.
  • terraform apply makes the plan real.
  • terraform destroy cleanly removes the resources.

You also learned:

  • The state file is the source of truth for what Terraform believes exists.
  • The idempotency of the plan is the safety net.
  • The save-plan workflow is the production pattern.
  • The .terraform.lock.hcl is the reproducibility contract.

Deliverables

  • · A working directory with a Terraform configuration
  • · A terraform.tfstate file with one resource
  • · A hello.txt file created by Terraform
  • · A terminal log of the apply

Verification status

Last reviewed
2026-08-12
Executed end to end
not yet run on hardware

The commands and configuration here have been reviewed against the verified software versions, but nobody has run this lab start to finish on a system meeting its prerequisites. Treat the Expected Outcome as the intended result rather than an observed one, and keep the Cleanup section to hand.