The Situation
It's late. You're wiring up a new module to pass its VPC ID or ARN to another resource, you run terraform plan, and get slapped with this:
Error: Reference to undeclared output value
on main.tf line 14, in resource "aws_instance" "app":
14: subnet_id = module.network.subnet_id
An output value with the name "subnet_id" has not been declared in module.network.
The module exists. The resource exists. Yet Terraform won't budge โ it can't find the output you're trying to read.
Why This Happens
Terraform modules are black boxes. The only values they expose to the outside world are those explicitly declared in an output block inside the module. When you reference module.network.subnet_id, Terraform looks for an output "subnet_id" block in the network module directory. No block, no value โ just this error.
A few things cause this:
- You added a reference in the calling module but forgot to declare the
outputblock in the child module. - You renamed an output block but didn't update all the places that reference it.
- You copied a module from somewhere else and the outputs you expected don't exist in this version.
- There's a subtle name mismatch โ
subnet_ids(plural) vs.subnet_id(singular), for example. - You're pulling outputs across workspaces via
terraform_remote_state, and that output was never declared in the source stack.
Step 1 โ See What Outputs the Module Actually Declares
Start by finding where the module lives. For a local module, the error message tells you โ module.network means look in modules/network/:
ls modules/network/
grep -r 'output' modules/network/
For a registry or Git-sourced module, check the cached download:
find .terraform/modules -name '*.tf' | xargs grep '^output'
You'll see every output block the module actually has. Compare that list against what you're referencing โ the mismatch will jump out.
Add the Missing Output Block
If you control the module, the fix is straightforward. Add the output inside the module directory โ typically modules/network/outputs.tf:
output "subnet_id" {
description = "The ID of the primary subnet"
value = aws_subnet.main.id
}
Run terraform plan again. Error gone.
Need to expose multiple subnets? Use a list output instead:
output "subnet_ids" {
description = "List of all subnet IDs"
value = aws_subnet.main[*].id
}
Then index into it from the calling config:
subnet_id = module.network.subnet_ids[0]
The Output Exists โ But Under a Different Name
Before editing anything, check what the module actually exposes. The Terraform console is useful here:
terraform console
> module.network
That prints all available outputs from the module. Nine times out of ten you'll spot a naming mismatch immediately โ then just update the reference:
# Wrong
subnet_id = module.network.subnet_id
# Right โ output block is named "private_subnet_id"
subnet_id = module.network.private_subnet_id
Remote State: Output Missing From the Source Stack
Using terraform_remote_state to read outputs from another workspace? The output must be declared โ and applied โ in the source stack first.
data "terraform_remote_state" "vpc" {
backend = "s3"
config = {
bucket = "my-tf-state"
key = "vpc/terraform.tfstate"
region = "us-east-1"
}
}
resource "aws_instance" "app" {
subnet_id = data.terraform_remote_state.vpc.outputs.subnet_id
}
The VPC stack needs this in its outputs.tf:
output "subnet_id" {
value = aws_subnet.main.id
}
Deploy the VPC stack first. Terraform reads from the state file โ if the output was never applied, it won't be there. Having it in a .tf file isn't enough.
Public Registry Module: Version Doesn't Have That Output
Pinning to an older module version? The output might not exist yet. This is common with terraform-aws-modules โ version 3.x and 5.x expose different outputs.
module "network" {
source = "terraform-aws-modules/vpc/aws"
version = "3.0.0" # missing the output you need
}
Check the module's changelog or browse its outputs.tf on GitHub for that specific tag. If a newer version has what you need, bump it:
module "network" {
source = "terraform-aws-modules/vpc/aws"
version = "5.1.0"
}
Then pull the update:
terraform init -upgrade
Verify the Fix
Run these three commands in order:
# Catch any syntax issues first
terraform validate
# Should complete with no errors
terraform plan
# See all root-level outputs after apply
terraform output
Want to confirm a specific module reference before applying? Use the console:
terraform console
> module.network.subnet_id
If it returns a value (or a known placeholder), the reference is wired up correctly.
Quick Reference
- Output declared in wrong file โ Doesn't matter which
.tffile inside the module holds theoutputblock, as long as it's somewhere in the module directory. - Sensitive outputs โ Marking an output
sensitive = truedoesn't break references. Terraform just redacts the value in plan output. - Modules using count or for_each โ The output shape changes. Reference it as
module.network[0].subnet_idinstead ofmodule.network.subnet_id.

