Skip to content
All essays
ArchitectureJanuary 19, 202418 min

Terraform Providers: Understanding and Using Providers

Deep dive into Terraform providers. Learn how providers work, configure them, and use them to manage resources across multiple cloud platforms and services.

Ü
Ümit Uz
Mobile & Full Stack Developer

What are Terraform Providers?

Terraform providers are plugins that Terraform uses to manage resources. They act as translators between Terraform's configuration language and the APIs of various services, enabling you to manage infrastructure across multiple cloud providers and services.

Provider Architecture

Providers are written in Go and implement a standard interface that Terraform uses to:

  • Discover available resources and data sources
  • Read and update resource state
  • Plan and apply changes
  • Handle authentication and API communication

How Providers Work

Provider Lifecycle

  1. 1Initialization: Terraform downloads and configures providers
  2. 2Schema Exchange: Terraform requests resource schemas
  3. 3Planning: Provider calculates changes needed
  4. 4Application: Provider executes API calls
  5. 5State Update: Provider returns new state to Terraform

Communication Flow

┌─────────────┐         ┌──────────────┐         ┌─────────────┐
│  Terraform  │ ────────▶│   Provider   │ ────────▶│     API     │
│   Core      │ ◀─────── │   Plugin     │ ◀─────── │  Endpoint   │
└─────────────┘         └──────────────┘         └─────────────┘
       │                         │
       └─────────────────────────┘
         State Management

Installing and Configuring Providers

Provider Source

hcl
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }

    azurerm = {
      source  = "hashicorp/azurerm"
      version = "~> 3.0"
    }
  }
}

Provider Sources Breakdown

  • hashicorp/aws: Official HashiCorp AWS provider
  • hashicorp/azurerm: Official HashiCorp Azure provider
  • google-beta: Google provider with beta features
  • kreuzwerker/docker: Community Docker provider

Version Constraints

hcl
terraform {
  required_providers {
    # Exact version
    aws = {
      version = "5.0.0"
    }

    # Minimum version
    azurerm = {
      version = ">= 3.0"
    }

    # Pessimistic version constraint
    google = {
      version = "~> 4.0"  # >= 4.0, < 5.0
    }

    # Range
    docker = {
      version = ">= 2.0, < 4.0"
    }
  }
}

Provider Initialization

bash
terraform init

Output:

Initializing the backend...

Initializing provider plugins...
- Finding hashicorp/aws versions matching "~> 5.0"...
- Installing hashicorp/aws v5.0.0...
- Installed hashicorp/aws v5.0.0 (signed by HashiCorp)

Terraform has created a lock file...

AWS Provider

Basic Configuration

hcl
provider "aws" {
  region = "us-east-1"
}

Authentication Methods

1. Environment Variables:

bash
export AWS_ACCESS_KEY_ID="your_access_key"
export AWS_SECRET_ACCESS_KEY="your_secret_key"
export AWS_DEFAULT_REGION="us-east-1"

2. Shared Credentials File:

bash
~/.aws/credentials
[default]
aws_access_key_id = your_access_key
aws_secret_access_key = your_secret_key

[production]
aws_access_key_id = prod_access_key
aws_secret_access_key = prod_secret_key
hcl
provider "aws" {
  region  = "us-east-1"
  profile = "production"
}

3. Static Credentials (Not Recommended):

hcl
provider "aws" {
  region     = "us-east-1"
  access_key = "your_access_key"
  secret_key = "your_secret_key"
}

4. Assume Role:

hcl
provider "aws" {
  region = "us-east-1"

  assume_role {
    role_arn     = "arn:aws:iam::123456789012:role/TerraformRole"
    session_name = "terraform-session"
    external_id  = "unique_external_id"
  }
}

5. Web Identity (EKS, ECS):

