TerraformXVIII · Troubleshooting and RecoveryProduction Terraform
Troubleshooting Configuration Errors
What you'll learn
- Diagnose HCL syntax and semantic errors without mutating production state
- Trace a wrong provider from source address through install, configuration, and plan output
- Verify module source resolution and distinguish local, registry, and versioned sources
- Use validation and focused plan commands to test one configuration hypothesis
- Recover configuration safely while preserving provider and module version controls
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
Terraform configuration errors are failures in the declarative description of infrastructure: HCL syntax, provider selection and version, module source resolution, variable values, or the graph built from references. They are usually found before a provider API call, but an error can be reported later when an invalid value reaches a provider. The goal is to find the earliest failing boundary.
The configuration is the source of intent. State is evidence of
what Terraform previously recorded. Do not repair a bad plan by
changing state when the problem is in .tf files or module/provider
metadata.
The configuration diagnostic order
Use a clean, exact working copy of the revision under investigation. Record the Terraform version, working directory, backend, workspace, and any environment-provided variables. Do not begin with an apply.
- Reproduce the error. Run formatting checks and validation with the same revision and provider/plugin cache conditions.
- Classify the boundary. Parser, provider installation, provider configuration, module installation, graph construction, or provider API.
- Inspect the selected source. Check the exact provider source
address and module
source, including relative paths and version constraints. - Test one hypothesis. Change only the suspected configuration or dependency declaration, then run a read-only or saved plan.
- Apply the smallest fix. Preserve version locks, review the plan, and document any deliberate upgrade or module migration.
- Verify the result. Run validation, a plan, and the service-level checks relevant to the affected resources.
HCL syntax: fix structure before semantics
A missing brace, quote, comma, or attribute separator is a parser failure. Formatting can make the file easier to inspect, but it cannot fix an unsupported argument or an invalid value.
This fragment is intentionally incomplete:
resource "aws_instance" "web" {
ami = var.web_ami
instance_type = "t3.small"
A parser reports a message such as:
Error: Missing attribute separator
on main.tf line 5, in resource "aws_instance" "web":
5: instance_type = "t3.small"
Expected an attribute name or a closing brace.
Add the missing close brace, then run the check again:
resource "aws_instance" "web" {
ami = var.web_ami
instance_type = "t3.small"
}
READ-ONLY
terraform fmt -check -recursive -diff
terraform validate
fmt -check reports files that are not canonical without rewriting
them. validate reports configuration and provider schema problems
after the working directory has been initialised. If either command
changes no file, you have preserved a useful baseline for comparison.
The wrong provider
A provider has two identities in the configuration: the local name used
by resources and the canonical source address used to install the
plugin. A common mistake is to write hashicorp/aws as if it were the
local name, or to change a resource type without changing the
provider source and version constraint.
terraform {
required_version = ">= 1.9.0, < 2.0.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
provider "aws" {
region = var.aws_region
}
aws is the local provider name. hashicorp/aws is the registry
source address. The version constraint controls which compatible
provider releases Terraform may select. A lock file records the
selected versions and checksums for the platforms used by the team.
The provider problem can occur at three different stages:
- Provider installation.
terraform initcannot find, download, or verify the selected plugin. Check the required-provider block and the lock file. - Provider configuration. The provider block lacks a required
argument, uses the wrong region, or points at an invalid endpoint.
terraform validatemay report the first category of error. - Provider runtime. Credentials, permissions, API quotas, or resource schemas fail only during planning or apply. Inspect the provider’s operation and environment, not just the HCL line.
CONFIGURATION — downloads or verifies provider and module metadata
and may write .terraform and lock data. Do not add -upgrade to a
production diagnosis without an explicit review.
terraform init -backend=false -input=false
terraform providers
Illustrative output from terraform providers:
provider[registry.terraform.io/hashicorp/aws] 5.80.0
The important field is the canonical address. If the output names a different provider, or if the version is outside the constraint, stop and correct the requirement or lock decision. A missing provider can also be caused by a local plugin mirror or an unavailable network, so collect that evidence before changing the source.
The wrong module source
A module’s source tells Terraform where to download code. It is a
path, registry address, or supported package source, not a friendly label.
Relative paths resolve from the directory containing the module call.
A local module can be tested without a registry version, while a
registry module can be constrained to a version or version range.
module "network" {
source = "../modules/network"
vpc_cidr = "10.20.0.0/16"
}
The path is correct only when ../modules/network exists from this
module’s directory. A common failure is to copy a path from the root
working directory into a child module, where the same relative path
points at a different location. The fix is to state the intended
working directory and test the source in a clean checkout.
CONFIGURATION — downloads module code into the local module cache. It does not apply the module.
terraform get
terraform init -backend=false -input=false
terraform validate
terraform plan -input=false -no-color -refresh-only
For a registry module, pin a reviewed version instead of relying on a moving tag:
module "network" {
source = "terraform-aws-modules/vpc/aws"
version = "~> 5.5"
}
Do not use a version argument with a local path; Terraform does not
treat a local directory as a versioned registry package. If a module
source changes, compare the provider requirements and resource
addresses, and expect a potentially different graph. A source change
is a configuration change, not a harmless line edit.
Five configuration failure modes
1. Syntax error hidden by a vague wrapper
Observable symptom. terraform plan says “could not load plugin”
or produces an internal error while the actual message contains a
missing separator or unclosed expression.
Recovery. Run terraform fmt -check -recursive -diff and
terraform validate first, then inspect the first parser diagnostic.
Correct the HCL structure; do not alter provider code or state to work
around it.
2. Provider name and source address disagree
Observable symptom. Resources use aws_instance, but the
required_providers source is omitted, misspelled, or points to a
different provider. The error may say the provider is not available or
the resource type is unsupported.
Recovery. Read terraform providers, verify the canonical address
and local name, pin a compatible version, and run init without an
upgrade. A resource rename is a separate migration and needs a plan;
do not perform it merely to silence an installation error.
3. Provider credentials are mistaken for a syntax problem
Observable symptom. init succeeds and validate succeeds, but
planning returns No valid credential sources, AccessDenied, or a
region-specific API error.
Recovery. Verify the provider’s region, account, credentials helper, and environment variables. Do not paste a token into HCL or a ticket; use the approved short-lived identity mechanism.
4. Module path resolves from the wrong directory
Observable symptom. terraform get cannot find the module, or it
loads a different module with a valid but unintended name. The
resulting resource addresses or inputs do not match the reviewed
configuration.
Recovery. Confirm the current working directory, calculate the relative path from the module call, and test in a clean checkout. If the source is changing from local to registry, review the module’s provider and variable contract before planning.
5. A format or validate pass is treated as a safe apply
Observable symptom. CI shows both checks green, yet the plan proposes a replacement or the provider rejects an existing value. Formatting and validation are deterministic checks; they do not query the cloud or review blast radius.
Recovery. Require a saved plan and a second review for every
non-empty plan. Use -detailed-exitcode in automation so a non-empty
plan is not mistaken for a no-op result.
Security and performance
Providers and modules are executable supply-chain inputs. Pin provider
versions and checksums in the lock file, review registry modules before
adoption, and avoid a source that changes every run because it points
at an unpinned moving tag. terraform init downloads and starts the
provider installation workflow, so it belongs inside a controlled
developer or CI environment with an approved plugin cache.
Repeated init and module downloads add network and CPU overhead. A
shared, permissioned plugin cache can reduce downloads, but it must
still enforce the selected versions. Run formatting and validation
before network-heavy plan work, and keep modules small enough to review
and test.
Production guidance
- Pin Terraform, providers, and registry modules; commit and review the lock file.
- Use a clean checkout and a consistent plugin cache for diagnosing failures in CI.
- Separate provider installation, provider authentication, and provider runtime errors in the incident record.
- Require a reviewed plan and a rollback revision before applying a module or provider change.
- Test configuration recovery in staging, including a failed init, wrong source path, and unexpected provider default.
Verification
- You can run formatting and validation checks without changing the files under investigation.
- You can identify the local provider name and canonical provider source address separately.
- You can diagnose a module path from the directory containing the module call.
- You can distinguish a provider install error from an authentication or runtime error.
- You can use a saved plan and detailed exit status to prove that a configuration is not ready to apply.
- You can pin and review providers and modules before production recovery.
Knowledge check · 7 questions
Q1. What is the best first diagnostic sequence for a configuration error?
Q2. In a provider declaration, what is the distinction between `aws` and `hashicorp/aws`?
Q3. Formatting a Terraform file changes layout and quoting only, so an unsupported provider argument or an invalid external value survives it untouched.
Q4. Which checks help separate a configuration problem from a provider runtime problem? (Select all that apply.)
Q5. `terraform get` reports that a local module cannot be found. The module call is one directory deeper than expected. What is the likely cause?
Q6. Why should a production configuration recovery prefer a reviewed `.tf` change over a one-off local state edit?
Q7. What does `terraform providers` help an operator inspect?
Passing score: 75%. Answers are checked in this browser.