Skip to content
Commitpedia
Log in Join & edit

First Terraform configuration: variables, a module and outputs (no cloud needed)

A complete Terraform project that writes local files with the hashicorp/local provider, so you can practice init, plan, apply, modules and destroy safely without a cloud account.

Not checked by a trusted editor yet Last edited Outdated?

Edit history of First Terraform configuration: variables, a module and outputs (no cloud needed): 1 recorded edit
Edit history · 1 edit
Type
Script
Edits
1
Editors
1
First issued
15 Oct 2026
Last edit
15 Oct 2026
Language
hcl
Licence
CC0-1.0

What this changes

terraform init downloads the hashicorp/local provider into .terraform/ and writes .terraform.lock.hcl. apply creates out/ENV-inventory.ini, out/ENV-README.txt and terraform.tfstate in the tf-sandbox folder. No cloud resources are created.

How to undo it

Run terraform destroy to delete the files in out/, then rm -rf .terraform .terraform.lock.hcl terraform.tfstate*. Or delete the whole tf-sandbox folder.

Learning Terraform against a real cloud costs money and can leave stray resources behind. The hashicorp/local provider manages files on your own disk, which makes it a safe sandbox: the workflow, state, variables and modules behave exactly as they do with cloud providers. The same files work with OpenTofu; replace terraform with tofu in the commands.

Project layout

tf-sandbox/
  main.tf
  modules/
    note/
      main.tf

main.tf (root module)

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

variable "environment" {
  description = "Name of the environment, used in file names"
  type        = string
  default     = "dev"
}

variable "servers" {
  description = "Hostnames that should appear in the generated inventory"
  type        = list(string)
  default     = ["web1.example.com", "web2.example.com"]
}

resource "local_file" "inventory" {
  filename        = "${path.module}/out/${var.environment}-inventory.ini"
  content         = "[web]\n${join("\n", var.servers)}\n"
  file_permission = "0644"
}

module "readme" {
  source = "./modules/note"

  file_path = "${path.module}/out/${var.environment}-README.txt"
  text      = "Generated by Terraform for the ${var.environment} environment."
}

output "inventory_file" {
  value = local_file.inventory.filename
}

output "readme_file" {
  value = module.readme.file_path
}

modules/note/main.tf (child module)

terraform {
  required_providers {
    local = {
      source = "hashicorp/local"
    }
  }
}

variable "file_path" {
  description = "Where to write the note"
  type        = string
}

variable "text" {
  description = "Content of the note"
  type        = string
}

resource "local_file" "this" {
  filename        = var.file_path
  content         = "${var.text}\n"
  file_permission = "0644"
}

output "file_path" {
  value = local_file.this.filename
}

The child module declares its own inputs (file_path, text) and an output. The root module passes values in through the module block and reads the result as module.readme.file_path. path.module is the folder of the module the expression is written in.

Run it step by step

  1. terraform init downloads the local provider, installs the module and creates .terraform.lock.hcl.
  2. terraform fmt and terraform validate format the code and check it for errors.
  3. terraform plan -out=tfplan shows two resources to add (+) and saves the plan to a file.
  4. terraform apply tfplan applies exactly the saved plan, without asking again. The files appear under out/ and the outputs are printed.
  5. Run terraform plan again: it reports no changes, because state and reality match the code.

Make changes

terraform plan -var 'environment=test'
terraform apply -var 'servers=["web1.example.com","web2.example.com","web3.example.com"]'
terraform state list
terraform output inventory_file

Changing environment changes the file names, so the plan shows -/+: the old files are replaced by new ones. Changing servers only changes the content, which for this resource also means replace. terraform state list shows the addresses Terraform tracks, such as local_file.inventory and module.readme.local_file.this.

Delete one of the generated files by hand and run terraform plan: Terraform notices the object is gone and plans to create it again. This is drift detection.

Clean up

Warning: destroy deletes every resource in the state. Here that is only the generated files, but in a real project it means servers and data. Always preview first with terraform plan -destroy and check which project folder and backend you are in.

terraform plan -destroy
terraform destroy

Do not commit terraform.tfstate, tfplan or the .terraform/ folder to Git. Do commit the .tf files and .terraform.lock.hcl.

Official documentation

See also

Repos in the wild

History

Every saved edit is kept. The plot on the sleeve draws one line per edit. Open any edit to see exactly what changed.

  1. Commitpedia +4,266 chars

Report a problem

Something wrong, unsafe or copied? Tell the editors.
What is wrong?