Cloud  /  Terraform

IaC Terraform 50 guides · updated 2026

Infrastructure as code done right — providers, state, reusable modules, and the workflow patterns that keep multi-cloud deployments sane in 2026.

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:

  1. Translates your HCL resource configuration into AWS API calls
  2. Handles authentication and retry logic
  3. 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

versions.tf
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

ProviderRecommended MethodHow
AWSIAM role / OIDCEC2 instance role, ECS task role, or GitHub Actions OIDC
AzureWorkload Identity / Service PrincipalAzure AD + OIDC or service principal with client secret
GCPService Account + Workload IdentityApplication Default Credentials or Workload Identity
AnyEnvironment variablesAWS_*, ARM_*, GOOGLE_* env vars

Provider Aliases for Multi-Region Deployments

# Default provider — us-east-1
provider "aws" {
region = "us-east-1"
}
# Alias for a second region
provider "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 resources
resource "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 management
provider "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 code
provider "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 management
provider "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 operators
version = "= 5.54.1" # Exact version only
version = ">= 5.50" # Any version >= 5.50
version = "~> 5.50" # >= 5.50 and < 6.0 (most common)
version = ">= 5.0, < 6.0" # Explicit range

The ~> (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.