August 2, 2017·6 min read·engineeringterraformprovidersdependencies

Terraform separates provider releases from the core executable

Terraform 0.10 installs providers during initialization and gives them independent versions. Configuration now describes acceptable provider versions as well as provider settings.

An infrastructure API can gain an operation without Terraform's core execution engine needing a new design. Yet distributing providers with the main Terraform release ties those changes together. A user seeking a new capability in one provider receives the versions of every other bundled provider that accompany the executable. Terraform 0.10 separates those release decisions.

The August 2 changelog introduces independent provider releases, automatic provider installation and provider version constraints. Providers no longer ship inside the main distribution. Running terraform init installs the plugins required by a configuration, and initialization becomes mandatory before operations that use those plugins.

I value Terraform's general configuration language over infrastructure APIs. Separating the API implementations from the executable fits that architecture. It also makes dependency selection an explicit part of a configuration's maintenance, since the Terraform version alone no longer identifies all the provider code that will run.

The release boundary follows the implementation boundary

The 0.10 upgrade guide explains the motivation in practical terms. A user can upgrade a particular provider for new functionality without also upgrading another provider that has introduced incompatible behavior. The main download also becomes smaller because it no longer contains providers the configuration never uses.

This does not make provider upgrades independent of configuration compatibility. It makes their selection independent of the main Terraform distribution. A provider still implements resource behavior and schema expectations that the configuration relies on. Independent releases give those changes their own version identifiers and their own upgrade decisions.

Provider changelogs consequently move to their respective repositories, as the release notes explain. Reading only the core changelog is no longer sufficient to understand every infrastructure behavior change in an upgrade. The relevant provider's history now describes changes to its API integration.

That division is useful for maintainers as well as users. A provider change has a distribution path that does not require packaging all the other providers again. The important consequence for a configuration author is that dependency policy must account for both core and plugin versions.

Initialization installs what the configuration requires

The initialization reference says Terraform searches the configuration for direct and indirect references to providers. It retrieves the required plugins and installs them in a subdirectory of the working directory. Indirect references matter because child modules can introduce providers that the root configuration does not use directly.

Initialization also prepares the backend and retrieves child modules. The command therefore assembles the working directory needed for subsequent Terraform operations. Installing the executable alone does not complete that preparation in 0.10.

Repeated initialization has defined behavior. For providers already installed, ordinary init does not automatically replace them with the newest available versions. It installs providers newly required by configuration. The -upgrade option additionally updates installed providers to the newest versions allowed by the configured constraints.

That distinction makes the local working directory relevant. A fresh directory may need to select and install providers, while an initialized directory already contains them. Version constraints define acceptable selections, and the upgrade flag controls whether Terraform revisits an installed selection during that initialization run.

terraform init
terraform init -upgrade

These commands express different maintenance actions. The first prepares the directory under the normal reuse rules. The second asks initialization to consider upgrades, including upgrades to already installed plugins within the configuration's allowed versions. Treating them as interchangeable would hide that intent.

Version constraints become configuration

The provider configuration reference adds a special version argument to provider blocks. It applies to all providers. The documentation recommends constraints for production configurations so that future initialization does not automatically select a new major provider version containing incompatible changes.

The reference demonstrates a pessimistic constraint with the following shape. Authentication is intentionally outside this example.

provider "aws" {
  version = "~> 1.0"
  region  = "us-east-1"
}

The constraint describes which provider releases are acceptable. It is distinct from the region and credentials that configure an instance of the provider. One controls code selection, while the others control where that code sends requests and how it authenticates.

When no constraint exists, initialization prints a suggested constraint after installing the latest provider. That output helps an author make the policy explicit. It should not be mistaken for an automatic edit to the source configuration. The documented way to retain the policy is to add the version argument to the provider block.

Terraform also provides terraform providers to display provider constraints across the current configuration. This is useful when modules contribute requirements indirectly. A root configuration's visible resource declarations do not necessarily describe every plugin dependency in the full module tree.

A constraint can permit more than one version, so it is not by itself a statement that all fresh installations use identical provider code. Its practical role is to bound selection. The exact installed release remains a separate fact about the prepared directory.

I prefer making that boundary explicit because a broad version policy and an exact dependency choice answer different questions. Both can be deliberate, but they should not be described as providing the same reproducibility.

Automatic installation has an initial limit

The upgrade guide states that automatic installation initially covers providers packaged and released by HashiCorp. Third-party plugins keep their existing installation mechanisms. The project intends to generalize the approach later, but that intention is not the capability delivered in this release.

Third-party maintainers can participate in the versioning convention by naming binaries in the form terraform-provider-NAME_v0.0.1. Terraform expects semantic versioning and can verify that a manually installed plugin satisfies a configuration constraint. It cannot automatically install those third-party versions through the new mechanism yet.

That makes automatic installation and version checking separate capabilities. A third-party plugin can have a version Terraform understands without having a download source that Terraform automatically uses. Describing 0.10 as automatically fetching every provider would erase this distinction.

The initialization command also supports -plugin-dir=PATH. When that option is present, Terraform consults only the supplied directory for plugins and does not request them from the plugin repository or search other local directories. This gives an automation environment a way to supply a prepared set of binaries explicitly.

The tradeoff is responsibility for populating that directory. Automatic repository discovery is intentionally excluded by the option. The operator must make the needed provider binaries available there before initialization can use them.

Download verification and version policy are different checks

For automatically downloaded plugins, the initialization documentation says Terraform verifies their contents against an official HashiCorp release signature by default. This checks the download against the official release material. Version constraints determine whether the release is acceptable to the configuration.

Those checks address different failure modes. A correctly downloaded release can still be outside the version range a configuration supports. Conversely, a release identifier inside the range does not by itself establish the integrity of the downloaded binary. The initialization workflow handles both code selection and verification for the providers it automatically installs.

The signature behavior is particularly relevant because installation now happens as part of preparing a working directory. A pipeline that previously obtained one Terraform distribution now also obtains plugins selected by its configuration. The initialization step becomes part of explaining which code executes infrastructure operations.

Other 0.10 changes preserve separate upgrade concerns

The provider split is the main architectural change, but the release also renames terraform env commands to terraform workspace. For compatibility, the previous name remains an alias. The changelog attributes the rename to confusion with other meanings of environment, while keeping the commands' behavior the same.

Terraform 0.10 also adds optional interactive approval for an unsaved apply through -auto-approve=false. The upgrade guide says the previous automatic behavior remains the default in this release. This matters when describing an automation upgrade because the new flag does not mean 0.10 immediately changes every existing wrapper's default interaction.

These concerns can be handled independently of provider selection. A configuration can adopt explicit provider constraints while a wrapper retains its chosen approval behavior. The provider configuration documents the acceptable version policy alongside the API settings that readers already expect to find there. Separating releases gives provider dependencies their own maintenance decisions, and initialization supplies the concrete binaries needed to carry those decisions into the working directory.