hcl
provider "aws" {
  region = "us-east-1"

  assume_role_with_web_identity {
    role_arn                = "arn:aws:iam::123456789012:role/EKSRole"
    session_name            = "terraform-session"
    web_identity_token_file = "/var/run/secrets/eks.amazonaws.com/serviceaccount/token"
  }
}

Multiple AWS Providers

hcl
provider "aws" {
  alias  = "us_east_1"
  region = "us-east-1"
}

provider "aws" {
  alias  = "eu_west_1"
  region = "eu-west-1"
}

resource "aws_instance" "east" {
  provider    = aws.us_east_1
  ami         = "ami-0c55b159cbfafe1f0"
  instance_type = "t2.micro"
}

resource "aws_instance" "west" {
  provider    = aws.eu_west_1
  ami         = "ami-0c55b159cbfafe1f0"
  instance_type = "t2.micro"
}

AWS Provider Features

hcl
provider "aws" {
  region = "us-east-1"

  # Default tags for all resources
  default_tags {
    tags = {
      Environment = "production"
      ManagedBy   = "Terraform"
      Project     = "my-app"
    }
  }

  # Ignore tags
  ignore_tags {
    keys = ["GitCommit"]
  }

  # Max retries
  max_retries = 10

  # HTTP settings
  http_proxy  = "http://proxy.example.com:8080"
  https_proxy = "https://proxy.example.com:8080"

  # S3 force path style
  s3_force_path_style = true

  # Skip credentials validation
  skip_credentials_validation = true

  # Skip metadata API check
  skip_metadata_api_check = true

  # Skip region validation
  skip_region_validation = true

  # Retry modes
  retry_mode = "adaptive"
}

Azure Provider

Basic Configuration

hcl
provider "azurerm" {
  features {}
}

Authentication Methods

1. Service Principal:

bash
export ARM_CLIENT_ID="your_client_id"
export ARM_CLIENT_SECRET="your_client_secret"
export ARM_SUBSCRIPTION_ID="your_subscription_id"
export ARM_TENANT_ID="your_tenant_id"

2. Managed Identity:

hcl
provider "azurerm" {
  features {}

  use_msi = true
}

3. Azure CLI:

bash
az login
az account set --subscription "your_subscription_id"
hcl
terraform {
  required_providers {
    azurerm = {
      source  = "hashicorp/azurerm"
      version = "~> 3.0"
    }
  }
}

provider "azurerm" {
  features {}
  skip_provider_registration = true
}

Azure Provider Features

hcl
provider "azurerm" {
  features {
    # API Management
    api_management {
      purge_soft_delete_on_destroy = true
    }

    # Key Vault
    key_vault {
      purge_soft_delete_on_destroy       = true
      purge_soft_deleted_certificates_on_destroy = true
    }

    # Virtual Machine
    virtual_machine {
      delete_os_disk_on_deletion = true
    }

    # Virtual Network
    virtual_network {
      soft_delete_enabled = false
    }
  }

  # Storage account settings
  storage_use_azuread = true

  # Disable Terraform partner ID
  disable_terraform_partner_id = true
}

Google Provider

Basic Configuration

hcl
provider "google" {
  project = "my-project-id"
  region  = "us-central1"
  zone    = "us-central1-a"
}

Authentication Methods

1. Service Account JSON:

hcl
provider "google" {
  project      = "my-project-id"
  region       = "us-central1"
  credentials  = file("account.json")
}

2. Application Default Credentials:

bash
gcloud auth application-default login

3. Service Account Impersonation:

hcl
provider "google" {
  project    = "my-project-id"
  region     = "us-central1"
  impersonate_service_account = "terraform@my-project-id.iam.gserviceaccount.com"
}

Google Provider Features

hcl
provider "google" {
  project = "my-project-id"
  region  = "us-central1"

  # User project overrides
  billing_project = "billing-project-id"

  # Request timeout
  timeout = 60

  # Request retry
  retry_errors = ["UNAVAILABLE", "INTERNAL"]

  # Scopes
  scopes = ["https://www.googleapis.com/auth/cloud-platform"]

  # Zone
  zone = "us-central1-a"
}

