Skip to content

Language record

HCL / Terraform

HCL is read as the configuration language it is: every block and attribute becomes a symbol with the enclosing block as its owner, and every dotted path an expression writes — `var.x`, `local.y`, `module.z`, `aws_s3_bucket.assets.id` — becomes a reference with its own line, while a module block's `source` becomes an import.

.tf, .tfvars, .tofu, .hcl · 2 registry ids · 10 of 14 relationship types

The registry row this page is generated from

Read at render time from the same family row the register reads, so nothing in this block can disagree with it.

Extensions
.tf, .tfvars, .tofu, .hcl
Registry names
hclterraform
Recorded symbols
resources · modules · variables · outputs

Relationship types this analyzer constructs

10 of 14 constructed, read from this analyzer's source and re-derived by the languages gate. Each row below is one of the engine's types, in the engine's own order.

Relationship typeConstructed
callsconstructed by this analyzer
extendsconstructed by this analyzer
implementsruled absent
usesconstructed by this analyzer
importsconstructed by this analyzer
exportsconstructed by this analyzer
containsconstructed by this analyzer
referencesconstructed by this analyzer
overridesconstructed by this analyzer
instantiatesconstructed by this analyzer
decoratesruled absent
rendersruled absent
mixes_inruled absent
part_ofconstructed by this analyzer

10 constructed, 4 not constructed. All 4 absent types are recorded as ruled absent: the written reason and the corpus measurement behind each are in this family's completeness record. The register explains what each label means.

What this analyzer does not resolve

  • There is no call edge, and that is a measurement rather than an assumption: across 12,977 function-call nodes in 9,079 corpus files over 144 distinct callees, none resolves to a symbol this format declares. The five that appear to are provable name collisions — four Terraform core functions, plus object, which is a type keyword in type = object({…}). The references read inside a call are still published.
  • count.index, each.key, each.value, path.module, self and terraform are published as member reads rather than as references, because a target in the language's global namespace is not a project symbol — 2,029 edges, 4.6% of all references, led by path.module at 506.
  • Terraform repeats blocks on purpose. Two locals blocks in one file become locals and locals#2 with the plain address kept alongside, because a name that is not unique is an identity nothing downstream can resolve. A duplicate key INSIDE a block is deliberately left alone, since Terraform rejects that outright — 21 such identities remain in the corpus.

Every line above was written against HCL / Terraform’s own analyzer module and is named against a construct in it, so it can be checked against the source. Nothing here is inherited from a template, or from another language’s page.

Where you meet it in a repository

Recognised by extension — .tf, .tfvars, .tofu, .hcl. Everything below is about where those files actually sit in a working repository.

  • A Terraform root module is a directory of .tf files plus .tfvars; each file is its own root symbol, so the directory is a convention rather than something the index knows about.
  • The same grammar covers the other HashiCorp DSLs — Packer's .pkr.hcl, Vault's .hcl, Nomad's job files — and framework detection is structural rather than extension-based, so a Consul agent config or a Packer template is recorded as HCL with no Terraform block in sight and an empty framework list rather than a guessed name.
  • moved.tf and removed.tf hold a single label-less block each, which is the case that makes an ordinal rule for repeated sibling blocks necessary rather than decorative. The file's own stem is reserved in the name space first: doc/book/book.toml-style collisions were measured 308 times over the corpus, and the file's identity, derived from its path, is not negotiable.

Questions this index answers

Phrased as retrievals, because that is what an index can honestly do: it holds the symbols and the edges between them, and hands them back with the evidence attached.

  1. Which resources and modules does this file declare?
  2. Which variables and outputs does this module expose?
  3. Which variables does this resource reference?
  4. Which module sources does this configuration pull in?

What the analyzer sees in code like this

A resource, the variable it references, and a module block's source
resource "aws_s3_bucket" "assets" {   // bb records: class aws_s3_bucket.assets
  bucket = var.bucket_name                  // bb records: references -> var.bucket_name
}

module "network" {                         // bb records: module network
  source = "./modules/network"             // bb records: imports -> ./modules/network
  region  = var.region                     // a project address: references, not a builtin use
}

Symbols are stored with the file they came from and the bytes they span, so a hit points at a line rather than at a guess. The annotations in the sample are commentary for the reader, not strings the index would store.

Languages that turn up in the same repository

Web, data & config — the count beside each name is how many relationship types that family’s analyzer constructs.

The extensions, registry names, recorded symbols and relationship types above are the parser registry row for HCL / Terraform, read while the page rendered — the same row the register reads, so the two surfaces cannot tell two stories. Every relationship type listed as constructed appears in src/ast/real-*, the analyzer modules bb ships. bb 0.1.0 answers from a local store over 50 languages (53 registry ids, three of them aliases). There is no benchmark on this page: every claim here is one you can go and read in the source.