Open-Source Wikis

/

Terraform

/

Lore

hashicorp/terraform

Lore

A timeline of how Terraform Core got to where it is today, derived from the public commit history and CHANGELOG.md. Dates are approximate where the historical record only reveals month or quarter granularity.

Eras

The bootstrap (May 2014 – mid-2015)

The first commit (649cf336e8) lands on 2014-05-21 under Mitchell Hashimoto's authorship, with a small package layout and the original command package as the front door. The early codebase predates HCL2, the for_each meta-argument, modules-as-first-class-citizens, and providers-as-separate-binaries. The first releases ship a monolithic binary that bundles the AWS provider directly.

Highlights:

  • Original CLI dispatch shape — commands.go mapping subcommand names to cli.Command factories — is set up here and survives basically intact to today.
  • The terraform.Context god-object is introduced as the coordinator for plan/apply.
  • The DAG primitive lives in internal/dag/ and is reused unchanged through every later rewrite.

Provider extraction and the SDK split (Mar 2017 – Aug 2018)

The single biggest non-language change in Terraform's history. Providers, originally compiled into the Terraform binary, are extracted into separate repositories and loaded via hashicorp/go-plugin over gRPC. This required:

  • Defining the wire protocol in Protocol Buffers (docs/plugin-protocol/tfplugin5.proto, retained today).
  • Building the provider installer in internal/getproviders/ to download and verify provider releases.
  • Inventing the registry-style address scheme (hashicorp/aws) that became the basis for Terraform Registry.

After this era, Terraform Core stopped growing AWS/Azure/GCP code and started growing infrastructure to talk to the idea of a provider.

HCL2 and the 0.12 rewrite (early 2018 – Q3 2019)

The configuration language was rewritten on top of HCL2. This introduced:

  • First-class types via cty.Value (replacing string-based config).
  • Type constraints in variable declarations.
  • dynamic blocks, splat expressions, conditional improvements.
  • The expression evaluator now in internal/lang/.

The 0.12 rewrite touched almost every file. The git log around 0.12-beta shows hundreds of commits from Martin Atkins reshaping internal/configs/, internal/terraform/, and internal/states/. The internal/legacy/ package was carved out at this time to retain the old representations of state and provider data so that tests and migrations could still compile.

State-as-data and the JSON state format (mid-2019)

Terraform 0.13 reorganized state to support fully-qualified provider source addresses (so the dependency lock file in 0.14 had something to reference) and also exposed a formal JSON representation, codified in internal/command/jsonstate/ and internal/command/jsonplan/. From this point forward, automation could parse plans and states without screen-scraping the human output.

The dependency lock file (Q4 2020)

terraform 0.14 introduced .terraform.lock.hcl. The implementation is internal/depsfile/ and the supporting hash logic is in internal/getproviders/hash.go. This was the first user-visible break that forced operators to update their CI workflows; before it, any terraform init could silently upgrade providers.

for_each for modules and moved blocks (2021)

Terraform 1.0 was released in Jun 2021 — a stability commitment more than a feature jump. The interesting platform changes around this time were:

  • Modules gained count and for_each (1.x). The graph engine learned to expand entire module trees dynamically; see internal/terraform/node_module_expand.go.
  • The moved block (1.1) let users refactor without terraform state mv. Lives in internal/configs/moved.go and internal/refactoring/.

terraform test and native testing (Q3 2022 – ongoing)

The terraform test command graduated from an experiment to a production feature over several releases. The runtime moved out of internal/command/test.go and into a dedicated internal/moduletest/ package with its own graph builder (internal/moduletest/graph/) and run/state machinery. The matching parser lives in internal/configs/test_file.go. Mock providers (internal/configs/mock_provider.go) were added to support deterministic test runs without real provider calls.

HCP Terraform / cloud {} block and remote operations (2023 – ongoing)

The cloud {} configuration block superseded the terraform { backend "remote" {} } form for opting into HCP Terraform (formerly Terraform Cloud). The implementation lives in internal/cloud/ and reuses the older internal/backend/remote/ for the operations channel. Renames from "Terraform Cloud" to "HCP Terraform" over 2024 are visible throughout the codebase as comments and string literals are updated incrementally.

Stacks (2024 – ongoing)

The largest new subsystem since provider extraction. internal/stacks/ introduces a new top-level configuration form (stack {} and component {}) that composes multiple Terraform configurations into one planned-and-applied unit. Stacks have their own graph builders, plan format, and runtime, and ship a new RPC-API surface (internal/rpcapi/stacks.go) so HCP Terraform can drive them remotely. The stacks CLI subcommand (internal/command/stacks.go) is the local entry point. As of 1.16-dev, stacks are still gated behind experiment flags in many code paths.

