Quick Answer
Pin Terraform core with required_version and providers with required_providers version constraints. Use ~> (pessimistic) for most cases — it allows patch/minor updates while blocking breaking changes. Always commit .terraform.lock.hcl.
Terraform Core Version
terraform {
required_version = ">= 1.5.0, < 2.0.0"
}
This ensures anyone running this config has Terraform 1.5+ but not 2.x. If someone runs an incompatible version:
Error: Unsupported Terraform Core version
This configuration does not support Terraform version 1.3.9.
Provider Version Constraints
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.40" # >= 5.40.0, < 6.0.0
}
azurerm = {
source = "hashicorp/azurerm"
version = ">= 3.80, < 4.0"
}
google = {
source = "hashicorp/google"
version = "~> 5.20.0" # >= 5.20.0, < 5.21.0 (patch only)
}
}
}
Version Constraint Operators
| Operator | Meaning | Example | Allows |
|---|---|---|---|
= 5.40.0 | Exact version | Only 5.40.0 | 5.40.0 |
!= 5.40.0 | Exclude version | Any except 5.40.0 | All but 5.40.0 |
> 5.40 | Greater than | 5.41+ | 5.41.0, 6.0.0, etc. |
>= 5.40 | Greater than or equal | 5.40+ | 5.40.0, 5.41.0, etc. |
< 6.0 | Less than | Below 6.0 | 5.99.99, etc. |
<= 5.40 | Less than or equal | 5.40 and below | 5.40.0, 5.39.0, etc. |
~> 5.40 | Pessimistic (recommended) | >= 5.40.0, < 6.0.0 | 5.40.x, 5.41.x, 5.99.x |
~> 5.40.0 | Pessimistic (patch) | >= 5.40.0, < 5.41.0 | 5.40.0, 5.40.1, 5.40.99 |
The Lock File (.terraform.lock.hcl)
# Created by terraform init
# Records EXACT versions + hashes used
terraform init # Creates lock file
terraform init -upgrade # Upgrades within constraints
# .terraform.lock.hcl (auto-generated — don't edit manually)
provider "registry.terraform.io/hashicorp/aws" {
version = "5.40.0"
constraints = "~> 5.40"
hashes = [
"h1:abc123...",
]
}
Always commit .terraform.lock.hcl to git — it ensures everyone uses the exact same provider version.
Root Module vs Child Module Constraints
# Root module — pin tightly
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.40" # Narrow range
}
}
}
# Child module — use loose minimum
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 5.0" # Wide range — let root module decide
}
}
}
Upgrade Strategy
Step 1: Check Current Versions
terraform version
terraform providers
Step 2: Update Constraints
# Bump the constraint
aws = {
source = "hashicorp/aws"
version = "~> 5.50" # Was ~> 5.40
}
Step 3: Upgrade
terraform init -upgrade
terraform plan # Review changes — new provider versions may change behavior
Step 4: Test
terraform plan # Check for unexpected changes
terraform apply # Apply in non-production first
Common Issues
| Problem | Solution |
|---|---|
| “No matching version” | Broaden constraints or check registry |
| “Inconsistent dependency lock file” | Run terraform init -upgrade |
| Provider version conflict between modules | Use compatible ranges |
| Wrong Terraform core version | Install correct version with tfenv |
Best Practices
- Use
~>for most constraints — allows safe updates, blocks breaking changes - Pin tightly in root modules — you control the exact versions
- Pin loosely in shared modules — let consumers choose
- Commit the lock file — reproducible builds across machines
- Upgrade one provider at a time — isolate changes
- Test upgrades in non-production first — providers can change behavior
Hands-On Courses
Related Articles
- Terraform Required Providers Block
- Fix Provider Version Conflict
- How to Upgrade Terraform
- Terraform Glossary
Conclusion
Version constraints protect your infrastructure from unexpected breaking changes. Use ~> for providers, >= for modules, and always commit the lock file. Upgrade deliberately — one provider at a time, in non-production first.




