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
initstates in CI pipelines - The
randomprovider is widely used in production for generating unique suffixes, passwords, and pet names for resource naming conventions - Re-running
terraform initafter 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
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
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.
Declare both providers with version constraints (recommended)
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 initafter 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 likerandom. - Use
random_petorrandom_idto generate unique name suffixes for resources that require globally unique names (S3 buckets, storage accounts, etc.). - Use
keepersinrandom_*resources to tie regeneration to a meaningful lifecycle event rather than allowing unbounded drift. - Commit
.terraform.lock.hclafter adding a new provider so all team members and CI use the same plugin version.
Security Best Practices
Security
- Do not use
random_passwordoutput values in plaintext logs or outputs — mark themsensitive = 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_integerfor security-sensitive purposes — it is not cryptographically secure. Userandom_idorrandom_passwordwith 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 initafter adding a provider: Terraform will error withprovider not installedon the nextplanorapply. - Misreading the resource type prefix: In
random_pet, the provider israndomand the type ispet— 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.petare untouched when a newrandom_petis added. - Not pinning
randomprovider versions: Minor version bumps inrandomcan 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.tfcan 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→ providerrandom, typepet terraform initmust be re-run whenever a new provider is added; previously installed providers are reusedrandomis a logical provider — resources exist only in state, with no remote API callsrandom_petgenerates human-readable names;prefix,separator, andlengthcontrol 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 initto install all plugins. - What must you do after adding a new provider to an existing config? Run
terraform initto 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.
randomandlocal. 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
keepersargument inrandom_*resources? A map of arbitrary values; changing any value triggers regeneration of the random output.