Skip to content

1.02 Terraform: HCL Basics, Resource Creation, Update & Destroy

Overview

This page covers the full Terraform resource lifecycle — from writing your first HCL configuration file and running the core workflow commands, through to updating and destroying infrastructure.

Abstract

Terraform uses HCL (HashiCorp Configuration Language) to declare infrastructure as code in .tf files. Resources are created with terraform initplanapply, updated by modifying arguments and re-applying, and removed with terraform destroy. Updates to certain arguments trigger an immutable destroy-then-recreate cycle rather than an in-place patch.


Part 1 — HCL Syntax & Creating Your First Resource

HCL File Structure

An HCL file is made up of blocks and arguments.

  • A block is enclosed in curly braces {} and contains configuration data.
  • Arguments are key-value pairs inside a block that define the resource's properties.

The most important block type in Terraform is the resource block — it is mandatory for deploying any infrastructure resource.

Resource Block Anatomy

resource "local_file" "pet" {
  filename = "/root/pets.txt"
  content  = "We love pets!"
}

Each part of the resource block has a specific role:

Element Example Description
Block type resource Keyword identifying this as a resource block
Resource type "local_file" Fixed value — provider + resource kind
Provider local Word before the _ in the resource type
Resource kind file Word after the _ in the resource type
Resource name "pet" Logical label — can be anything; used for internal reference
Arguments filename, content Key-value pairs specific to this resource type

Note

The resource name ("pet") is a local identifier only — it does not affect the actual resource created on the system.

Setting Up a Working Directory

# Create and enter a new working directory
mkdir /root/terraform-local-file
cd /root/terraform-local-file

Create local.tf in this directory with the resource block above.

Resource Examples Across Providers

resource "local_file" "pet" {
  filename = "/root/pets.txt"
  content  = "We love pets!"
}
# aws-ec2.tf
resource "aws_instance" "webserver" {
  ami           = "ami-0c2f25c1f66a1ff4d"
  instance_type = "t2.micro"
}
# aws-s3.tf
resource "aws_s3_bucket" "data" {
  bucket = "webserver-bucket-org-2207"
  acl    = "private"
}

Terraform Providers

The word before the _ in a resource type identifies the provider. Terraform supports 100+ providers. Common examples:

Provider Platform
local Local filesystem
aws Amazon Web Services
azurerm Microsoft Azure
google Google Cloud Platform
alicloud Alibaba Cloud

Each provider has its own set of resource types and required/optional arguments. Always refer to the Terraform documentation as the source of truth — it is impossible (and unnecessary) to memorise all arguments.


Core Terraform Workflow

Write config → terraform init → terraform plan → terraform apply

Step 1 — terraform init

terraform init

Reads the configuration, identifies the required provider from the resource type, and downloads the provider plugin into the working directory.

Initializing provider plugins...
- Finding latest version of hashicorp/local...
- Installing hashicorp/local v1.4.0...
- Installed hashicorp/local v1.4.0 (signed by HashiCorp)

Terraform has been successfully initialized!

Note

terraform init must be run once per working directory, and again any time a new provider is added. It is safe to re-run at any time.

Step 2 — terraform plan

terraform plan

Shows the execution plan — what Terraform will create, update, or destroy — without making any changes. Output is similar to a git diff.

# local_file.pet will be created
+ resource "local_file" "pet" {
    + content              = "We love pets!"
    + directory_permission = "0777"
    + file_permission      = "0777"
    + filename             = "/root/pets.txt"
    + id                   = (known after apply)
  }

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

The + symbol means the resource will be created. Optional arguments not declared in the config (like directory_permission) are shown with their default values.

Step 3 — terraform apply

terraform apply

Displays the plan again, then prompts for confirmation before executing:

Do you want to perform these actions?
  Only 'yes' will be accepted to approve.

  Enter a value: yes

local_file.pet: Creating...
local_file.pet: Creation complete after 0s [id=521c5c732c78cb42cc9513ecc7c0638c4a115b55]
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

Verify the Resource

# View the created file
cat /root/pets.txt

# Inspect the Terraform state for this resource
terraform show

terraform show reads the state file and displays the full details of every managed resource.


local_file Argument Reference

The local_file resource accepts the following arguments (from Terraform documentation):

Argument Required Default Description
filename ✅ Yes Absolute path of the file to create
content Optional Plain text content for the file
sensitive_content Optional Content hidden from plan/log output
content_base64 Optional Base64-encoded binary content
file_permission Optional "0777" Unix file permission string
directory_permission Optional "0777" Unix permission for parent directories

