Quick Answer
The required_providers block declares which providers your config needs, where to download them, and which versions are compatible. Always pin versions to avoid unexpected breaking changes.
Basic Syntax
terraform {
required_version = ">= 1.5"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.40"
}
random = {
source = "hashicorp/random"
version = "~> 3.6"
}
}
}
Source Addresses
# Format: namespace/provider
# Registry: registry.terraform.io (default)
required_providers {
# HashiCorp providers
aws = { source = "hashicorp/aws" }
azurerm = { source = "hashicorp/azurerm" }
google = { source = "hashicorp/google" }
# Third-party providers
datadog = { source = "DataDog/datadog" }
cloudflare = { source = "cloudflare/cloudflare" }
mongodb = { source = "mongodb/mongodbatlas" }
# Custom/private registry
internal = { source = "company.example.com/team/internal" }
}
Version Constraints
| Constraint | Meaning | Example |
|---|---|---|
= 5.40.0 | Exact version | Only 5.40.0 |
>= 5.40 | Minimum version | 5.40 or higher |
~> 5.40 | Pessimistic (recommended) | >= 5.40.0, < 6.0.0 |
~> 5.40.0 | Patch only | >= 5.40.0, < 5.41.0 |
>= 5.30, < 6.0 | Range | Between 5.30 and 5.99 |
# Recommended: pessimistic constraint
aws = {
source = "hashicorp/aws"
version = "~> 5.40" # Allows 5.40.x, 5.41.x, ... but not 6.0
}
# Strict pinning (for reproducibility)
aws = {
source = "hashicorp/aws"
version = "= 5.40.0"
}
The Lock File (.terraform.lock.hcl)
# Generated by terraform init
# Commit this file to version control!
terraform init # Creates/updates lock file
terraform init -upgrade # Upgrades within version constraints
# .terraform.lock.hcl (auto-generated)
provider "registry.terraform.io/hashicorp/aws" {
version = "5.40.0"
constraints = "~> 5.40"
hashes = [
"h1:abc123...",
"zh:def456...",
]
}
Always commit .terraform.lock.hcl — it ensures everyone uses the exact same provider version.
In Modules
Child modules should declare required_providers but not pin exact versions — let the root module control:
# modules/networking/versions.tf
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 5.0" # Minimum, not exact
}
}
}
Multi-Provider with Aliases
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.40"
}
}
}
provider "aws" {
region = "us-east-1"
}
provider "aws" {
alias = "west"
region = "us-west-2"
}
resource "aws_s3_bucket" "east" {
bucket = "my-bucket-east"
}
resource "aws_s3_bucket" "west" {
provider = aws.west
bucket = "my-bucket-west"
}
Common Mistakes
| Mistake | Fix |
|---|---|
| No version constraint | Always add version = "~> X.Y" |
| Not committing lock file | Add .terraform.lock.hcl to git |
| Exact versions in modules | Use >= in modules, ~> in root |
Missing source | Required since Terraform 0.13 |
Related Articles
Conclusion
Always declare required_providers with source and version constraints. Use ~> for the root module, >= for reusable modules, commit the lock file, and run terraform init -upgrade when you want newer versions.




