Skip to content
Infrastructure as Code iac concepts 4 min read

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_instance or aws_s3_bucket.
  • Data sources — read-only lookups of existing information, such as aws_ami or aws_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.hcl to 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

ProviderSourceTypical use
AWShashicorp/awsEC2, S3, IAM, VPC, RDS, Lambda
Azurehashicorp/azurermVMs, storage accounts, resource groups
Google Cloudhashicorp/googleCompute Engine, GCS, GKE, IAM
Kuberneteshashicorp/kubernetesDeployments, services, namespaces
Randomhashicorp/randomGenerated 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.hcl for reproducible runs.
  • Keep provider configuration out of reusable modules; pass configured providers in from the root module instead.
  • Use alias to 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 -upgrade deliberately 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.
Last updated June 14, 2026
Was this helpful?