Note

content, sensitive_content, and content_base64 are mutually exclusive — only one can be used at a time.


Part 2 — Update & Destroy Infrastructure

Overview

Once a resource is created with Terraform, it can be updated by modifying the configuration file and re-applying, or permanently removed using terraform destroy.

Abstract

Terraform manages the full lifecycle of infrastructure — create, update, and destroy. Updates that change immutable arguments trigger a destroy-then-recreate cycle (immutable infrastructure pattern). The terraform destroy command tears down everything in the current working directory. Both operations display an execution plan before making any changes.


Why It Matters in Production

HCL is the language of all Terraform infrastructure. Understanding its block/argument structure is fundamental to reading, writing, and debugging any .tf file. Understanding how Terraform handles updates and deletions prevents accidental data loss — some argument changes silently force resource replacement, and knowing when Terraform will destroy vs. update-in-place is critical before running apply in any environment.


Key Concepts

Concept Description
Block Curly-brace-enclosed section declaring a resource or config
Argument Key-value pair inside a block defining resource properties
Resource type provider_kind string (e.g. local_file, aws_instance)
Resource name Local logical label for the resource within config
Provider Plugin that interfaces with a platform (local, aws, gcp, etc.)
terraform init Downloads required provider plugins
terraform plan Shows what will change — no resources affected
terraform apply Executes the plan after confirmation
Update in place Terraform modifies the resource without destroying it
Forces replacement Argument change requires destroy + recreate (-/+)
Immutable infrastructure Resources are replaced rather than patched
terraform destroy Deletes all resources in the current config directory

Updating a Resource

To update a resource, modify its arguments in the .tf file and re-run terraform plan and terraform apply.

Example — Adding file_permission to a local file

Before (local.tf):

resource "local_file" "pet" {
  filename = "/root/pets.txt"
  content  = "We love pets!"
}

After (adding file_permission):

resource "local_file" "pet" {
  filename        = "/root/pets.txt"
  content         = "We love pets!"
  file_permission = "0700"
}

This sets the file permission to 0700 — owner read/write/execute only, no access for group or others (versus the default 0777).


Understanding the Plan Output for Updates

terraform plan
# local_file.pet must be replaced
-/+ resource "local_file" "pet" {
      content            = "We love pets!"
      directory_permission = "0777"
  ~   file_permission    = "0777" -> "0700"  # forces replacement
      filename           = "/root/pets.txt"
  ~   id                 = "5f8fb950ac60f7f23ef968097cda0a1fd3c11bdf" -> (known after apply)
    }

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

Forces Replacement

The -/+ symbol means Terraform will destroy the existing resource and create a new one. The line annotated # forces replacement identifies the argument that triggered this behaviour. Even a minor change like file_permission causes a full replacement of the file — this is the immutable infrastructure model.

Plan Symbols Reference

Symbol Meaning
+ Resource will be created
- Resource will be destroyed
~ Resource will be updated in place
-/+ Resource will be destroyed then recreated (replacement)

Applying an Update

terraform apply

Terraform displays the plan again and prompts for confirmation:

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

After confirming, the old resource is deleted and the new one is created with the updated configuration.


Destroying Infrastructure

To completely remove all resources managed in the current configuration directory:

terraform destroy

Terraform shows the full execution plan with a - symbol next to every resource and argument, then prompts for confirmation:

# local_file.pet will be destroyed
- resource "local_file" "pet" {
  - content            = "My favorite pet is a gold fish" -> null
  - directory_permission = "0777" -> null
  - file_permission    = "0700" -> null
  - filename           = "/root/pets.txt" -> null
  - id                 = "5f8fb950ac60f7f23ef968097cda0a1fd3c11bdf" -> null
  }

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

Do you really want to destroy all resources?
  Terraform will destroy all your managed infrastructure, as shown above.
  There is no undo. Only 'yes' will be accepted to confirm.

  Enter a value: yes
local_file.pet: Destroying... [id=5f8fb950ac60f7f23ef968097cda0a1fd3c11bdf]
local_file.pet: Destruction complete after 0s
Destroy complete! Resources: 1 destroyed.

No Undo

terraform destroy is irreversible. It destroys all resources in the working directory. Always review the plan output carefully. In production, restrict who can run this command via CI/CD pipeline permissions and remote state locks.


Immutable Infrastructure

Terraform follows the immutable infrastructure pattern — rather than patching a running resource, it replaces it entirely. This ensures configuration drift cannot accumulate and every resource always matches its declared state.

