Skip to main content
In this lesson we cover Terraform provider version constraints: how to declare them, why they matter, and how Terraform resolves and locks provider versions for reproducible infrastructure deployments. Version constraints control which provider versions Terraform is allowed to install. They are essential for stability and repeatability because providers are released frequently—sometimes multiple times per month. Without explicit constraints, each terraform init could download a newer provider with breaking changes. Constraints make your configuration deterministic and prevent accidental upgrades. Example: pin a provider to an exact version
With this constraint Terraform will only install hashicorp/azurerm version 4.27.0. When you run terraform init, Terraform reads the constraints, queries the registry, and installs the matching version instead of the latest:
This deterministic behavior prevents accidental upgrades and unexpected changes during runs.
The image is a chart explaining version constraints, showing symbols, their meanings, examples, and allowable version ranges. It covers constraints like exact version, pessimistic constraint, minimum/maximum versions, and exclusion of specific versions.
Common operators and patterns
  • Exact version: = 3.29.0 or simply 3.29.0 — only that exact version will be used.
  • Pessimistic constraint: ~> 3.29.0 — allows patch releases within the same minor version (e.g., 3.29.x) but prevents upgrading to 3.30.0.
  • Range operators: >=, <=, >, < — specify minimum/maximum allowed versions.
  • Exclusion: != 4.28.0 — exclude specific versions known to be problematic.
  • Combine multiple operators to express complex rules like minimum + exclusion.
Operators quick reference Example: allow any azurerm version greater than 4.26.0 but exclude 4.28.0
Terraform evaluates the combined constraints and selects the highest compatible version that satisfies them. In the example above, terraform init would select 4.27.0 as the best match. Once a provider version is selected, Terraform records the exact version and its checksums in the .terraform.lock.hcl file so future runs are reproducible.
Include the generated .terraform.lock.hcl file in version control so everyone running the configuration uses the same provider binaries.
A typical lock file entry (generated by terraform init):
When a .terraform.lock.hcl file exists, subsequent terraform init runs will reuse the recorded versions:
Real-world workflow: modular code and provider separation A common pattern is to keep provider configuration in provider.tf and resources in main.tf (or split by resource types). This keeps configuration modular and easier to manage in editors like Visual Studio Code. Example files provider.tf
main.tf
With version = "4.55.0", terraform init will fetch that exact provider instead of any newer release:
Refreshing provider selection and updating the lock file If you change version constraints and want Terraform to re-evaluate available versions and update .terraform.lock.hcl, run terraform init --upgrade. Example: allow versions greater than 4.55.0 but less than 4.58.0:
Then run:
If you modify constraints but do not use --upgrade and the new constraint conflicts with the locked version, Terraform will refuse to proceed and instruct you to run terraform init --upgrade. Example error:
Summary and best practices
  • Declare version constraints in the terraform block under required_providers to control which provider versions Terraform can install.
  • Use exact pins for maximum stability in production, or ranges/pessimistic constraints for controlled patch updates.
  • Combine operators to express ranges and exclusions for known problematic releases.
  • Commit .terraform.lock.hcl to version control to ensure all users and CI environments use the same provider binaries.
  • Use terraform init --upgrade intentionally to refresh provider selections and update the lock file.
Links and references

Watch Video