Terraform Provider Versioning
Provider versioning is the practice of specifying which versions of provider plugins your configuration is compatible with — and locking to exact versions across your team. Without versioning, a provider update can silently break your infrastructure configuration or behavior.
Why Provider Versioning Matters
Providers release new versions frequently. The AWS provider alone releases updates every few weeks. These updates can:
- Add new resource types or attributes
- Change default behavior of existing resources
- Deprecate or remove arguments
- Fix bugs that your configuration may have been accidentally relying on
Without pinned versions, terraform init in CI on Monday and terraform init by a teammate on Thursday might download different provider versions — leading to inconsistent behavior.
Version Constraint Syntax
terraform { required_providers { aws = { source = "hashicorp/aws" version = "~> 5.50" # Most common — allows 5.50.x and 5.51+ but not 6.0 } }}Constraint Operators Explained
| Operator | Example | Meaning |
|---|---|---|
= | = 5.54.1 | Exact version only |
!= | != 5.51.0 | Any version except this |
> | > 5.0 | Greater than |
>= | >= 5.50 | Greater than or equal |
< | < 6.0 | Less than |
<= | <= 5.60 | Less than or equal |
~> | ~> 5.50 | Allows rightmost version to increment freely (pessimistic constraint) |
The ~> operator is the community standard:
~> 5.50 # >= 5.50 and < 6.0 (allows 5.51, 5.52, ... but not 6.0)~> 5.50.1 # >= 5.50.1 and < 5.51 (only patch updates)~> 5 # >= 5.0 and < 6.0 (same as ~> 5.0)The .terraform.lock.hcl File
After terraform init, Terraform creates or updates .terraform.lock.hcl with the exact resolved version:
# .terraform.lock.hcl — generated automatically, should be committed to gitprovider "registry.terraform.io/hashicorp/aws" { version = "5.54.1" constraints = "~> 5.50" hashes = [ "h1:abc123...", # Hash for the platform running this init (e.g., macOS ARM) "zh:def456...", # Canonical hash (same across all platforms) "zh:ghi789...", # Additional platform hashes ]}
provider "registry.terraform.io/hashicorp/random" { version = "3.6.2" constraints = ">= 3.5" hashes = [ "h1:xyz789...", "zh:abc123...", ]}Always commit this file. It ensures:
- CI/CD uses the identical provider version as local development
- No silent upgrades between team members
- Reproducible builds
Upgrading Provider Versions
Check for Available Upgrades
# View current locked versionscat .terraform.lock.hcl
# Check what versions satisfy your constraints (without applying)terraform providersUpgrade to Latest Within Constraints
# Upgrade all providers to latest within their constraintterraform init -upgrade
# This updates .terraform.lock.hcl with new versions# Review the diff before committinggit diff .terraform.lock.hclManually Upgrading to a New Major Version
When a provider releases a major version (5.x → 6.x), you must explicitly update the constraint:
# Beforeversion = "~> 5.50"
# After updatingversion = "~> 6.0"Then run terraform init -upgrade and test thoroughly — major versions often include breaking changes.
Terraform CLI Versioning
Just as important as provider versions — pin the Terraform CLI version itself:
terraform { required_version = ">= 1.6, < 2.0"}Use tfenv or asdf to manage multiple Terraform versions locally:
# Using tfenvtfenv install 1.8.5tfenv use 1.8.5terraform version
# Using .terraform-version file (auto-selected by tfenv)echo "1.8.5" > .terraform-versionIn CI/CD, always pin the Terraform version explicitly:
# GitHub Actions- uses: hashicorp/setup-terraform@v3 with: terraform_version: "1.8.5" # Pin — never use "latest"Provider Version Strategy by Environment
# Development: more permissive, can test newer versionsterraform { required_providers { aws = { source = "hashicorp/aws" version = "~> 5.50" } } required_version = ">= 1.6"}
# Production: pin exact versions after testing# environments/production/versions.tfterraform { required_providers { aws = { source = "hashicorp/aws" version = "= 5.54.1" # Exact — no surprises in production } } required_version = "= 1.8.5"}Handling Provider Deprecations
When a provider deprecates an argument, Terraform shows warnings during plan:
Warning: Argument is deprecated
on main.tf line 15, in resource "aws_s3_bucket" "data": 15: acl = "private"
Use the aws_s3_bucket_acl resource instead.Fix before the deprecation becomes a hard error in a future provider version:
# Before (deprecated)resource "aws_s3_bucket" "data" { bucket = "my-bucket" acl = "private" # Deprecated in aws provider 4.0}
# After (current)resource "aws_s3_bucket" "data" { bucket = "my-bucket"}
resource "aws_s3_bucket_acl" "data" { bucket = aws_s3_bucket.data.id acl = "private"}Multi-Provider Version Coordination
terraform { required_providers { aws = { source = "hashicorp/aws" version = "~> 5.50" } kubernetes = { source = "hashicorp/kubernetes" version = "~> 2.30" } helm = { source = "hashicorp/helm" version = "~> 2.13" } } required_version = ">= 1.6, < 2.0"}When providers depend on each other’s resources (e.g., Helm and Kubernetes), upgrading one may require upgrading the other. Check the provider changelogs for compatibility notes.