Skip to content
DevOps AI ToolKit
Newsletter
All guides
AI for Terraform By James Joyner IV · · 9 min read

Terraform Error Guide: 'Provider configuration not present' (removed or aliased provider during destroy/refactor)

Fix Terraform's 'Provider configuration not present' error caused by removed or aliased providers during refactors and module deletions.

  • #terraform
  • #troubleshooting
  • #errors
  • #providers

Exact Error Message

When Terraform plans or applies a change that touches a resource whose original provider has vanished, you get an error like this:

Error: Provider configuration not present

To work with module.app.aws_instance.web its original provider
configuration at module.app.provider["registry.terraform.io/hashicorp/aws"].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. To make changes to these resources, the provider
configuration must be present in the destroy operation. Re-add the
provider configuration to destroy module.app.aws_instance.web, after
which you can remove the provider configuration again.

The key signal is the provider reference ending in an alias name (here .secondary) and the phrase “its original provider configuration … is required, but it has been removed.”

What the Error Means

Every resource Terraform tracks in state is bound to a specific provider configuration, not just a provider type. When you write provider "aws" { alias = "secondary" }, you create a distinct configuration that resources opt into via provider = aws.secondary. Terraform records that binding in the state file.

If you later delete that provider block (or the module that declared it) while resources created by it still live in state, Terraform can no longer talk to those resources. It needs the configuration’s credentials, region, and endpoints to destroy or modify them, and that information is gone. Terraform refuses to guess, so it stops with “Provider configuration not present.”

Critically, this is a state-versus-config mismatch. The resources are real and still exist in your cloud account; only the configuration that manages them disappeared. Terraform is protecting you from orphaning real infrastructure.

Common Causes

  • Removing an aliased provider block that resources still reference. The default (unaliased) provider is usually fine, but aliases are explicit and easy to forget.
  • Deleting a module that declared its own provider. Child-module provider blocks are an anti-pattern precisely because they cause this on removal.
  • Refactoring with moved blocks or terraform state mv where the destination resource’s provider binding differs from the source.
  • Splitting a configuration into multiple root modules and pulling state across without carrying the provider definitions.
  • Renaming an alias (for example secondary to replica) without updating the resources still pinned to the old name in state.
  • Running a partial destroy with -target that removes a provider’s resources, leaving dangling state entries.

How to Reproduce the Error

Start with a working configuration that uses a second aliased AWS provider:

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

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

resource "aws_instance" "web" {
  provider      = aws.secondary
  ami           = "ami-0abcdef1234567890"
  instance_type = "t3.micro"
}

Run terraform apply so the instance is created and tracked in state. Now delete the provider "aws" { alias = "secondary" } block and the provider = aws.secondary line, or remove the whole module that owned them:

# secondary provider block deleted
resource "aws_instance" "web" {
  ami           = "ami-0abcdef1234567890"
  instance_type = "t3.micro"
}

On the next terraform plan, Terraform compares state (which says aws_instance.web belongs to aws.secondary) against config (where that provider no longer exists) and raises the error.

Diagnostic Commands

Use read-only commands to confirm which resources are bound to the missing provider before changing anything.

List everything in state, then inspect the suspect resource:

terraform state list
terraform state show aws_instance.web

The state show output includes the provider binding. Look for the aliased provider:

# aws_instance.web:
resource "aws_instance" "web" {
    ami           = "ami-0abcdef1234567890"
    instance_type = "t3.micro"
    # provider = provider["registry.terraform.io/hashicorp/aws"].secondary
}

See which provider configurations Terraform currently knows about, and validate the config:

terraform providers
terraform validate

terraform providers prints the provider tree from your configuration. If .secondary appears in the error but not in this tree, you have confirmed the mismatch. Run a plan to capture the exact failing addresses:

terraform plan

Grep your code for any lingering references and for the alias declaration:

grep -rn "aws.secondary" .
grep -rn 'alias *= *"secondary"' .

Confirm the underlying resource really exists in the cloud so you know whether to keep or destroy it:

aws ec2 describe-instances --region us-west-2 \
  --filters "Name=image-id,Values=ami-0abcdef1234567890"

