Open-Source Wikis

/

Terraform

/

Background

/

Migration context

hashicorp/terraform

Migration context

Why several pieces of the codebase are almost duplicated, almost obsolete, or almost gone but not quite. Each of these survives because removing it would break a backward-compatibility promise.

internal/legacy/

Pre-0.12 Terraform represented state and provider data with a different set of types. The 0.12 rewrite produced new representations, but tests, migrations, and a few rare runtime paths still reference the old types. The legacy types are quarantined in internal/legacy/ so the rest of the codebase doesn't have to know about them.

You'll see internal/legacy/ referenced primarily by:

  • Tests for the state-file upgrader (statefile/upgrade.go).
  • Some helpers in internal/configs/hcl2shim/ that bridge legacy and modern values.
  • Internal utilities used by old test fixtures.

The expectation is that this package contracts over time as old fixtures are removed, but it will probably never be empty.

Two plug-in protocol versions

internal/plugin/ (v5) and internal/plugin6/ (v6) implement the same operations against the same providers.Interface but use different protobuf messages. They coexist because:

  • Protocol v5 was released with Terraform 0.12 and is supported by the bulk of existing providers.
  • Protocol v6 added features (server capabilities, deferred actions, identity, ephemeral resources, list resources, actions, provider-defined functions) that needed wire-level changes.
  • Forcing every provider to upgrade simultaneously would have broken the ecosystem.

The decision happens at handshake time. Each provider declares which protocols it speaks; the host picks v6 if available, otherwise v5. Older providers never implement v6; new providers usually only implement v6.

This split will persist for the foreseeable future. Removing v5 would require every provider in the ecosystem to release a v6-only build, which isn't going to happen quickly.

See plugin protocol.

cloud {} and the remote backend

internal/backend/remote/ and internal/cloud/ overlap heavily. They both implement HCP Terraform integration; they differ in:

  • The configuration block that opts into them. remote is selected by terraform { backend "remote" {} }; cloud is selected by cloud {}.
  • The CLI flow. cloud {} integrates with terraform login, supports tag-based workspace selection, and is the documented form going forward. remote predates terraform login and uses a different precedence model.
  • The level of active development. cloud {} gets new features; remote only gets fixes for backward compat.

Why both: removing remote would require all existing users to migrate their configurations. The team has done the design but not pulled the trigger on deprecation in any specific release.

See cloud.

terraform push and other hidden commands

The push, internal-plugin, and a few env ... aliases are wired into commands.go as HiddenCommands. They exist because removing them would break automation that calls them either by name or for their non-zero exit code.

push in particular is a stub: its Run method just prints an error message. It has not done anything functional in years; the file's bytes are mostly there to keep the registry consistent and the help output clean.

Pre-0.13 unqualified provider references

Pre-0.13 Terraform allowed provider "aws" without an associated required_providers source. The 0.13 release made source mandatory. The old code path is gone, but you can still see traces in internal/configs/provider.go and internal/addrs/provider.go where short names are reconciled against required_providers. The reconciliation logic exists for current configurations, but the same machinery handles old configurations being migrated.

State file version 4 has been current since 0.12

Version 4 of the state-file format dates to the 0.12 rewrite. Every change to state since then has been additive — new optional fields, new top-level entries — without bumping the version. This is intentional: bumping the version invalidates state files that older versions of Terraform can read, which would prevent users from rolling back releases.

The internal/states/statefile/ package has readers for versions 1, 2, 3, and 4, but only writes 4. Readers run a chain of upgraders to bring older files up to v4 in memory.

Sensitive markings used to live on state, now live on values

Pre-0.15, sensitivity was a flag on the resource-instance-state record: "this attribute is sensitive." Sensitivity propagation through expressions had to be implemented manually for each function. 0.15 moved sensitivity into cty value marks, automating propagation.

The consumer code in the engine was rewritten cleanly. The on-disk state format had to be backwards-compatible: it now stores the marks-aware paths in AttrSensitivePaths, while older state can be read via the legacy path.

Tool-specific provisioners

chef, salt-masterless, puppet, and habitat provisioners were deprecated in 0.13.4 (Sep 2020). The code is not actively maintained, but it remains in the tree for users who haven't migrated. The CONTRIBUTING guide explicitly says no PRs will be accepted for these.

Generic provisioners (file, local-exec, remote-exec) are not deprecated, just discouraged.

License migration to BUSL

In August 2023, Terraform's license changed from MPL 2.0 to the Business Source License 1.1. Most files have a Copyright IBM Corp. 2014, 2026 header (the date range covers the original copyright through the license change era and the IBM acquisition). The mass-update was scripted via scripts/copyright.sh and the output controlled by .copywrite.hcl.

This is mostly invisible at the code level, but you'll see the header at the top of nearly every .go file.

Stacks are still under the experimental umbrella

Many features in internal/stacks/ and internal/rpcapi/stacks.go are gated behind experiment flags or are not yet exposed in the public CLI. The cloud subcommand is enabled only when meta.AllowExperimentalFeatures is true, which in turn is true only in alpha builds.

The expectation is that stacks will graduate to general availability over the next several releases. Until then, expect breaking changes within internal/stacks/ and within internal/rpcapi/terraform1/stacks/ between minor releases.

Why these layers don't get cleaned up

Terraform's user base treats the binary as critical infrastructure. The cost of any upgrade-required change — even a "small" one — is high in operator time and in support load. The team explicitly trades code cleanliness for compatibility. New contributors are sometimes surprised that obviously-unused code stays in the tree; the answer is almost always "removing it is more disruptive than keeping it."

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

Migration context – Terraform wiki | Factory