Terraform
Infrastructure as Code
Terraform Modules
Output Variables
Module Composition

Terraform module - output variable as input for another module

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

Introduction

Terraform modules become powerful when you compose them instead of treating each one as an isolated folder. A common pattern is to create a resource in one module, expose an output from that module, and pass the output into another module as an input variable.

The important detail is that modules do not talk directly to each other. The parent configuration reads the first module's output and passes it into the second module explicitly.

The Wiring Pattern

Assume you have a network module that creates a VPC and a compute module that launches instances inside that VPC. The flow looks like this:

  1. The network module defines an output such as vpc_id.
  2. The root module calls module.network.vpc_id.
  3. The root module passes that value into the compute module as an input variable.

That parent-child wiring is the standard Terraform pattern.

Module That Exposes An Output

Inside modules/network, define the resources and export the value you want other modules to consume:

hcl
1resource "aws_vpc" "main" {
2  cidr_block = var.cidr_block
3}
4
5output "vpc_id" {
6  value = aws_vpc.main.id
7}

The output is visible to the parent module after Terraform evaluates the module.

Module That Accepts The Input

Inside modules/compute, declare an input variable for the value coming from the parent:

hcl
1variable "vpc_id" {
2  type = string
3}
4
5resource "aws_security_group" "app" {
6  name   = "app-sg"
7  vpc_id = var.vpc_id
8}

The compute module does not know where the vpc_id came from. It only knows that the parent provided it.

Root Module That Connects Them

Now wire both modules together in the root configuration:

hcl
1module "network" {
2  source     = "./modules/network"
3  cidr_block = "10.0.0.0/16"
4}
5
6module "compute" {
7  source = "./modules/compute"
8  vpc_id = module.network.vpc_id
9}

That is the whole pattern. Terraform automatically understands that module.compute depends on the value produced by module.network, so the dependency graph is created from the reference.

Passing Structured Outputs

Outputs do not have to be simple strings. If one module needs several related values, expose an object or a map. That can make module composition cleaner:

hcl
1output "network_info" {
2  value = {
3    vpc_id     = aws_vpc.main.id
4    cidr_block = aws_vpc.main.cidr_block
5  }
6}

Then consume the object from the parent:

hcl
1module "compute" {
2  source = "./modules/compute"
3  vpc_id = module.network.network_info.vpc_id
4}

This is useful when the second module needs multiple values from the first and you want a stable interface.

Why The Parent Module Matters

A frequent misconception is expecting module.compute to reference module.network internally. Terraform does not work that way. Sibling modules are isolated. Only the parent knows about both children, so only the parent can connect them.

That design keeps modules reusable. The compute module can be used with any VPC source as long as it receives a valid vpc_id.

Common Pitfalls

The first mistake is trying to reference module.other_module.some_output from inside a child module. That fails because child modules cannot see their siblings.

Another pitfall is forgetting to define the output in the first module. If the value is not exposed with an output block, the parent has nothing to pass downstream.

A third issue is type mismatch. If one module outputs a list or object but the receiving variable expects a string, Terraform raises a validation error. Declare input variable types carefully so the interface is explicit.

Summary

  • One module's output becomes another module's input through the parent configuration.
  • Child modules do not reference sibling modules directly.
  • Use output blocks to expose values from a module.
  • Pass the value through the root module with module.source_name.output_name.
  • Keep variable types explicit so module interfaces remain predictable.

Related reading
Course
Beginner
27 lessons
10 hours
System Design Fundamentals

Build a strong foundation in designing scalable, reliable distributed systems.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

All Rights Reserved.