Terraform Providers
Terraform's core knows nothing about AWS, Kubernetes, GitHub, or Datadog. Everything it manages comes from providers: plugins that translate Terraform resources into API calls for a specific platform. There are thousands of them, from the major clouds to DNS hosts, SaaS tools, and databases, which is a large part of why Terraform works as a universal infrastructure-as-code tool.
Handling providers well means pinning versions so upgrades are deliberate, authenticating without long-lived keys, configuring multiple regions or accounts with aliases, and using data sources to read existing infrastructure instead of hard-coding IDs.
TL;DR
- Declare providers in
required_providerswith a source (hashicorp/aws) and a version constraint (~> 6.0). terraform initdownloads them and records exact versions and hashes in.terraform.lock.hcl. Commit that file.- Configure providers in the root module; authenticate with short-lived credentials (OIDC, instance roles), never hard-coded keys.
- Use aliases for multiple regions or accounts, and pass them to modules explicitly.
- Data sources read existing infrastructure (AMIs, VPCs, zones) without managing it.
- Upgrade providers deliberately (
terraform init -upgrade) and read their changelogs; major versions break things.
Quick Example
Core Concepts
Provider Sources and the Registry
A provider source address has three parts: [hostname/]namespace/type. hashicorp/aws is shorthand for registry.terraform.io/hashicorp/aws. Providers come in tiers: official (maintained by HashiCorp), partner (maintained by the vendor, such as Cloudflare or Datadog), and community. Check maintenance activity before depending on a community provider.
Version Constraints
Root modules usually use ~> MAJOR.MINOR. Shared modules should declare the widest range they actually support (>= 5.0, < 7.0) so they don't conflict with callers.
The Dependency Lock File
.terraform.lock.hcl records the exact provider versions selected and their checksums for each platform. Commit it so every machine and CI run uses identical provider builds. terraform init -upgrade picks newer versions within your constraints and updates the lock file, which you then review in a pull request. For teams on mixed OSes, terraform providers lock -platform=linux_amd64 -platform=darwin_arm64 records hashes for all platforms.
Configuration and Authentication
Provider blocks set region, endpoints, and credentials. Credentials should come from the environment, not code:
- CI/CD: OIDC federation (GitHub Actions → AWS IAM role, GCP Workload Identity, Azure federated credentials). No stored keys at all. See GitHub Actions security.
- Cloud runners: instance profiles or managed identities.
- Local: SSO sessions (
aws sso login,gcloud auth application-default login). assume_rolein the provider block to switch into a target account.
Never put access keys in .tf files or variables committed to Git.
Aliases
Multiple configurations of the same provider are distinguished by alias. Resources select one with provider = aws.eu; modules receive them through a providers map:
The same approach handles multiple accounts (one alias per assumed role) and multiple Kubernetes clusters.
Data Sources
A data block reads information from the provider without managing it: the latest AMI, an existing VPC by tag, a Route 53 zone, the current account ID. Data sources keep configurations portable and avoid copy-pasted IDs. They run during plan, so the resource they read must already exist, or depend on something created earlier in the same run.
Best Practices
Pin and Upgrade Deliberately
Constrain to a major version, commit the lock file, and upgrade on a schedule (Renovate and Dependabot support Terraform providers). Read the upgrade guide before crossing a major version; AWS provider majors have historically split or renamed resources.
Use default_tags and Provider-Level Defaults
Setting tags, labels, or project IDs once at the provider level ensures every resource gets them, which is essential for cloud cost allocation and ownership tracking.
Keep Providers Out of Child Modules
Configure providers in the root; child modules only declare required_providers. That keeps modules reusable across regions and accounts, and lets you remove module instances cleanly.
Mirror Providers for Reliability
Large organizations use a provider mirror or cache (provider_installation in the CLI config, or terraform providers mirror) so CI isn't dependent on registry availability and downloads are fast.
Common Mistakes
Hard-Coded Credentials
Not Committing the Lock File
Without .terraform.lock.hcl, each init may select a different provider version within your constraints. Plans then differ between a laptop and CI for no visible reason.
Provider Configuration Depending on Resources Created in the Same Run
Configuring a Kubernetes provider from a cluster that the same apply creates often fails or behaves unpredictably, because provider configuration is evaluated early. Split cluster creation and in-cluster resources into separate configurations or stages.
FAQ
What's the difference between a provider and a module?
A provider is a plugin binary that knows how to call a platform's API and defines resource types (aws_s3_bucket). A module is Terraform code that combines resources, possibly from several providers, into a reusable unit. Modules use providers; providers don't use modules.
Can I write my own provider?
Yes, in Go using the Terraform Plugin Framework. It's worthwhile for internal platforms with APIs you want managed declaratively. For one-off API calls, the http data source or community "REST API" providers may be enough.
Do providers work with OpenTofu?
Yes. OpenTofu uses the same provider protocol and its own registry mirroring the same providers, so nearly all providers work unchanged.
Why does my plan show changes I didn't make after upgrading a provider?
New provider versions can change defaults, add computed attributes, or normalize values differently. Read the changelog, run plan in a lower environment first, and use lifecycle { ignore_changes = [...] } only for attributes that are legitimately managed elsewhere.
Related Topics
- Terraform — The tool overview
- Terraform Modules — Passing provider configurations into modules
- Terraform State — Where provider-managed resources are tracked
- OpenTofu — The open-source fork and its registry
- AWS IAM — Roles and OIDC for Terraform authentication
- Multi-Cloud — Managing several clouds with one tool