Terraform Providers: Configuration and Aliases

Tested on Ubuntu 26.04 LTS (Resolute Raccoon)
Package terraform 1.15.8-1
hashicorp/local 2.9.0
hashicorp/random 3.9.0
kreuzwerker/docker 3.9.0
Applies to Any host with Terraform installed
Lab environment Single Ubuntu VM with Terraform and Docker — Terraform lab environment on Ubuntu
Privilege Normal user; sudo only if Terraform or Docker is not installed yet
Scope Terraform provider plugins — source addresses, required_providers, provider blocks, installation with terraform init, terraform providers, default and aliased configurations, multiple providers, conceptual authentication, and local/Docker verification. Does not cover provider version locking in depth, init flags, module provider mapping, or cloud credential walkthroughs.
Related guides Terraform HCL syntax
Install Terraform on Ubuntu
Terraform lab environment on Ubuntu
Terraform Associate certification course

Terraform providers are plugins between Terraform Core and external systems. The configuration below declares the local provider, installs it with terraform init, and manages a file on disk — no cloud account required:

hcl
terraform {
  required_providers {
    local = {
      source  = "hashicorp/local"
      version = "~> 2.5"
    }
  }
}

provider "local" {}

resource "local_file" "example" {
  filename = "${path.module}/example.txt"
  content  = "Managed by Terraform"
}

The sections below explain what providers do, how installation differs from configuration, and how aliases let you run multiple provider configurations in one root module.

NOTE
Complete install Terraform on Ubuntu and the Terraform lab environment first. Examples use ~/terraform-labs/providers so they stay separate from other lessons.

How Terraform providers fit the workflow

Terraform Core reads your .tf files and state, but it does not natively know how to manage Docker containers, AWS EC2 instances, or GitHub repositories. A provider plugin implements the resource and data source types for one API.

text
Terraform configuration
Terraform Core
Provider plugin
External API or resource

For local_file, the plugin writes to your filesystem. For docker_container, it calls the Docker Engine API. For aws_instance, it calls AWS — this guide keeps cloud providers conceptual and practices with hashicorp/local and hashicorp/random.


What is a Terraform provider?

A provider is a compiled plugin published on the Terraform Registry (or loaded from a local mirror). Each provider adds resource types such as local_file, docker_image, or aws_s3_bucket, and data sources that read existing objects.

Common registry addresses you will see in documentation and exams:

Source address Manages
hashicorp/local Files and directories on the machine running Terraform
kreuzwerker/docker Docker Engine API (containers, images, networks)
hashicorp/random Random values useful in tests and naming
hashicorp/aws Amazon Web Services (conceptual in this guide)
hashicorp/azurerm Microsoft Azure (conceptual)
hashicorp/google Google Cloud Platform (conceptual)

You declare which providers a module needs, run terraform init to download plugins, then reference provider-managed resources in resource blocks.


Declare a provider with required_providers

The terraform block lists provider dependencies for the current module. The local name (local) is what you use in provider "local" blocks and in provider = local.secondary meta-arguments. The source is the global registry address.

Create the lab directory:

bash
mkdir -p ~/terraform-labs/providers

Move into it — the remaining examples assume this path:

bash
cd ~/terraform-labs/providers

Create versions.tf with the provider requirement:

bash
cat > versions.tf <<'EOF'
terraform {
  required_providers {
    local = {
      source  = "hashicorp/local"
      version = "~> 2.5"
    }
  }
}
EOF

The three fields work together:

Field Example Role
Local name local Identifier inside this module
Source address hashicorp/local Registry namespace and provider type
Version constraint ~> 2.5 Acceptable plugin versions for terraform init

Version constraint syntax and .terraform.lock.hcl behavior belong in a dedicated provider versioning guide — this article only shows enough constraint to install a current plugin. After you change required_providers, run terraform init again so Terraform can download new plugins.


Configure a provider

The provider block configures a provider instance Terraform passes to resources. The local provider needs no arguments on Ubuntu:

bash
cat > providers.tf <<'EOF'
provider "local" {}
EOF

Other providers require settings before they can reach an API. A Docker provider might only need the default local socket; an AWS provider would expect a region and credentials through environment variables or shared config files — not hard-coded secrets in .tf files.

Add the resource from the introduction:

bash
cat > main.tf <<'EOF'
resource "local_file" "example" {
  filename = "${path.module}/example.txt"
  content  = "Managed by Terraform"
}
EOF

provider "local" {} is the default configuration for the local provider type. Resources that omit provider = … use this unaliased block.


Install a Terraform provider

required_providers declares the dependency; terraform init downloads the plugin binaries.

Run initialization in the lab directory:

bash
terraform init
output
Initializing provider plugins...
- Finding hashicorp/local versions matching "~> 2.5"...
- Installing hashicorp/local v2.9.0...
- Installed hashicorp/local v2.9.0 (signed by HashiCorp)

Terraform has been successfully initialized!

Terraform may also create .terraform.lock.hcl on first init. Treat that file as a record of selected provider versions — the dedicated lock-file guide explains when to commit it and how upgrades work. This article focuses on installation and configuration, not lock-file operations.

List what the configuration requires:

bash
terraform providers
output
Providers required by configuration:
.
└── provider[registry.terraform.io/hashicorp/local] ~> 2.5

Three concepts beginners often merge:

Mechanism Purpose
required_providers Declares which plugins the module needs and acceptable versions
provider block Configures a provider instance (region, endpoint, credentials path)
terraform init Downloads plugins into .terraform/providers/
terraform providers Prints the provider dependency tree for the working directory

Where Terraform stores provider plugins

Plugins live under .terraform/providers/ after terraform init. Inspect the directory layout without editing files inside it — Terraform manages that tree.

bash
find .terraform -maxdepth 4 -type d
output
.terraform
.terraform/providers
.terraform/providers/registry.terraform.io
.terraform/providers/registry.terraform.io/hashicorp
.terraform/providers/registry.terraform.io/hashicorp/local

On this host the selected version directory is 2.9.0/. Do not hand-edit or delete individual plugin binaries unless you are deliberately resetting the working directory; use terraform init when provider requirements change.


Use a provider with a resource

Apply the configuration so Terraform creates the managed file:

bash
terraform apply -auto-approve
output
local_file.example: Creating...
local_file.example: Creation complete after 0s [id=837c55ffe364651b77f5c4ba046777d02d10e2dc]

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

Confirm Terraform wrote the expected content on disk:

bash
cat example.txt
output
Managed by Terraform

The provider plugin performed the write. terraform destroy would remove the resource through the same plugin path.


Configure multiple Terraform providers

Multiple providers can mean two different things:

  1. Different provider types in one module — for example local and random together.
  2. Multiple configurations of the same type — handled with provider aliases below.

Create a separate directory for two provider types:

bash
mkdir -p ~/terraform-labs/providers/multi

Switch into the multi-provider directory:

bash
cd ~/terraform-labs/providers/multi

Write a configuration that uses both hashicorp/local and hashicorp/random:

bash
cat > main.tf <<'EOF'
terraform {
  required_providers {
    local = {
      source  = "hashicorp/local"
      version = "~> 2.5"
    }
    random = {
      source  = "hashicorp/random"
      version = "~> 3.6"
    }
  }
}

provider "local" {}
provider "random" {}

resource "random_id" "suffix" {
  byte_length = 2
}

resource "local_file" "tagged" {
  filename = "${path.module}/id-${random_id.suffix.hex}.txt"
  content  = "suffix=${random_id.suffix.hex}"
}
EOF

Initialize and inspect the provider tree:

bash
terraform init

Print the provider requirement tree for both plugins:

bash
terraform providers
output
Providers required by configuration:
.
├── provider[registry.terraform.io/hashicorp/local] ~> 2.5
└── provider[registry.terraform.io/hashicorp/random] ~> 3.6

Each top-level provider block configures the default instance for its type. The random_id resource uses the random provider implicitly; local_file uses local.


Terraform provider aliases

An alias labels a non-default provider configuration. Use aliases when you need two regions, two Docker hosts, or two accounts of the same provider type.

Create an aliases exercise directory:

bash
mkdir -p ~/terraform-labs/providers/aliases

Change into that directory:

bash
cd ~/terraform-labs/providers/aliases

Save the aliased provider configuration:

bash
cat > main.tf <<'EOF'
terraform {
  required_providers {
    local = {
      source  = "hashicorp/local"
      version = "~> 2.5"
    }
  }
}

provider "local" {}

provider "local" {
  alias = "secondary"
}

resource "local_file" "default_cfg" {
  filename = "${path.module}/default.txt"
  content  = "default provider config"
}

resource "local_file" "aliased_cfg" {
  provider = local.secondary
  filename = "${path.module}/aliased.txt"
  content  = "secondary alias config"
}
EOF

Initialize and apply:

bash
terraform init

Apply so both default and aliased files are created:

bash
terraform apply -auto-approve
output
local_file.aliased_cfg: Creation complete after 0s
local_file.default_cfg: Creation complete after 0s

Apply complete! Resources: 2 added, 0 changed, 0 destroyed.

List both files on disk:

bash
ls -l default.txt aliased.txt

Both files appear because default_cfg uses the unaliased provider "local" {} and aliased_cfg selects provider = local.secondary.

Rules to remember:

  • The unaliased provider "local" {} block is the default configuration.
  • alias = "secondary" creates local.secondary for explicit selection.
  • Resources use provider = <local-name>.<alias> when they should not use the default.