provider "google-beta" {
  project = "my-project-id"
  region  = "us-central1"
}

Kubernetes Provider

Basic Configuration

hcl
provider "kubernetes" {
  config_path    = "~/.kube/config"
  config_context = "my-context"
}

Authentication Methods

1. Exec Plugin (AWS EKS):

hcl
provider "kubernetes" {
  host                   = data.aws_eks_cluster.main.endpoint
  cluster_ca_certificate = base64decode(data.aws_eks_cluster.main.certificate_authority[0].data)

  exec {
    api_version = "client.authentication.k8s.io/v1beta1"
    command     = "aws"
    args = ["eks", "get-token", "--cluster-name", data.aws_eks_cluster.main.name]
  }
}

2. Static Token:

hcl
provider "kubernetes" {
  host = "https://kubernetes.default.svc"

  token = data.vault_generic_secret.kubernetes_token.data["token"]
}

3. Client Certificate:

hcl
provider "kubernetes" {
  host                   = var.kubernetes_host
  cluster_ca_certificate = base64decode(var.kubernetes_ca_cert)
  client_certificate     = base64decode(var.kubernetes_client_cert)
  client_key             = base64decode(var.kubernetes_client_key)
}

Kubernetes Provider Resources

hcl
resource "kubernetes_deployment" "nginx" {
  metadata {
    name = "nginx-deployment"
    labels = {
      app = "nginx"
    }
  }

  spec {
    replicas = 3

    selector {
      match_labels = {
        app = "nginx"
      }
    }

    template {
      metadata {
        labels = {
          app = "nginx"
        }
      }

      spec {
        container {
          name  = "nginx"
          image = "nginx:1.21.0"

          port {
            container_port = 80
          }
        }
      }
    }
  }
}

resource "kubernetes_service" "nginx" {
  metadata {
    name = "nginx-service"
  }

  spec {
    selector = {
      app = "nginx"
    }

    port {
      port        = 80
      target_port = 80
    }

    type = "LoadBalancer"
  }
}

Custom Providers

Using Community Providers

hcl
terraform {
  required_providers {
    docker = {
      source  = "kreuzwerker/docker"
      version = "~> 3.0"
    }

    helm = {
      source  = "hashicorp/helm"
      version = "~> 2.0"
    }

    github = {
      source  = "integrations/github"
      version = "~> 5.0"
    }
  }
}

Docker Provider Example

hcl
provider "docker" {}

resource "docker_image" "nginx" {
  name = "nginx:latest"
}

resource "docker_container" "nginx" {
  image = docker_image.nginx.image_id
  name  = "nginx-server"

  ports {
    internal = 80
    external = 8080
  }
}

Helm Provider Example

hcl
provider "helm" {
  kubernetes {
    host                   = data.aws_eks_cluster.main.endpoint
    cluster_ca_certificate = base64decode(data.aws_eks_cluster.main.certificate_authority[0].data)

    exec {
      api_version = "client.authentication.k8s.io/v1beta1"
      command     = "aws"
      args = ["eks", "get-token", "--cluster-name", data.aws_eks_cluster.main.name]
    }
  }
}

resource "helm_release" "nginx" {
  name       = "nginx-ingress"
  repository = "https://kubernetes.github.io/ingress-nginx"
  chart      = "ingress-nginx"
  namespace  = "ingress-nginx"

  set {
    name  = "controller.service.type"
    value = "LoadBalancer"
  }
}

Provider Development

Creating a Custom Provider

1. Provider Structure:

bash
my-provider/
├── provider/
│   ├── provider.go
│   ├── resource_example.go
│   └── data_source_example.go
├── main.go
└── go.mod

2. Provider Interface:

go
package provider