Mutable Infrastructure Immutable Infrastructure
Patch/update running resources Destroy and recreate from definition
Configuration drift is possible Guaranteed consistency
Examples: Ansible, Chef Examples: Terraform (for supported args)

Full Terraform Lifecycle — Command Reference

# 1. Write or update configuration
vim local.tf

# 2. Initialise working directory (only needed once or after provider changes)
terraform init

# 3. Preview changes
terraform plan

# 4. Apply changes
terraform apply

# 5. Destroy all resources (when needed)
terraform destroy

Best Practices

Best Practices

  • Always run terraform plan before apply and review every -/+ line — these indicate destructive replacements.
  • Use terraform plan -out=tfplan to save the plan and pass it to terraform apply tfplan to guarantee exactly what was reviewed is what runs.
  • Protect production environments by requiring manual approval gates in CI/CD before apply or destroy.
  • Use terraform destroy only in dev/staging environments or during explicit decommission workflows.
  • Add lifecycle { prevent_destroy = true } to critical resources to block accidental destroy.

Security Best Practices

Security

  • Restrict terraform destroy to senior engineers or automated decommission pipelines — never expose it to all developers.
  • Use remote state with locking (S3 + DynamoDB or Terraform Cloud) to prevent concurrent apply/destroy operations.
  • Set file_permission explicitly (e.g. "0600" for sensitive files) — never rely on the default "0777" in production.
  • For sensitive_content, ensure the argument is used instead of content to prevent secrets appearing in plan output or logs.

Do and Don't

✅ Do ❌ Don't
Review plan output before every apply Approve apply without reading the plan
Use prevent_destroy on critical resources Run terraform destroy in production without review
Set file_permission explicitly Leave file permissions at the default 0777
Save plans with -out flag in pipelines Re-run plan then apply separately (plan may drift)
Use sensitive_content for secrets Put secrets in the content argument

Common Mistakes

Common Mistakes

  • Unexpected replacement — changing an argument like file_permission and not realising it forces a destroy + recreate. Always check for # forces replacement in plan output.
  • Running destroy in the wrong directoryterraform destroy affects all resources in the current working directory. Confirm you're in the right directory before running.
  • Assuming destroy is reversible — there is no terraform undestroy. For cloud resources, this may mean permanent data loss.
  • Not saving the plan — running plan then apply without -out means the apply may execute a different plan if state changed between the two commands.

Troubleshooting

# Preview what will be destroyed without committing
terraform plan -destroy

# Target a specific resource for update or destroy (use with caution)
terraform apply -target=local_file.pet
terraform destroy -target=local_file.pet

# Check current state of all managed resources
terraform show

# List all resources in state
terraform state list

# Inspect a specific resource in state
terraform state show local_file.pet

Quick Recap

HCL & Resource Creation:

  • HCL files (.tf) consist of blocks (curly braces) and arguments (key-value pairs).
  • A resource block has: block type → resource type → resource name → arguments.
  • The provider is the word before _ in the resource type (e.g. local in local_file).
  • Workflow: terraform initterraform planterraform apply.
  • terraform show inspects the state file and displays current resource details.

Update & Destroy:

  • Modify .tf arguments and re-run terraform apply to update a resource.
  • Some argument changes force replacement (-/+) — the resource is destroyed then recreated.
  • This is the immutable infrastructure pattern — resources are replaced, not patched.
  • terraform destroy removes all resources in the working directory — irreversible.
  • Always review the execution plan before confirming any apply or destroy.

Interview / Revision Notes

Quick Revision

  • What does an HCL file consist of? Blocks (curly-brace sections) and arguments (key-value pairs).
  • What are the four parts of a resource block? Block type (resource), resource type, resource name, arguments.
  • How do you identify the provider from a resource type? The word before the _ (e.g. local from local_file).
  • What does terraform init do? Downloads the required provider plugin for the working directory.
  • What does terraform plan do? Shows a preview of changes — no resources are created or modified.
  • What is the only mandatory argument for local_file? filename.
  • What does -/+ mean in terraform plan? The resource will be destroyed and recreated (replacement).
  • What triggers a forced replacement? Changing an argument that cannot be updated in place (e.g. file_permission).
  • What is immutable infrastructure? Instead of modifying a resource, it is destroyed and a new one is created from the definition.
  • What does terraform destroy do? Removes all resources managed in the current configuration directory. Irreversible.
  • How do you prevent accidental destroy of a critical resource? Add lifecycle { prevent_destroy = true } to the resource block.
  • How do you save a plan for guaranteed execution? terraform plan -out=tfplan then terraform apply tfplan.