iac.htora.dev · security templates

Home/Start here/Terraform

Terraform

Terraform builds things in a cloud account. You describe what you want to exist, and it makes the account match.

Your fileswhat youwant to existTerraformplan showsthe differenceapply makes it soYour cloudwhat actuallyexists nowState fileits memory ofwhat it maderecords what it madere-checked each run
Terraform compares what you wrote against what exists, then changes only the difference. The state file is how it remembers what it made last time.

How it actually works

You write files ending in .tf. Each one lists resources you want: a bucket, a network, a permission. Then you run three commands.

terraform init     downloads the code that talks to your cloud
terraform plan     shows exactly what will change, and changes nothing
terraform apply    makes the change

plan is the one that matters. It prints a list where every line starts with a symbol: + to create, ~ to modify, - to destroy. Read that list before you type yes. A surprise - on a database is the reason this step exists.

Writeedit the filePlansee what changesApplymake the changethen do it again for the next change
The loop you will run hundreds of times. Reading the plan is the step people skip, and it is the step that catches the mistake.

What a plan actually looks like

Here is a real one, shortened. This is the quick start template for audit logs running against an empty account.

Terraform will perform the following actions:

  # aws_s3_bucket.audit will be created
  + resource "aws_s3_bucket" "audit" {
      + bucket              = "audit-log-immutable-123456789012-a1b2c3d4"
      + object_lock_enabled = true
      + force_destroy       = false
    }

  # aws_cloudtrail.audit will be created
  + resource "aws_cloudtrail" "audit" {
      + name                       = "audit-log-immutable"
      + is_multi_region_trail      = true
      + enable_log_file_validation = true
    }

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

Read the last line first. Nine new things, nothing changed, nothing destroyed. On a fresh account that is exactly right, so you type yes.

The one you stop at

Now the same template after somebody edited the bucket name.

  # aws_s3_bucket.audit must be replaced
-/+ resource "aws_s3_bucket" "audit" {
      ~ bucket = "audit-log-immutable-123456789012-a1b2c3d4" -> "audit-logs-prod"
        # forces replacement
    }

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

That -/+ and the words must be replaced mean Terraform will delete the bucket and make a new one. On a log bucket with a retention lock the delete fails and you are left half applied. On a database it would succeed.

Three things to look for every time:

  • Anything to destroy that you did not ask to destroy.
  • forces replacement next to a change you thought was small.
  • A count far off what you expected. Nine when you changed one setting usually means Terraform lost track of what it made.

The state file, and why it matters

Terraform keeps a file recording what it created. That file is how it knows the bucket in your code and the bucket in your account are the same bucket. Lose it and Terraform will try to create everything again.

Two things about it that catch people out:

  • It contains secrets in plain text. Any password or key that passed through Terraform is written into the state file as readable text. A state file anyone can download is a serious problem.
  • Two people cannot run at once. Without locking, two simultaneous applies will write over each other and leave the file describing something that does not exist.

On your own machine the state file sits in the folder. For a team it lives in a shared, encrypted, locked location. That is what the "remote state" block at the top of the Standard templates is for.

Two files look alike and are treated in opposite ways. terraform.tfstate never goes in Git, because of the secrets. .terraform.lock.hcl always does, because it records the exact provider versions and makes the next run behave the same as this one.

The words you will see in a Terraform file

WordPlain meaning
resourceOne thing you want to exist. A bucket, a key, a rule.
dataSomething that already exists, which you look up and leave alone.
variableA setting someone can change without editing the main file.
outputA value printed after apply, usually so you can use it in the next step.
localA shorthand for something repeated in several places in the same file.
moduleA folder of Terraform you reuse, called from somewhere else.
providerThe plugin that knows how to talk to AWS, Azure, Google Cloud, or Oracle Cloud.

What Terraform is good at

Anything that is created once and then exists: networks, machines, storage, databases, keys, permissions, DNS records, policies. It tracks what it made, notices when someone changed it by hand, and can put it back.

What to use something else for

Installing software inside a machine. Terraform can start a machine and hand it a startup script, and that is where most people stop. Anything more than that belongs to Ansible.

Deploying an application several times a day. Terraform runs when a person runs it. For something continuous on Kubernetes, use Argo CD.

Try it

Every template on this site has a Quick start version: one file, no setup, runs with no input. Audit logs nobody can delete is a good first one, because you can prove it worked by trying to delete something and watching it fail.

Registry 0.6.0. Built 2026-09-22.

Made by Habibullah Tora. Code under the MIT licence, writing under CC BY 4.0.