Terraform Providers
Providers are plugins that enable Terraform to interact with cloud platforms, SaaS APIs, and other services. Every resource you create (aws_instance, azurerm_virtual_machine, google_compute_instance) is backed by a provider that knows how to translate your HCL into API calls.
How Providers Work
When you write resource "aws_s3_bucket" "my_bucket" {}, Terraform doesn’t know natively how to create an S3 bucket. The AWS provider plugin does. It:
- Translates your HCL resource configuration into AWS API calls
- Handles authentication and retry logic
- Maps API responses back into Terraform state
There are 3,000+ providers on the Terraform Registry — AWS, Azure, GCP, Kubernetes, GitHub, Datadog, PagerDuty, Cloudflare, and everything else.
Declaring Providers
terraform { required_providers { aws = { source = "hashicorp/aws" # registry.terraform.io/hashicorp/aws version = "~> 5.50" } azurerm = { source = "hashicorp/azurerm" version = "~> 3.100" } google = { source = "hashicorp/google" version = "~> 5.30" } kubernetes = { source = "hashicorp/kubernetes" version = "~> 2.30" } random = { source = "hashicorp/random" version = ">= 3.5" } }}Provider Configuration
Each provider has its own configuration block for authentication and defaults:
AWS Provider
provider "aws" { region = "us-east-1"
# Option 1: Explicit credentials (dev/testing only, not recommended for production) access_key = var.aws_access_key secret_key = var.aws_secret_key
# Option 2: Assume a role (recommended for CI/CD) assume_role { role_arn = "arn:aws:iam::123456789012:role/TerraformRole" session_name = "terraform-session" }
# Default tags applied to all AWS resources default_tags { tags = { ManagedBy = "terraform" Environment = var.environment } }}Best practice: Never put credentials in .tf files. Use environment variables (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY) or IAM roles (for EC2/ECS/Lambda) or OIDC (for CI/CD).
Azure Provider
provider "azurerm" { features {} # Required block, can be empty
subscription_id = var.subscription_id tenant_id = var.tenant_id client_id = var.client_id # Service principal app ID client_secret = var.client_secret # Or use AZURE_CLIENT_SECRET env var}GCP Provider
provider "google" { project = var.project_id region = "us-central1" zone = "us-central1-a" # Credentials from GOOGLE_APPLICATION_CREDENTIALS env var or ADC}Provider Authentication Methods
| Provider | Recommended Method | How |
|---|---|---|
| AWS | IAM role / OIDC | EC2 instance role, ECS task role, or GitHub Actions OIDC |
| Azure | Workload Identity / Service Principal | Azure AD + OIDC or service principal with client secret |
| GCP | Service Account + Workload Identity | Application Default Credentials or Workload Identity |
| Any | Environment variables | AWS_*, ARM_*, GOOGLE_* env vars |
Provider Aliases for Multi-Region Deployments
# Default provider — us-east-1provider "aws" { region = "us-east-1"}
# Alias for a second regionprovider "aws" { alias = "us_west" region = "us-west-2"}
# Alias for a different AWS account (cross-account deployment)provider "aws" { alias = "security_account" region = "us-east-1" assume_role { role_arn = "arn:aws:iam::999888777666:role/SecurityRole" }}
# Use the aliased provider on specific resourcesresource "aws_s3_bucket" "west_backup" { provider = aws.us_west # Uses the us-west-2 provider bucket = "my-west-backup"}
resource "aws_cloudtrail" "org_trail" { provider = aws.security_account name = "org-cloudtrail"}Community and Partner Providers
# Cloudflare — DNS and CDN managementprovider "cloudflare" { api_token = var.cloudflare_api_token}
resource "cloudflare_record" "www" { zone_id = var.zone_id name = "www" value = aws_lb.main.dns_name type = "CNAME" proxied = true}
# Datadog — monitoring configuration as codeprovider "datadog" { api_key = var.datadog_api_key app_key = var.datadog_app_key}
resource "datadog_monitor" "cpu_alert" { name = "High CPU on ${var.service_name}" type = "metric alert" query = "avg(last_5m):avg:system.cpu.user{service:${var.service_name}} > 80" message = "CPU usage is above 80%. @pagerduty-on-call"}
# GitHub — repository and team managementprovider "github" { token = var.github_token owner = "my-organization"}
resource "github_repository" "service" { name = "my-service" visibility = "private" has_issues = true}Provider Versions and Stability
# Constraint operatorsversion = "= 5.54.1" # Exact version onlyversion = ">= 5.50" # Any version >= 5.50version = "~> 5.50" # >= 5.50 and < 6.0 (most common)version = ">= 5.0, < 6.0" # Explicit rangeThe ~> (pessimistic constraint) is the community standard because it allows patch updates (bug fixes) while preventing major version breaking changes from automatically applying.
After running terraform init, the exact version chosen is recorded in .terraform.lock.hcl — always commit this file.