Fix Terraform "Duplicate provider configuration" Error When Two Provider Blocks Lack an Alias

beginner๐Ÿ—๏ธ Terraform2026-07-17| Terraform CLI v1.x, any OS (Linux / macOS / Windows), HCL provider blocks in multi-region or multi-account setups

Error Message

Error: Duplicate provider "aws" configuration A module may have only one "default" (non-aliased) provider configuration per provider type.
#terraform#provider#alias#configuration#hcl

The Error Scenario

You run terraform plan โ€” or even just terraform validate โ€” and Terraform stops cold with this message:

Error: Duplicate provider "aws" configuration

A module may have only one "default" (non-aliased) provider configuration per provider type.

Nothing gets planned. Nothing gets applied. The config you thought was fine just refused to load.

This usually happens in one of three situations:

  • You copy-pasted a provider block from another file into a module that already had one
  • You split your Terraform config across multiple .tf files and defined the same provider in two of them
  • You added a second AWS region config without realizing a default provider block already existed in main.tf

Why Terraform Throws This Error

Terraform allows only one default provider block per provider type within a single module. A default provider is any provider block that does not have an alias argument set. When Terraform finds two blocks like this in the same module โ€” regardless of which .tf file they're in โ€” it can't determine which one to use as the default, so it errors out rather than guessing.

Here is the minimal example that reproduces the error:

# main.tf
provider "aws" {
  region = "us-east-1"
}

# providers.tf โ€” same module, different file
provider "aws" {
  region = "ap-southeast-1"
}

Terraform merges all .tf files in a directory into one logical module. Both blocks above become default providers for aws. That's the conflict.

Quick Fix: Add an Alias to the Second Provider

If you actually need two different AWS configurations (two regions, two accounts, two sets of credentials), the fix is to keep one block as the default and give the other an alias.

# providers.tf
provider "aws" {
  region = "us-east-1"   # default provider โ€” no alias
}

provider "aws" {
  alias  = "ap"
  region = "ap-southeast-1"
}

Any resource that needs the second configuration references it explicitly with the provider argument:

resource "aws_s3_bucket" "asia_logs" {
  provider = aws.ap
  bucket   = "my-asia-logs-bucket"
}

Resources without a provider argument automatically use the default (non-aliased) block. You only need to specify it when you want the aliased one.

Permanent Fix: Remove the Duplicate

If you only need one AWS configuration, the real fix is to delete the duplicate block entirely. Start by finding every occurrence:

grep -rn 'provider "aws"' .

Sample output might look like:

./main.tf:1:provider "aws" {
./providers.tf:1:provider "aws" {

Open both files, decide which one has the correct settings, and delete the other. Then consolidate everything into a single, authoritative provider block โ€” the conventional place is providers.tf or versions.tf:

# providers.tf
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

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

Special Case: Child Modules

If you're calling a child module and want it to use a specific provider configuration, pass the provider from the root using a providers map. The child module should not define its own provider "aws" {} block โ€” it should only declare what it requires.

# Root module โ€” calls child and passes a provider
module "networking" {
  source = "./modules/networking"

  providers = {
    aws = aws.ap   # pass the aliased provider explicitly
  }
}

# modules/networking/versions.tf โ€” declare requirement only, no full provider block
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

Adding a provider "aws" { region = ... } block inside the child module would create a second default provider from Terraform's perspective โ€” the same error, just harder to spot because it's in a subdirectory.

Verification Steps

Run these three commands in order after making your changes:

# 1. Validate configuration syntax and structure
terraform validate

# 2. Inspect the provider dependency tree
terraform providers

# 3. Confirm the plan runs cleanly
terraform plan

terraform validate should output:

Success! The configuration is valid.

terraform providers is especially useful for diagnosing where a provider is being pulled in from. It shows the full tree including child modules, so if a duplicate is hiding in a nested module, you'll see it here.

Prevention Tips

The single most effective habit: keep all provider and version declarations in one file (providers.tf or versions.tf), and never put a provider block anywhere else. When the whole team knows providers live in one file, accidental duplication drops to near zero.

Add a validation step to your CI pipeline so this error gets caught before it reaches anyone's local machine:

# .github/workflows/terraform.yml (excerpt)
- name: Validate Terraform
  run: |
    terraform init -backend=false
    terraform validate

When you're working with configurations that involve variable files mixing YAML and JSON formats โ€” which is common in larger Terraform projects โ€” I find ToolCraft's YAML โ†” JSON Converter handy for quickly verifying the structure of .tfvars or variable definition files before feeding them into Terraform. It runs entirely in the browser, nothing gets uploaded.

Related Error Notes