## TL;DR

You referenced a resource name that does not exist in the module where the reference lives. It is almost always a typo, a rename you forgot to propagate, or a reference to something declared in a different module. Fix the name to match the declared resource, then `terraform validate`.

## The error

```text
Error: Reference to undeclared resource

  on outputs.tf line 3, in output "vpc_id":
   3:   value = aws_vpc.main.id

A managed resource "aws_vpc" "main" has not been declared in the root module.
```

## Steps to fix

1. Open the file and line the error names. Copy the exact resource address it complains about (`aws_vpc.main`).
   - Expected: you have the type and name Terraform looked for.
2. Search your configuration for the resource block: `grep -rn 'resource "aws_vpc"' *.tf`. Compare the declared name with the referenced name.
   - Expected: you spot the typo or the rename (e.g. declared `aws_vpc.this`, referenced `aws_vpc.main`).
3. Fix the reference (or the declaration, whichever is wrong). If the resource lives in a child module, you cannot reference it directly: add a module output and use `module.[name].[output]`.
   - Expected: every reference matches a declared address.
4. Run `terraform validate`.
   - Expected: `Success! The configuration is valid.`

## When to use this

- `terraform plan` or `validate` fails with `Reference to undeclared resource` right after you renamed something, copied config between files, or split config into modules.

## When NOT to use this

- `Unsupported attribute` means the resource exists but the attribute does not. `Reference to undeclared variable` / `local value` are the variable/local equivalents, fixed the same way.

## Compatibility

- All Terraform versions 0.12+. Module scoping rules are unchanged across 1.x.

## Root cause

Terraform resolves every reference against the declarations in the same module. A resource in a child module is invisible to the root module (and vice versa); only outputs cross that boundary. Renames break references because addresses are type+name pairs, and nothing auto-updates them.

## Edge cases

- `count = 0` resources still count as declared; the reference resolves but the value may be an empty tuple. Use `one()` or `try()` instead of `[0]`.
- Data sources use `data.[type].[name]`: forgetting the `data.` prefix gives this error.
- After `terraform state mv`, config references do not change; this error means the config itself is wrong, not the state.