Cloud  /  Terraform

IaC Terraform 50 guides · updated 2026

Infrastructure as code done right — providers, state, reusable modules, and the workflow patterns that keep multi-cloud deployments sane in 2026.

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:

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

OperatorExampleMeaning
== 5.54.1Exact version only
!=!= 5.51.0Any version except this
>> 5.0Greater than
>=>= 5.50Greater than or equal
<< 6.0Less than
<=<= 5.60Less than or equal
~>~> 5.50Allows 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 git
provider "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:


Upgrading Provider Versions

Check for Available Upgrades

Terminal window
# View current locked versions
cat .terraform.lock.hcl
# Check what versions satisfy your constraints (without applying)
terraform providers

Upgrade to Latest Within Constraints

Terminal window
# Upgrade all providers to latest within their constraint
terraform init -upgrade
# This updates .terraform.lock.hcl with new versions
# Review the diff before committing
git diff .terraform.lock.hcl

Manually Upgrading to a New Major Version

When a provider releases a major version (5.x → 6.x), you must explicitly update the constraint:

# Before
version = "~> 5.50"
# After updating
version = "~> 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:

Terminal window
# Using tfenv
tfenv install 1.8.5
tfenv use 1.8.5
terraform version
# Using .terraform-version file (auto-selected by tfenv)
echo "1.8.5" > .terraform-version

In 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

environments/dev/versions.tf
# Development: more permissive, can test newer versions
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.50"
}
}
required_version = ">= 1.6"
}
# Production: pin exact versions after testing
# environments/production/versions.tf
terraform {
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.