Step-by-Step Resolution

There are two valid outcomes: keep the resource (re-attach a provider) or remove it cleanly (let the destroy run with the provider temporarily restored).

1. Decide whether you still want the resource. Use the diagnostic output above. If the instance should survive, you will rebind it to a provider. If it should go away, you will destroy it with the provider present.

2. Re-add the provider configuration that the error names. Paste the alias block back exactly as it was, matching region and credentials:

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

3a. To keep the resource, also restore the provider = aws.secondary line on the resource (or use a moved block if you are migrating it to a differently configured provider). Then run a plan to confirm Terraform shows no destroy:

terraform plan

If the plan is clean (no changes, or only the intended changes), apply it:

terraform apply

The resource is now correctly bound again and the error is gone.

3b. To remove the resource, keep the provider block in place and let Terraform destroy the object while it still has working credentials:

terraform apply

With the resource block deleted but the provider block present, the plan shows a destroy for aws_instance.web. Once that destroy completes, the resource no longer exists in state, and you can safely delete the now-unused provider block on a subsequent edit.

4. Remove the orphaned provider only after state is clean. Once no resources reference aws.secondary, delete the block and run terraform validate plus terraform plan one more time to confirm a stable, empty diff.

If you are migrating providers rather than deleting, prefer a moved block over manual state surgery:

moved {
  from = aws_instance.web
  to   = aws_instance.web
}

For incident playbooks around failed applies and stuck state, our incident response tooling can capture the failing plan and suggest the safest next step.

Prevention and Best Practices

  • Never declare provider blocks inside child modules. Pass providers in from the root with the providers = { aws = aws.secondary } argument. This keeps provider lifecycles independent of module removal.
  • Destroy before deleting. When retiring a module or aliased provider, run a targeted terraform apply that destroys its resources first, then remove the provider block in a separate, later commit.
  • Treat alias renames as two-step migrations. Add the new alias, move resources onto it (via config plus moved), apply, then delete the old alias.
  • Keep provider definitions in a dedicated providers.tf so they are easy to see and less likely to be deleted accidentally during refactors.
  • Run terraform plan in CI on every PR. A surprise destroy or a missing-provider error surfaces in review instead of during a production apply.
  • Pin and review state before structural changes. Snapshot the state file before large refactors so you can recover bindings if something goes wrong.

You can find more Terraform troubleshooting guides in our Terraform category.

These differ from “Provider configuration not present”: the install error is about the plugin binary being unavailable, the configuring error is about bad arguments, while this error is about a configuration block that existed and was deleted while state still depends on it.

Frequently Asked Questions

Why can’t Terraform just destroy the resource without the provider?

Because destroying a real cloud resource requires live credentials, a region, and an API endpoint, all of which come from the provider configuration. Without that block, Terraform has no way to authenticate or know where the resource lives, so it refuses rather than silently orphaning infrastructure.

Can I fix this with terraform state rm?

You can, but it is dangerous. terraform state rm aws_instance.web removes the resource from state without touching the real cloud object, leaving an orphaned, unmanaged instance you will keep paying for. Only use it when you have independently confirmed the resource is already gone or you intend to manage it elsewhere. The safe path is restoring the provider block.

The provider block is in a module I deleted. What now?

Re-add the module (or just its provider block, refactored to the root) temporarily. Run the destroy with the provider present, then remove the module again once state is clean. Going forward, define providers at the root and pass them in to avoid this.

Does this affect terraform import?

Yes. Importing a resource that references an aliased provider requires that provider configuration to be present and correctly defined. Add the alias block before running the import, and specify the provider on the imported resource.

How do I find every resource bound to the missing provider?

Run terraform state list to enumerate resources, then terraform state show <address> on each suspect and look at the commented # provider = line. You can also read terraform plan output, which names each address it cannot reconcile, and grep -rn your code for the alias to confirm config-side references.

Free download · 368-page PDF

Download the Free 500-Prompt DevOps AI Toolkit

500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.

  • 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
  • Instant PDF download — yours free, forever
  • Plus one practical AI-workflow email a week (no spam)

Single opt-in · unsubscribe anytime · no spam.