func New() *schema.Provider {
  return &schema.Provider{
    ResourcesMap: map[string]*schema.Resource{
      "myprovider_example": resourceExample(),
    },

    DataSourcesMap: map[string]*schema.Resource{
      "myprovider_example": dataSourceExample(),
    },
  }
}

3. Building:

bash
go build -o terraform-provider-myprovider
mkdir -p ~/.terraform.d/plugins/localhost/myorg/myprovider/1.0.0/darwin_arm64
mv terraform-provider-myprovider ~/.terraform.d/plugins/localhost/myorg/myprovider/1.0.0/darwin_arm64/

4. Using:

hcl
terraform {
  required_providers {
    myprovider = {
      source  = "localhost/myorg/myprovider"
      version = "1.0.0"
    }
  }
}

Provider Best Practices

1. Version Management

hcl
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"  # Lock major version
    }
  }
}

2. Provider Pinning

bash
# .terraform.lock.hcl
provider "hashicorp/aws" {
  version = "5.0.0"
  hashes = [
    "h1:abc123...",
  ]
}

3. Separate Provider Files

providers/
├── aws.tf
├── google.tf
└── kubernetes.tf

4. Provider Aliases for Multiple Regions

hcl
provider "aws" {
  alias  = "virginia"
  region = "us-east-1"
}

provider "aws" {
  alias  = "oregon"
  region = "us-west-2"
}

5. Use Environment-Specific Providers

hcl
variable "environment" {
  type    = string
  default = "dev"
}

locals {
  aws_regions = {
    dev  = "us-east-1"
    prod = "us-west-2"
  }
}

provider "aws" {
  region = local.aws_regions[var.environment]
}

Provider Troubleshooting

Common Issues

1. Provider Not Downloaded:

bash
terraform init -upgrade

2. Version Conflicts:

bash
terraform providers

3. Provider Timeout:

hcl
provider "aws" {
  region     = "us-east-1"
  timeout    = "60s"
  max_retries = 5
}

4. Authentication Issues:

bash
terraform login

Debug Mode

bash
TF_LOG=DEBUG terraform apply

Provider Configuration Validation

bash
terraform init
terraform validate
terraform plan

Advanced Provider Features

Provider Functions

hcl
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

provider "aws" {
  region = "us-east-1"
}

# Use provider functions
locals {
  ami_id = provider::aws::region("ami-0c55b159cbfafe1f0")
}

Provider Schema Validation

hcl
variable "region" {
  type        = string
  description = "AWS region"

  validation {
    condition     = can(regex("^us-[a-z]+-[0-9]+$", var.region))
    error_message = "Region must be a valid AWS region."
  }
}

provider "aws" {
  region = var.region
}

Provider Retry Logic

hcl
provider "aws" {
  region = "us-east-1"

  # Custom retry configuration
  max_retries = 10

  # Exponential backoff
  retry_mode = "adaptive"
}

Provider and Terraform Cloud

Terraform Cloud Providers

hcl
terraform {
  cloud {
    organization = "my-org"
    workspaces {
      name = "my-workspace"
    }
  }

  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "5.0.0"
    }
  }
}

provider "aws" {
  region = "us-east-1"
}

Environment Variables in Cloud

Set in Terraform Cloud workspace:

AWS_ACCESS_KEY_ID
AWS_SECRET_ACCESS_KEY
AWS_DEFAULT_REGION

Conclusion

Terraform providers are the bridge between your configuration and the infrastructure you manage. Understanding how to configure, authenticate, and optimize providers is essential for successful IaC implementations.

Key takeaways:

  • Providers enable multi-cloud and multi-service management
  • Authentication methods vary by provider
  • Version management is critical
  • Multiple providers enable complex architectures
  • Community providers extend Terraform's capabilities

Master providers to unlock Terraform's full potential across your entire infrastructure.

Enjoyed this essay? Share it.

Related essays

Next essay
Terraform State: Management and Best Practices