When every provider block uses an alias

When every provider block for a type carries an alias, Terraform still creates an implied empty default configuration for that provider. Resources that omit the provider meta-argument bind to that implied default—not to any aliased block.

This configuration declares only an aliased local provider:

hcl
provider "local" {
  alias = "secondary"
}

resource "local_file" "x" {
  filename = "${path.module}/x.txt"
  content  = "x"
}

local_file.x uses the implied empty default local configuration. That works here because the local provider has no mandatory configuration arguments. Whether the implied empty default works depends on the provider. Providers that require configuration not available from defaults or the environment can fail when resources use that empty configuration.

Aliased blocks such as local.secondary remain available for resources that set provider = local.secondary. When you refactor provider blocks, watch state: removing a configuration that existing resources still reference produces the Provider configuration not present error covered in troubleshooting.


Provider configuration and modules

Child modules automatically inherit default (unaliased) provider configurations from their parent. Aliased configurations are not inherited automatically; the child declares them with configuration_aliases, and the caller passes the matching configurations through the providers argument.

Module provider inheritance and the providers meta-argument belong in the modules guide. For now, remember that required_providers in a child module declares dependencies, while the root module supplies configured provider instances.


Provider authentication

Authentication is provider-specific. Terraform does not centralize cloud logins in one block.

Typical patterns:

  • Environment variables — many providers read AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, ARM_*, or GOOGLE_CREDENTIALS.
  • Shared credential files — AWS CLI profiles, Azure CLI, or ~/.docker/config.json.
  • Workload identity — IAM roles for EC2, managed identities on Azure, or GKE workload identity on Google Cloud.
  • Local sockets — the Docker provider talks to /var/run/docker.sock by default on Linux.

Do not paste secrets into .tf files to make examples easy to copy. Use environment variables or credential files the provider documents, and keep secret values out of version control.


Provider source address vs local name

The registry address and the module-local name are different on purpose. This pattern scales when the same module uses multiple sources or aliases:

hcl
terraform {
  required_providers {
    random = {
      source  = "hashicorp/random"
      version = "~> 3.6"
    }
  }
}
Symbol Value Meaning
Global source hashicorp/random Registry namespace and provider type
Local name random Key used in provider "random" and random.secondary

A module could map random = { source = "hashicorp/random" } while resources still call provider = random.replica. The local name is arbitrary but must stay consistent inside the module.


View providers used by a configuration

terraform providers shows the dependency tree for the current working directory. From the multi-provider example:

bash
cd ~/terraform-labs/providers/multi

Re-run the providers report from that directory:

bash
terraform providers
output
Providers required by configuration:
.
├── provider[registry.terraform.io/hashicorp/local] ~> 2.5
└── provider[registry.terraform.io/hashicorp/random] ~> 3.6

When modules are nested, the output indents child modules under their parents. Use it after refactors to confirm the root module still pulls the providers you expect.


Optional Docker provider example

When Docker Engine is installed (as in the Terraform lab environment), the kreuzwerker/docker provider talks to the local daemon:

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

provider "docker" {}

resource "docker_image" "hello" {
  name = "hello-world:latest"
}

terraform init installs kreuzwerker/docker the same way as hashicorp/local. Image pull and container lifecycle are covered in resource-focused lessons — here the point is that any registry provider follows the same declare → init → configure → resource pattern.


Common Terraform provider errors

Symptom Likely cause Fix
Failed to query available provider packages Wrong source address or typo in namespace Verify the address on the registry; fix required_providers
Missing required provider / Required plugins are not installed terraform init not run, or .terraform/ removed Run terraform init in the working directory
Inconsistent dependency lock file Config changed without re-init Run terraform init after editing required_providers
Provider configuration not present State contains resources tied to a provider configuration or alias that was later removed from the configuration Restore that provider configuration long enough to destroy or migrate the affected resources
Unsupported argument Unsupported argument in provider block Compare with the provider documentation

Failed to query available provider packages

Use a deliberately invalid source such as hashicorp/not-a-real-provider, then initialize:

bash
terraform init
output
Error: Failed to query available provider packages

Could not retrieve the list of available versions for provider
hashicorp/not-a-real-provider: provider registry registry.terraform.io does
not have a provider named registry.terraform.io/hashicorp/not-a-real-provider

Fix the source string in required_providers to match a real registry address such as hashicorp/local.

Provider not installed

If the configuration has not been initialized, or .terraform/ was removed, run terraform init before terraform validate or terraform plan. Validation and planning expect required provider plugins to already be installed — HashiCorp defines terraform init as the command that downloads those plugins from required_providers.

From ~/terraform-labs/providers with a valid configuration but no plugin cache, remove the local plugin directory:

bash
cd ~/terraform-labs/providers

Delete .terraform/ to simulate a fresh clone or manual cache removal:

bash
rm -rf .terraform

Re-run initialization so Terraform reinstalls the required plugins:

bash
terraform init
output
Initializing provider plugins...
- Finding hashicorp/local versions matching "~> 2.5"...
- Installing hashicorp/local v2.9.0...
- Installed hashicorp/local v2.9.0 (signed by HashiCorp)

Terraform has been successfully initialized!

The Installing lines confirm Terraform repopulated .terraform/providers/. Run terraform init again whenever you clone a configuration, delete .terraform/, or change required_providers.

Provider configuration not present

This error appears when state still references a provider configuration you removed from .tf files. A common path: apply a resource with provider = local.secondary, delete the provider "local" { alias = "secondary" } block, then run a later Terraform operation that needs to work with that state. terraform plan surfaces it:

bash
terraform plan
output
Error: Provider configuration not present

To work with local_file.z its original provider configuration at
provider["registry.terraform.io/hashicorp/local"].secondary is required, but
it has been removed. This occurs when a provider configuration is removed
while objects created by that provider still exist in the state. Re-add the
provider configuration to destroy local_file.z, after which you can remove
the provider configuration again.

Terraform needs the original provider configuration to operate on those state objects. Re-add the missing provider block (or the old alias name), run terraform destroy or migrate state, then remove the configuration once nothing in state depends on it.


Provider vs resource vs data source

Layer Role Example
Provider API integration and configuration provider "local" {}
Resource Managed object Terraform creates, updates, and destroys resource "local_file" "example" { … }
Data source Read-only lookup of existing objects data "local_file" "readme" { … }

Providers enable resources and data sources. Resource and data source syntax are covered in their own articles — this guide stops at how plugins are declared, installed, and selected.


References


Summary

You declared Terraform providers with required_providers, configured default instances with provider blocks, and installed plugins through terraform init into .terraform/providers/.

The hashicorp/local walkthrough connected provider configuration to a real local_file on disk. The local plus random example showed multiple provider types in one module.

Provider aliases let you name alternate configurations and select them with provider = local.secondary:

  • Resources without an explicit provider argument use the default or implied empty configuration for that type
  • Removing a provider block that state still references triggers Provider configuration not present until you destroy or migrate those resources

Authentication stays provider-specific — use environment variables and credential files rather than secrets in .tf files.

When you are ready to constrain provider versions and understand .terraform.lock.hcl in depth, continue with the provider version guide. For initialization flags and backend setup, see the dedicated terraform init lesson.

Next, practice resource lifecycle details once providers are installed, or return to the Terraform lab environment if Docker is not available yet.


Frequently Asked Questions

1. What is a Terraform provider?

A provider is a plugin that teaches Terraform how to talk to an API — the local filesystem, Docker, AWS, Azure, and so on. You declare the provider in required_providers, configure it with a provider block, and Terraform downloads the plugin during terraform init.

2. What is the difference between required_providers and a provider block?

required_providers in the terraform block declares which provider plugins the module needs and their source address. The provider block configures a specific provider instance — endpoints, region, or other settings. terraform init installs the plugin; the provider block supplies runtime settings.

3. What is a Terraform provider alias?

An alias creates a second configuration of the same provider type, such as two Docker hosts or two AWS regions. Resources select the configuration with provider = docker.secondary. Keep one unaliased provider block when resources should use the default configuration.

4. How does Terraform install providers?

Run terraform init in the working directory. Terraform reads required_providers, downloads matching plugins from the registry into .terraform/providers/, and records selections in .terraform.lock.hcl. Use terraform providers to inspect what the configuration requires.

5. What is the difference between a provider source address and the local provider name?

The source address is the global registry path such as hashicorp/local or hashicorp/random. The local name is the key you choose in required_providers and provider blocks — often local or random — used in resource provider meta-arguments like provider = random.secondary.

6. Do I need AWS credentials to learn Terraform providers?

No. This guide uses hashicorp/local and hashicorp/random on the filesystem, with an optional kreuzwerker/docker example when Docker is available. Cloud provider authentication patterns are mentioned conceptually only.
Deepak Prasad

R&D Engineer

Founder of GoLinuxCloud with more than 15 years of expertise in Linux, Python, Go, Laravel, DevOps, Kubernetes, Git, Shell scripting, OpenShift, AWS, Networking, and Security. With extensive experience, he excels across development, DevOps, networking, and security, delivering robust and efficient solutions for diverse projects.

  • Go (programming language)
  • Python (programming language)
  • DevOps
  • Computer Security
  • Cloud Computing
  • Kubernetes
  • Linux
  • Ansible (software)