Providers
Terraform itself knows nothing about AWS, Azure, GCP, or Kubernetes. All of that platform-specific knowledge lives in providers — plugins that translate the resources you declare in HCL into concrete calls against a platform’s API. When you write a resource block, Terraform hands it to the matching provider, which knows how to create, read, update, and delete that object. Understanding providers is essential because they define what you can manage and how Terraform authenticates and talks to each platform.
What a provider is
A provider is a separately versioned plugin (a Go binary) that Terraform Core invokes over an RPC interface. Terraform Core is responsible for the planning algorithm, the dependency graph, and state management; the provider is responsible for the mapping between a declared resource and the platform’s real API.
Each provider exposes two kinds of building blocks:
- Resources — objects Terraform creates and manages over their full lifecycle, such as
aws_instanceoraws_s3_bucket. - Data sources — read-only lookups of existing information, such as
aws_amioraws_availability_zones.
This same architecture is shared by OpenTofu, the open-source fork of Terraform — providers built for one generally work with the other because both speak the same plugin protocol.
The registry and provider source addresses
Providers are distributed through the Terraform Registry (OpenTofu has its own mirror at registry.opentofu.org). Every provider has a fully qualified source address of the form HOSTNAME/NAMESPACE/TYPE. You declare which providers and versions a configuration needs in a required_providers block:
terraform {
required_version = ">= 1.5"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
random = {
source = "hashicorp/random"
version = "~> 3.6"
}
}
}
provider "aws" {
region = "us-east-1"
}
When you run terraform init, Terraform reads these constraints, downloads the matching plugins into .terraform/, and records exact versions and checksums in .terraform.lock.hcl.
Output:
Initializing provider plugins...
- Finding hashicorp/aws versions matching "~> 5.0"...
- Finding hashicorp/random versions matching "~> 3.6"...
- Installing hashicorp/aws v5.62.0...
- Installed hashicorp/aws v5.62.0 (signed by HashiCorp)
- Installing hashicorp/random v3.6.2...
- Installed hashicorp/random v3.6.2 (signed by HashiCorp)
Terraform has been successfully initialized!
Tip: Always commit
.terraform.lock.hclto version control. It pins provider versions and checksums so every teammate and CI run resolves the exact same plugins, making builds reproducible.
Using many providers at once
A single configuration can use any number of providers together, which is how Terraform manages real-world systems that span multiple platforms. For example, you might provision compute on AWS, DNS records on Cloudflare, and a database role on PostgreSQL — all in one apply, with Terraform ordering the operations based on the dependencies between resources.
resource "aws_instance" "web" {
ami = "ami-0c101f26f147fa7fd"
instance_type = "t3.micro"
tags = {
Name = "web-server"
}
}
resource "cloudflare_record" "web" {
zone_id = "abc123def456"
name = "app"
type = "A"
content = aws_instance.web.public_ip
proxied = true
}
Here the Cloudflare record depends on the AWS instance’s IP. Terraform’s dependency graph guarantees the instance is created first, then its public_ip is passed to the DNS record.
Multiple configurations of the same provider
You can configure the same provider more than once using alias — for instance, to deploy into several AWS regions from one run. Resources then select a configuration with the provider meta-argument.
provider "aws" {
alias = "primary"
region = "us-east-1"
}
provider "aws" {
alias = "dr"
region = "us-west-2"
}
resource "aws_s3_bucket" "primary" {
provider = aws.primary
bucket = "devcraftly-data-primary"
}
resource "aws_s3_bucket" "replica" {
provider = aws.dr
bucket = "devcraftly-data-replica"
}
Common providers
| Provider | Source | Typical use |
|---|---|---|
| AWS | hashicorp/aws | EC2, S3, IAM, VPC, RDS, Lambda |
| Azure | hashicorp/azurerm | VMs, storage accounts, resource groups |
| Google Cloud | hashicorp/google | Compute Engine, GCS, GKE, IAM |
| Kubernetes | hashicorp/kubernetes | Deployments, services, namespaces |
| Random | hashicorp/random | Generated IDs, passwords, suffixes |
Note: Provider configuration — authentication, region, endpoints, and provider-level options — is covered in depth in the dedicated Providers section. This page focuses on the concept and how providers fit into the Terraform model.
Best practices
- Always pin provider versions with
~>constraints and commit.terraform.lock.hclfor reproducible runs. - Keep provider configuration out of reusable modules; pass configured providers in from the root module instead.
- Use
aliasto manage multiple regions or accounts rather than duplicating whole configurations. - Prefer data sources over hardcoded IDs (e.g., look up an AMI with
aws_ami) so configurations stay portable. - Run
terraform init -upgradedeliberately to bump versions, then review and commit the updated lock file. - Authenticate via environment variables or short-lived credentials, never by hardcoding secrets in provider blocks.