All guides
Practice now
D3 · Terraform Core

Providers: plugin architecture and configuration

are the bridge between Terraform and external APIs. Understanding their plugin architecture, support tiers, and advanced configuration is essential for the exam.

Plugin architecture

Terraform is split into two main components that communicate through an RPC protocol over gRPC:

Terraform Core

  • • Reads and parses the HCL files
  • • Builds the dependency graph
  • • Manages the
  • • Coordinates the execution of the
  • • Communicates with providers via gRPC
  • • Binary: terraform

Terraform Providers (plugins)

  • • Implement and
  • • Contain the logic for each API (AWS, Azure…)
  • • Are downloaded into .terraform/providers/
  • • Are binaries separate from the core
  • • Each provider is a separate process (RPC)
  • • Developed by HashiCorp, partners, or the community

💡 Why separate plugins

Separating Core from Providers allows: (1) the core doesn't need to be recompiled when new APIs are added, (2) providers can have their own release cycle, (3) any company can build its own provider without modifying Terraform Core.

Provider tiers

TierMaintained byRegistry namespaceExamples
OfficialHashiCorphashicorp/hashicorp/aws, hashicorp/azurerm, hashicorp/google
PartnerTechnology company with a HashiCorp agreementcompany/datadog/datadog, mongodb/mongodbatlas, pagerduty/pagerduty
CommunityOpen-source communityanyone/kreuzwerker/docker, hashicorp/dns (deprecated examples)

🎯 For the exam

Official providers are maintained by HashiCorp and have the highest level of support and testing. The source of an official provider always starts with hashicorp/. The exam may ask you about the difference between official, partner, and community.

required_providers and versions

Always declare the providers you use in the required_providers block inside terraform . Specifying the version is a mandatory best practice in production.

terraform {
  required_version = ">= 1.6.0"

  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"        # >= 5.0.0, < 6.0.0
    }
    random = {
      source  = "hashicorp/random"
      version = "= 3.5.1"       # exact version
    }
    datadog = {
      source  = "DataDog/datadog"
      version = ">= 3.20.0"
    }
  }
}
OperatorMeaningExample
~>Pessimistic constraint: only increments the last segment~> 5.0 = >=5.0, <6.0 | ~> 5.2.1 = >=5.2.1, <5.3
>=Greater than or equal to>= 5.0 = any version 5.0 or higher
<=Less than or equal to<= 5.9 = up to and including 5.9
=Exact version (hard pin)= 5.31.0 = only that version
!=Excludes a specific version!= 5.10.0 = any version except 5.10.0
>Strictly greater than> 5.0 = from 5.0.1 onward

💡 ~> is the most used operator on the exam

~> 5.0 means "5.x but not 6.x". ~> 5.2.1 means "5.2.x but not 5.3". The difference lies in which segment is the "last" one allowed to increment.

.terraform.lock.hcl

After terraform init, the is generated, pinning the exact versions installed, even if the constraint allows a range.

# .terraform.lock.hcl — auto-generated, IS committed
provider "registry.terraform.io/hashicorp/aws" {
  version     = "5.31.0"          # exact installed version
  constraints = "~> 5.0"          # constraint from required_providers
  hashes = [
    "h1:HASH_FOR_CURRENT_PLATFORM",
    "zh:CROSS_VERIFICATION_HASH",
  ]
}

# To add hashes for other platforms (e.g. CI uses Linux, devs use Mac):
# terraform providers lock -platform=linux_amd64 -platform=darwin_arm64

✅ Why commit the lock file

  • • Guarantees everyone uses the exact same provider version
  • • Prevents an init on another machine from installing a different version
  • • Allows auditing which provider version produced a specific change
  • • Terraform only updates it when you run init -upgrade

⚠️ Don't confuse it with tfstate

  • • The lock file pins PROVIDER versions (not resources)
  • • The tfstate holds the state of the created RESOURCES
  • • Lock file: is committed | tfstate: is not committed (use a remote backend)

Multiple configs with alias

When you need multiple configurations of the same provider (for example, two AWS regions or two accounts), you use the alias argument.

# Two AWS regions in the same module
provider "aws" {
  region = "us-east-1"
  # No alias = default provider
}

provider "aws" {
  alias  = "west"
  region = "us-west-2"
}

# Use the aliased provider in a resource
resource "aws_vpc" "main" {
  cidr_block = "10.0.0.0/16"
  # Uses the default provider (us-east-1)
}

resource "aws_vpc" "disaster_recovery" {
  provider   = aws.west          # reference: provider_name.alias
  cidr_block = "10.1.0.0/16"
}

# Pass an aliased provider to a module
module "vpc_west" {
  source = "./modules/vpc"
  providers = {
    aws = aws.west               # provider map for the module
  }
}

Provider inheritance in modules

Child automatically inherit the default provider from the root module. You only need the providers argument when using an alias or when you want to override the inherited provider.

ScenarioNeeds providers = {}?Reason
Module uses a single provider, same as root❌ NoAutomatically inherits the default provider from the root
Module uses a provider with an alias✅ YesYou must map which root alias corresponds to the module's provider
Module needs a provider different from the root✅ YesThe root explicitly passes the correct provider to the module
Multi-provider module (e.g.: aws + random)✅ YesEach provider used by the module must be mapped explicitly

Did you get this topic?

Test what you just learned

A Terraform module needs to create resources in two different AWS accounts. How should this be configured correctly?

Ready to test "Providers: Plugin Architecture and Configuration"?

Put what you learned to the test with original Terraform Associate exam questions.

Start Terraform Associate mock exam

Unlimited mock exams with the Pro plan

Log in to track your progress.