4.06 Terraform Provider Version Constraints
Overview
By default, terraform init downloads the latest available version of each provider plugin a configuration needs. Version constraints let you pin, exclude, or bound which provider versions Terraform is allowed to install instead.
Abstract
Provider functionality can change meaningfully between versions, and a configuration written against one version may not behave the same on another. The required_providers block inside a terraform block lets you specify an exact version or a range of acceptable versions for each provider, making terraform init predictable and repeatable.
Why It Matters in Production
Letting terraform init silently pull the latest provider version is risky — a provider upgrade can introduce breaking changes that cause a previously working configuration to fail, behave differently, or produce unexpected plans. Pinning or constraining provider versions keeps environments consistent across team members, CI pipelines, and time, and gives you control over when and how a provider upgrade happens.
Key Concepts
| Concept | Description |
|---|---|
terraform block |
Top-level block for configuring Terraform itself, including required provider versions |
required_providers |
Block inside terraform { } listing each provider's source address and version constraint |
source |
The provider's registry address, e.g. hashicorp/local |
version |
A version constraint string controlling which provider versions are acceptable |
= (exact) |
Installs exactly the specified version |
!= (not equal) |
Excludes the specified version |
<, <=, >, >= |
Bounds versions below or above a given value |
~> (pessimistic) |
Allows the specified version or any higher version within the same major/minor segment |
Common Use Cases
- Pinning an exact provider version so a configuration's behavior stays consistent across
terraform initruns, regardless of when or where it's run. - Excluding a specific provider version known to have a bug or breaking change, using
!=. - Allowing only patch-level upgrades (bug fixes) while blocking minor or major version changes, using the pessimistic constraint operator
~>. - Combining multiple constraints to define an acceptable version range, e.g. allowing anything newer than one version but older than another.
Example Configuration or Commands
Default behavior — latest version
Without any version constraint, terraform init downloads the latest provider release:
The Terraform Registry page for a provider shows its latest version and a dropdown of all prior releases — for the local provider, the latest is 2.0.0, with 1.4.0, 1.3.0, 1.2.2, and others available below it.
Pinning an exact version
The Registry's "Use Provider" button generates the snippet needed to require a specific version:
terraform {
required_providers {
local = {
source = "hashicorp/local"
version = "1.4.0"
}
}
}
resource "local_file" "pet" {
filename = "/root/pet.txt"
content = "We love pets!"
}
Initializing the backend...
Initializing provider plugins...
- Finding hashicorp/local versions matching "1.4.0"...
- Installing hashicorp/local v1.4.0...
- Installed hashicorp/local v1.4.0 (signed by HashiCorp)
Terraform has been successfully initialized!
You may now begin working with Terraform. Try running
"terraform plan" to see any changes that are required for your infrastructure. All
Terraform commands should now work.
If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.
Not equal — excluding a version
Terraform will not install version 2.0.0; it falls back to the next available version, 1.4.0.
Less than / greater than
The first constraint allows any version older than 1.4.0; the second allows any version newer than 1.1.0.
Combining constraints into a range
terraform {
required_providers {
local = {
source = "hashicorp/local"
version = "> 1.2.0, < 2.0.0, != 1.4.0"
}
}
}
This requires a version greater than 1.2.0, less than 2.0.0, and explicitly not 1.4.0. Given the available releases, Terraform resolves this to version 1.3.0.
Pessimistic constraint operator (~>)
Initializing provider plugins...
- Finding hashicorp/local versions matching "~> 1.2.0"...
- Installing hashicorp/local v1.2.2...
- Installed hashicorp/local v1.2.2 (signed by HashiCorp)
Terraform has been successfully initialized!
~> 1.2 allows version 1.2 or any later version within the 1.x line (1.3, 1.4, ... up to but not including 2.0). Since the highest available release in that range is 1.4.0, that's what gets installed.
Using a more specific value narrows the allowed range further:
~> 1.2.0 only allows 1.2.0 through 1.2.x (patch-level increments) — not 1.3.0 or higher. With 1.2.2 being the highest patch release available, Terraform installs 1.2.2.
Best Practices
Best Practices
- Pin exact provider versions in production configurations to keep behavior consistent across team members and CI runs.
- Use the pessimistic constraint operator (
~>) when you want to allow safe patch updates automatically but block breaking minor/major changes. - Check the Terraform Registry's provider documentation and changelog before widening a version constraint or upgrading.
- Commit the
.terraform.lock.hcldependency lock file to version control so everyterraform initresolves to the same exact provider build.
Security Best Practices
Security
- Always verify that
terraform initreports a provider as signed (e.g. "signed by HashiCorp") before trusting it in a pipeline. - Avoid leaving provider versions completely unconstrained in shared or production configurations — an unexpected upstream release could introduce vulnerabilities or behavior changes without review.
- Review provider release notes for security advisories before relaxing a version constraint to allow newer releases.
Do and Don't
| ✅ Do | ❌ Don't |
|---|---|
| Pin or constrain provider versions in shared configurations | Leave provider versions unconstrained and rely on whatever is "latest" at init time |
Use ~> to allow safe patch-level upgrades |
Use ~> without understanding which version segment it locks |
| Test provider upgrades in a non-production environment first | Upgrade provider versions directly in production without review |
| Commit the lock file so installs are reproducible | Assume terraform init always resolves to the same version without a lock file |
Common Mistakes
Common Mistakes
- Omitting the
required_providersblock entirely and being surprised whenterraform initpulls a newer, incompatible provider version later. - Misreading
~> 1.2as locking to exactly1.2.x, when it actually allows any1.xversion greater than or equal to1.2. - Combining contradictory constraints (e.g. a lower bound higher than an upper bound) and not noticing until
terraform initfails to resolve a version.
Troubleshooting
# Re-run after changing version constraints
terraform init
# Force Terraform to re-evaluate and install provider versions
terraform init -upgrade
# Confirm which provider version is currently in use
terraform providers
Real-World Examples
Platform Team — Pinning to Avoid a Breaking Provider Upgrade
Scenario: A team's CI pipeline ran terraform init without any version constraints on a cloud provider plugin.
Problem: A new major provider release shipped with breaking changes to a resource's required arguments, causing every pipeline run to fail overnight with no code changes on the team's side.
Solution: Added a required_providers block pinning the provider to the last known-good version, then planned and tested the major upgrade separately before adopting it.
Outcome: CI pipeline runs became stable again immediately, and the provider upgrade was rolled out deliberately once compatibility was verified.
Security Team — Blocking a Vulnerable Provider Release
Scenario: A provider plugin used across the organization's Terraform configurations had a version with a disclosed security issue.
Problem: Some configurations had no version constraint and risked picking up the vulnerable release on the next terraform init.
Solution: Updated required_providers across affected configurations with a != constraint excluding the vulnerable version, combined with a ~> constraint to allow only safe patch releases.
Outcome: New initializations could no longer resolve to the vulnerable version, closing the exposure window without waiting for every team to manually intervene.
Startup — Reproducible Builds Across Developer Laptops
Scenario: A small engineering team noticed terraform plan produced different results depending on which developer's laptop ran it.
Problem: Provider versions weren't pinned, so different developers had initialized with different provider releases installed at different times.
Solution: Added explicit version constraints to required_providers and committed the .terraform.lock.hcl file to the repository.
Outcome: Every developer and CI run resolved to the same provider version, eliminating the inconsistent plan output.
Quick Recap
- By default,
terraform initinstalls the latest available provider version for each required provider. - The
required_providersblock insideterraform { }lets you pin or constrain provider versions per provider, usingsourceandversion. - Constraint operators:
=(exact),!=(exclude),</<=/>/>=(bounds), and~>(pessimistic — allows the given version or later within the same segment). - Constraints can be combined with commas to define a precise acceptable range.
~> 1.2allows any1.xversion>= 1.2;~> 1.2.0only allows1.2.xpatch releases.
Interview / Revision Notes
- Q: What happens if no version constraint is specified for a provider?
terraform initdownloads the latest available version of that provider. - Q: Where do you configure provider version constraints? Inside a
required_providersblock, nested within a top-levelterraform { }block. - Q: What does the
!=operator do in a version constraint? Excludes a specific version from being installed. - Q: What's the difference between
~> 1.2and~> 1.2.0?~> 1.2allows any1.xversion>= 1.2(up to but excluding2.0);~> 1.2.0only allows patch-level releases within1.2.x. - Q: How would you require a version strictly between two values while excluding one specific version? Combine constraints with commas, e.g.
version = "> 1.2.0, < 2.0.0, != 1.4.0". - Q: Why pin provider versions in production rather than always using the latest? Provider behavior can change between versions, and an unplanned upgrade could break a working configuration without warning.