Terraform Unused Declaration Linter

Find what is declared and never used, and what is used and never declared. One of these costs money on every plan, and one of them is a breaking change to delete.

Paste below, or drop a file anywhere on this panel

Or drop a file anywhere on this panel. Nothing is uploaded: the analysis runs in this tab.

The answer appears here

Paste on the left and press Check. Nothing leaves this tab.

Examples

Real input you can load into the tool above. Each one shows a different thing going wrong, because that is what the tool is for.

An unused variable and an unused data source

The data source is the expensive one: an API call on every plan for nothing

variable "unused" {
  type = string
}

data "aws_ami" "ubuntu" {
  most_recent = true
}

resource "aws_s3_bucket" "logs" {
  bucket = "my-logs"
}

A reference with no declaration

Fails at plan, and in a real module the declaration may just be in another file

variable "declared" {
  type = string
}

resource "aws_s3_bucket" "a" {
  bucket = var.declared
  tags   = var.missing
}

A local left behind by a refactor

Nothing consumes it, and unlike a variable there is no interface to preserve

locals {
  used   = "a"
  unused = "b"
}

output "o" {
  value = local.used
}

Common mistakes

These are the ones that fail silently. The config is accepted, nothing raises an error, and the consequence arrives later.

  1. Deleting an unused variable from a shared module

    It is part of the module's published interface. Every caller setting it breaks on the next upgrade, with an error about an undeclared variable in a module they did not change.

    Instead:Deprecate it in the description, and remove it on a major version.

  2. Leaving a data source in place after the thing using it went away

    It is still read on every plan and apply, still counts against the provider's rate limit, and still fails the plan if it cannot be read.

    Instead:Delete it, or move it inside the check block that needs it if it is there to probe something.

  3. Running a linter like this against one file of a module

    Terraform treats every .tf file in the directory as one module, so a variable used elsewhere in the same module looks unused.

    Instead:Concatenate the module's files before checking, or verify each finding against the whole directory.

An unused data source is not free

It is an API call on every plan and every apply, it counts against the provider's rate limit, and if it cannot be read the plan fails. Dead weight with a running cost.

Deleting an unused variable is not always safe

In a root module it is dead configuration that everybody setting values still has to reason about, so delete it. In a module other people call, that variable is part of the published interface: removing it breaks every caller that sets it, which makes it a major version rather than a tidy-up.

An unused local is simply dead

There is no interface to preserve. They accumulate when an expression is factored out of one place and the original is left behind, and they are the cheapest thing on this page to delete.

An unused data source has a real cost

Terraform reads it on every plan whether or not anything uses it. On an account near a rate limit that is a plan that fails intermittently, for a value nobody consumes. If it is there to probe something, a check block is where it belongs, because a failure there is a warning rather than a failed plan.

A reference with no declaration fails at plan

Terraform's message is clear enough on its own. It appears here because in a module split across files the declaration may be in a file you did not paste, which is the same reason this page cannot be trusted on a partial module.

Outputs are not checked, on purpose

An output exists to be consumed by something outside the module, so nothing inside it refers to one. "Unused" is not a thing that can be determined from the module, and reporting them all would be noise.

This only sees what you pasted

A module is every .tf file in its directory and Terraform does not care which file anything is in. So a variable used in a file you did not include looks unused here. Concatenate the directory before trusting a finding that looks wrong.

More terraform tools

Terraform Validator & Formatter Does it parse, and is it formatted? Terraform dynamic Block Generator It is ingress.value, not each.value Terraform check and precondition Generator A failed check does not stop the apply Terraform variables.tf Generator From the var. references you already wrote Terraform Atlantis YAML Generator when_modified misses your modules Terraform Function Tester and HCL Console terraform console, in a tab Terraform for Expression Tester Where do the keys collide? Terraform Interpolation Tester null becomes an empty string Terraform templatefile Tester Render it before you apply it Terraform Resource Count Calculator How many objects, not how many blocks Terraform count to for_each Converter With the moved blocks it needs Terraform moved Block Generator Rename without destroying Terraform state mv Command Builder No plan, no undo Terraform target Flag Builder And why to stop using it Terraform outputs.tf Generator Outputs are not sensitive by default Terraform Remote State Data Source Generator It grants read of the whole state Terraform Provider Block Generator docker is not hashicorp/docker Terraform .gitignore Generator and Checker .terraform* eats the lock file Terraform to OpenTofu Migration Checker The state is the part to watch Terraform tfvars to JSON Converter The JSON file wins Terraform Version Constraint Parser What ~> actually allows Terraform Resource Address Parser Quote it before the shell eats it Terraform cidrsubnet Calculator newbits is added, not the target Terraform Import Block Generator The id is not the name Terraform Lock File Viewer Why CI fails and your laptop does not Terraform Variables Checker A missing value hangs CI Terraform tfvars to Env Vars Converter Lists and maps have to be JSON Terraform Deprecated Syntax Checker What a current Terraform rejects Terraform State File Viewer Read it without uploading it Terraform Plan JSON Viewer It is not redacted Terraform HCL to JSON Converter And what the conversion loses Terraform Module Source Validator version only works for the registry Terraform Backend Config Decoder Is state actually locked? Terraform Naming Convention Validator A hyphen makes a resource unreferenceable

Elsewhere on the site