Skip to main content
RunBook Academy

TerraformX · State Operations: Read, Move, Remove, ImportProduction Terraform

replace-provider: Switching Provider Namespaces

Advanced⏱ ~10 minbash

What you'll learn

  • Use terraform state replace-provider to migrate state between provider sources (e.g. hashicorp/aws to mycorp/aws)
  • Plan a provider migration with verification gates
  • Recognise when replace-provider is wrong (different provider version with different schema, true fork)
  • Recover from a failed replace-provider with the backup file

Prerequisites

None — start here.

Verified against Terraform CLI 1.9.x · OpenTofu 1.7.x · HCL 2.0 · bpg/proxmox provider 0.66+ · hashicorp/local provider 2.5+ · hashicorp/null provider 3.2+ · hashicorp/random provider 3.6+ · hashicorp/http provider 3.4+ · Ubuntu 24.04 LTS · Debian 12 (Bookworm) · 2026-08-13

Not yet marked complete on this device.

Provider sources in Terraform have a namespace (hashicorp, mycorp, cloudflare) and a name (aws, google, cloudflare). The combination is the source address: hashicorp/aws, mycorp/aws, cloudflare/cloudflare. When the source changes — a community provider moves to a vendor namespace, the team adopts an in-house fork, a deprecated provider is replaced — the state records the old source. Terraform needs to be told the source has changed; otherwise the next plan will either recreate every resource or fail with “provider not found”.

What replace-provider does

terraform state replace-provider rewrites the provider field in every resource instance in state from the old source to the new one. The real-world resources are untouched. The next plan sees the new provider source, finds state that matches, and proposes no changes.

Before:
   provider = "provider[\"registry.terraform.io/hashicorp/aws\"]"
   Real world: i-0a1b2c3d4e5f6a7b8 (running)

After:
   provider = "provider[\"registry.terraform.io/mycorp/aws\"]"
   Real world: i-0a1b2c3d4e5f6a7b8 (still running, now managed by mycorp/aws)

The state now points at the new provider. The real-world resource is unchanged. The next plan will succeed if the new provider exposes the same resource types and attributes as the old one.

When replace-provider is right

  • HashiCorp adopts a community provider. Example: the AWS provider was originally terraform-providers/aws and later moved to hashicorp/aws. State from older runs references the old source; the new run needs the new source.
  • The team adopts a vendor-maintained fork. Example: a cloud vendor publishes their own provider under their namespace (cloudflare/cloudflare instead of terraform-providers/cloudflare).
  • The team adopts an in-house fork. Example: a security patch is needed; the team forks the provider under mycorp/aws and switches.

When replace-provider is wrong

  • Schema divergence. If the new provider exposes different attributes than the old one, the plan will show a massive diff. The right answer is to evaluate the new provider against the old one, not to migrate wholesale.
  • Different cloud. A provider for AWS cannot be replaced with a provider for GCP — the resource types are completely different. replace-provider is not a cross-cloud migration tool.
  • Major version upgrade with breaking changes. A 4.x → 5.x provider upgrade with renamed attributes should be planned attribute by attribute, not bulk-replaced.

The procedure

# 1. Pull state to a backup
terraform state pull > /tmp/state-before-replace.json

# 2. Confirm the old source is in use
terraform state pull | jq -r '.resources[].provider' | sort -u
# provider["registry.terraform.io/hashicorp/aws"]

# 3. Add the new provider to the configuration
# terraform {
#   required_providers {
#     mycorp-aws = {
#       source = "mycorp/aws"
#       version = "~> 1.0"
#     }
#   }
# }
# provider "mycorp-aws" { alias = "..." }

# 4. terraform init
terraform init -upgrade
# Terraform downloads the new provider and locks the version.

# 5. Run replace-provider
terraform state replace-provider -backup-file=/tmp/replace-backup.tfstate \
  'registry.terraform.io/hashicorp/aws' \
  'registry.terraform.io/mycorp/aws'
# Terraform rewrites every resource instance to the new provider.

# 6. Plan; expect no changes (if the new provider is drop-in)
terraform plan
# No changes. Your infrastructure matches the configuration.
# (If the new provider differs: the plan shows the diff. Investigate.)

# 7. Apply
terraform apply

A real example: adopting a vendor fork

A team is migrating from hashicorp/aws to aws/aws (the vendor namespace for AWS itself, used for AWS-specific features not in the HashiCorp provider).

terraform {
  required_providers {
    aws = {
      source = "aws/aws"
      version = "~> 1.0"
    }
  }
}
terraform init -upgrade
terraform state replace-provider -backup-file=/tmp/replace.tfstate \
  'registry.terraform.io/hashicorp/aws' \
  'registry.terraform.io/aws/aws'

terraform plan
# Expect no changes if the resource types and attributes align.
# If the plan shows changes, the providers differ; investigate.

The backup file is the recovery artifact if the replace-provider causes unexpected diffs.

Validation

READ-ONLY

# Confirm the provider source has changed in state
terraform state pull | jq -r '.resources[].provider' | sort -u
# provider["registry.terraform.io/mycorp/aws"]

# Confirm the plan is empty (or contains only expected diffs)
terraform plan
# No changes. Your infrastructure matches the configuration.
# (or a reviewed diff)

# Confirm the dependency lock file reflects the new provider
cat .terraform.lock.hcl | grep 'mycorp/aws'

Production failure modes

Symptom: replace-provider errors with “provider not configured”. Cause: the new provider is not in required_providers or init has not been run. Add the provider to the configuration block and run terraform init -upgrade.

Symptom: replace-provider succeeds but the plan shows a massive diff. Cause: the new provider is not drop-in; attributes or defaults differ. Investigate the diff; consider rolling back via the backup file before applying.

Symptom: replace-provider succeeds but the apply fails with “provider returned error”. Cause: the new provider cannot manage the resource type (it was removed or renamed). Investigate the provider’s documentation; do not retry the apply blindly.

Symptom: lock acquire failed during replace-provider. Cause: another apply is in progress. Wait or break the lock with team agreement.

Symptom: serial advanced but the plan still references the old provider. Cause: replace-provider did not run; the error was silent. Investigate with state pull.

Recovery

  1. Stop. Do not apply.
  2. Restore state from the backup file (S3 versioning, Terraform Cloud state versions).
  3. Verify with terraform plan — expect the original provider source.
  4. Investigate the divergence between the old and new providers before retrying.

What comes next

The next lesson covers moved blocks — the declarative counterpart to state mv for refactoring resource addresses in code, reviewable in pull requests.

Verification

  • You can describe when state replace-provider is the right tool (community provider adoption, vendor migration, in-house fork).
  • You can run the procedure: backup, init, replace-provider, plan, apply.
  • You can recognise the symptoms of a non-drop-in provider fork (massive plan diff after replace).
  • You can recover from a failed replace-provider with the backup file.

Knowledge check · 7 questions

  1. Q1. What does `terraform state replace-provider` do?

  2. Q2. What is the right pre-flight before running state replace-provider?

  3. Q3. state replace-provider will surface schema divergence between the old and new providers as a plan diff.

  4. Q4. Which scenario is a valid use of state replace-provider?

  5. Q5. Which of the following are pre-flight steps for a state replace-provider? (Select all that apply.)

  6. Q6. After replace-provider, the plan shows a massive diff across hundreds of resources. What does this mean?

  7. Q7. A team is migrating from hashicorp/aws to mycorp/aws because the vendor fork has a security patch. After running replace-provider, the plan is empty. What is the right next step?

Passing score: 75%. Answers are checked in this browser.