Skip to main content
Terraform Required Providers Block Explained

Terraform Required Providers Block Explained

Key Takeaway

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

Table of Contents

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.

🚀

Level Up Your Terraform Skills

Hands-on courses, books, and resources from Luca Berton

Luca Berton
Written by

Luca Berton

DevOps Engineer, AWS Partner, Terraform expert, and author. Creator of Ansible Pilot, Terraform Pilot, and CopyPasteLearn.