Actions, queries, ephemeral values, identity (2024 – ongoing)

A cluster of language-level features arrived in late 2024 and continues into 1.16:

  • Actions — first-class, non-resource operations a provider exposes. action_trigger blocks and action {} configuration. internal/configs/action.go, internal/plans/action_invocation.go, and a family of node_action_*.go files in internal/terraform/.
  • Queries — read-only operations whose results aren't persisted to state. internal/command/query.go and eval_query.go.
  • Ephemeral values — values that live only for the duration of one operation, never written to state. Implemented via cty value marks in internal/lang/marks/ and supported throughout the engine (node_resource_ephemeral.go, context_apply_ephemeral_test.go).
  • Identity — a stable, server-assigned identifier for a managed resource, separate from its address. internal/command/state_identities.go and tests under internal/terraform/context_apply_identity_test.go.

These features extend the protocol; the v6 plugin protocol (tfplugin6.proto) is where they are wired in.

License change (Aug 2023)

Terraform's license changed from MPL 2.0 to the Business Source License 1.1 on 2023-08-10. This is the most visible non-code event in the project's history; it triggered the OpenTofu fork. The repository's copyright headers were updated en masse via the scripts/copyright.sh tooling.

Longest-standing features

Feature First appeared Status
The dag package 2014 Unchanged in shape; still the only DAG implementation.
commands.go factory map 2014 Same pattern as v0.1; entries added/removed but structure is identical.
terraform.Context 2014 Massively rewritten internally over the years, but the public surface (Plan, Apply, Refresh, Validate) is largely unchanged.
internal/states/statefile JSON format 2018 (0.12) Backwards-compatible additions only. The version field is still 4.
cli library (hashicorp/cli) 2014 Same dependency since the start.

Deprecated or removed features

Feature Removed/Deprecated Replacement
terraform push Removed in spirit, retained as a stub that errors. See internal/command/push.go. HCP Terraform / VCS-driven runs.
Tool-specific provisioners (chef, salt-masterless, puppet, habitat) Deprecated in 0.13.4 (Sep 2020). Configuration management tools used outside Terraform.
Pre-0.12 state format Migrated automatically; loader code still lives in internal/legacy/ for compatibility. The 0.12+ JSON state format.
Pre-0.13 unqualified provider references (provider "aws" without source) Removed in 0.13. Required source addresses (hashicorp/aws).
env list / env new / env select / env delete Renamed to workspace … in 0.10. The originals are still wired up in commands.go as hidden aliases. terraform workspace ….
Partial-import without configuration Replaced by import {} blocks in 1.5+. The CLI subcommand still works but is no longer the recommended path. import {} configuration blocks.
Several state-storage backends (Consul, etcdv2/v3, OSS, OCI, COS, Postgres-third-party-maintainer) Retained as code but explicitly unmaintained per CODEOWNERS. The team is not accepting new state-storage backends. Existing maintained backends (S3, GCS, Azure, K8s, HTTP, HCP Terraform).

Major rewrites and migrations

  • 0.12 HCL2 rewrite (2019). Configuration parsing, expression evaluation, and type system rewritten around HCL2 and cty.
  • 0.13 provider source addresses (2020). Every reference to a provider gained a fully qualified address; the registry namespace migration was driven by tooling in internal/getproviders/.
  • 0.15 sensitive values (2021). Sensitive marks moved from a state-level flag to per-value cty marks, propagated through the entire evaluation pipeline.
  • 1.0 → 1.1 module expansion. Modules gained count/for_each, requiring a parallel rewrite of the apply graph builder to handle dynamic module trees.
  • License migration to BUSL (Aug 2023). Mechanical change applied to ~12,000 source files; visible in headers via .copywrite.hcl.
  • HCP Terraform rename (2024). A long tail of string updates across internal/cloud/, internal/backend/remote/, internal/command/, and tests.

Growth trajectory

  • 2014: One package, one author, one provider (AWS) baked in.
  • 2017: First multi-provider release with binaries downloaded ad-hoc.
  • 2019: HCL2 rewrite triples the size of internal/configs/.
  • 2020–2021: 1.0 stabilization; growth slows in core, accelerates in internal/cloud/.
  • 2024–2026: Stacks, actions, queries, ephemerals, identity — the language and engine grow another ~150 KLOC across internal/stacks/, internal/terraform/, and internal/configs/.

The codebase has roughly tripled in size since 1.0 (Jun 2021), almost entirely from new subsystems rather than new code in old ones.

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

Lore – Terraform wiki | Factory