Skip to content

2.03 Terraform Multiple Providers

Overview

Terraform supports using multiple providers within a single configuration directory. Each provider exposes its own resource types, and they can be combined freely in main.tf to provision infrastructure across different platforms in one terraform apply.

Abstract

A single Terraform configuration can declare resources from any number of providers simultaneously. When a new provider is added, terraform init must be re-run to download the new plugin. Terraform will reuse already-installed providers and only fetch what is missing.


Why It Matters in Production

  • Real infrastructure rarely involves a single provider — compute on AWS, DNS on Cloudflare, secrets in Vault, and random IDs for naming all coexist in one config
  • Understanding how Terraform resolves provider plugins per resource prevents broken init states in CI pipelines
  • The random provider is widely used in production for generating unique suffixes, passwords, and pet names for resource naming conventions
  • Re-running terraform init after adding a provider is a mandatory step that is easy to forget and causes confusing errors

Key Concepts

Concept Description
Multiple providers A single main.tf can contain resource blocks from different providers
random provider Logical provider for generating random values — IDs, integers, passwords, pet names, UUIDs
random_pet Resource type that generates a random human-readable pet name
Re-init required Every time a new provider is added, terraform init must be re-run
Plugin reuse terraform init reuses already-installed provider plugins and only downloads new ones
Logical provider A provider with no real remote API — resources exist only in Terraform state (e.g. random, local)

The random Provider — Resource Types

Resource Description
random_id Generates a random byte string encoded in various formats
random_integer Generates a random integer within a given range
random_password Generates a random password string
random_pet Generates a random pet name (e.g. Mrs.hen)
random_shuffle Returns a randomised list from an input list
random_string Generates a random alphanumeric string
random_uuid Generates a random UUID

random_pet Argument Reference

Argument Required Description
length Optional Number of words in the generated pet name (default: 2)
prefix Optional String prepended to the generated name
separator Optional Character placed between prefix and name (default: -)
keepers Optional Arbitrary map of values — changing any value forces a new resource

Example Configuration & Commands

main.tf — two providers, two resources

# local provider resource (previously created)
resource "local_file" "pet" {
  filename = "/root/pets.txt"
  content  = "We love pets!"
}

# random provider resource
resource "random_pet" "my-pet" {
  prefix    = "Mrs"
  separator = "."
  length    = 1
}

Initialise after adding the new provider

# Must be re-run whenever a new provider is added to the configuration
terraform init

Expected output:

Initializing provider plugins...
- Using previously-installed hashicorp/local v2.0.0
- Finding latest version of hashicorp/random...
- Installing hashicorp/random v2.3.0...
- Installed hashicorp/random v2.3.0 (signed by HashiCorp)

* hashicorp/local:  version = "~> 2.0.0"
* hashicorp/random: version = "~> 2.3.0"

Terraform has been successfully initialized!

Plan and apply

terraform plan
terraform apply

Expected apply output (excerpt):

# random_pet.my-pet will be created
+ resource "random_pet" "my-pet" {
    + id        = (known after apply)
    + length    = 1
    + prefix    = "Mrs"
    + separator = "."
  }

Plan: 1 to add, 0 to change, 0 to destroy.

random_pet.my-pet: Creating...
random_pet.my-pet: Creation complete after 0s [id=Mrs.hen]

Note

The local_file.pet resource is unchanged and will show 0 to change — Terraform only modifies what has drifted or is new.

terraform {
  required_providers {
    local = {
      source  = "hashicorp/local"
      version = "~> 2.0"
    }
    random = {
      source  = "hashicorp/random"
      version = "~> 3.0"
    }
  }
}

Best Practices

Best Practices

  • Always re-run terraform init after adding any new provider block — do not skip this step even in local development.
  • Pin version constraints for every provider in required_providers, including utility providers like random.
  • Use random_pet or random_id to generate unique name suffixes for resources that require globally unique names (S3 buckets, storage accounts, etc.).
  • Use keepers in random_* resources to tie regeneration to a meaningful lifecycle event rather than allowing unbounded drift.
  • Commit .terraform.lock.hcl after adding a new provider so all team members and CI use the same plugin version.

Security Best Practices

Security

  • Do not use random_password output values in plaintext logs or outputs — mark them sensitive = true.
  • random_* resources store their generated values in Terraform state. Ensure state is encrypted at rest (use remote backends with encryption enabled).
  • Avoid using random_integer for security-sensitive purposes — it is not cryptographically secure. Use random_id or random_password with sufficient length instead.

Do and Don't

✅ Do ❌ Don't
Re-run terraform init every time a provider is added Run terraform plan or apply before init after adding a provider
Declare all providers in required_providers with version pins Rely on auto-detected latest versions in multi-provider configs
Use keepers to control when random values are regenerated Let random_* resources regenerate on every apply unintentionally
Store state in an encrypted remote backend Store state locally when random_password values are in use

Common Mistakes

Common Mistakes

  • Forgetting terraform init after adding a provider: Terraform will error with provider not installed on the next plan or apply.
  • Misreading the resource type prefix: In random_pet, the provider is random and the type is pet — the underscore separates them. This is how Terraform maps resources to providers.
  • Assuming unchanged resources are re-applied: Terraform only modifies resources that have changed. Existing resources like local_file.pet are untouched when a new random_pet is added.
  • Not pinning random provider versions: Minor version bumps in random can change generated output formats and break downstream references.

Troubleshooting

# Re-initialise to install a newly added provider
terraform init

# Check which providers are now installed
terraform providers

# Upgrade a specific provider to the latest allowed version
terraform init -upgrade

# Inspect the lock file to confirm the new provider is pinned
cat .terraform.lock.hcl

# Show the current state to find the generated random value
terraform show

Quick Recap

  • A single main.tf can contain resource blocks from multiple providers — they are all applied together
  • The provider name is the prefix before the underscore in a resource type: random_pet → provider random, type pet
  • terraform init must be re-run whenever a new provider is added; previously installed providers are reused
  • random is a logical provider — resources exist only in state, with no remote API calls
  • random_pet generates human-readable names; prefix, separator, and length control the output format

Interview / Revision Notes

Revision

  • Can a single Terraform config use multiple providers? Yes — add resource blocks from each provider and run terraform init to install all plugins.
  • What must you do after adding a new provider to an existing config? Run terraform init to download the new provider plugin.
  • How does Terraform know which provider a resource belongs to? The resource type prefix (before the first underscore) maps to the provider name.
  • What is a logical provider? A provider with no real remote API — e.g. random and local. Resources are managed in Terraform state only.
  • Will existing resources be modified when a new resource is added? No — Terraform only creates, updates, or destroys resources that have changed or are new.
  • What is the keepers argument in random_* resources? A map of arbitrary values; changing any value triggers regeneration of the random output.