TerraformPilot

Terraform

Terraform Required Providers Block Explained

Understand the Terraform required_providers block for version pinning. Covers source addresses, version constraints, lock files, and multi-provider configs.

LLuca Berton1 min read

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

#
ConstraintMeaningExample
= 5.40.0Exact versionOnly 5.40.0
>= 5.40Minimum version5.40 or higher
~> 5.40Pessimistic (recommended)>= 5.40.0, < 6.0.0
~> 5.40.0Patch only>= 5.40.0, < 5.41.0
>= 5.30, < 6.0RangeBetween 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

#
MistakeFix
No version constraintAlways add version = "~> X.Y"
Not committing lock fileAdd .terraform.lock.hcl to git
Exact versions in modulesUse >= in modules, ~> in root
Missing sourceRequired since Terraform 0.13
#

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.

#Terraform#Best Practices#Infrastructure as Code

Share this article