Bird
Raised Fist0
Terraformcloud~10 mins

Dependency inversion with modules in Terraform - Step-by-Step Execution

Choose your learning style10 modes available

Start learning this pattern below

Jump into concepts and practice - no test required

or
Recommended
Test this pattern10 questions across easy, medium, and hard to know if this pattern is strong
Process Flow - Dependency inversion with modules
Define low-level module
Define high-level module
High-level module calls low-level module
Root module calls high-level module
Terraform applies root module
Dependency inversion achieved: root depends on abstraction
Shows how Terraform modules depend on abstractions, not details, by having high-level modules call low-level modules, and root module calling high-level modules.
Execution Sample
Terraform
module "network" {
  source = "./modules/network"
}

module "app" {
  source = "./modules/app"
  network_id = module.network.id
}
Root module calls network module first, then app module uses network's output, showing dependency inversion.
Process Table
StepActionModuleInput/OutputResult
1Start terraform applyrootNo inputs yetBegin processing modules
2Call network modulenetworkNo inputsCreates network, outputs id=net-123
3Call app moduleappInput network_id=net-123Creates app using network id
4Complete applyrootAll modules appliedInfrastructure ready with app linked to network
💡 All modules applied successfully, dependencies resolved by passing outputs as inputs
Status Tracker
VariableStartAfter Step 2After Step 3Final
module.network.idundefinednet-123net-123net-123
module.app.network_idundefinedundefinednet-123net-123
Key Moments - 2 Insights
Why does the app module get the network id as input instead of creating the network itself?
Because the network module is a low-level module that creates the network, and the app module depends on it. Passing the network id as input in step 3 (execution_table) shows dependency inversion: app depends on network's abstraction, not implementation.
What ensures the network module runs before the app module?
Terraform automatically orders module execution based on input dependencies. Since app module uses network_id output from network module (step 3), Terraform runs network module first (step 2).
Visual Quiz - 3 Questions
Test your understanding
Look at the execution table, what is the value of module.network.id after step 2?
Anet-123
Bundefined
Cnull
Dapp-456
💡 Hint
Check the 'Input/Output' column in row for step 2
At which step does the app module receive the network id as input?
AStep 1
BStep 3
CStep 2
DStep 4
💡 Hint
Look at the 'Action' and 'Input/Output' columns in the execution table
If the app module did not depend on network module output, what would change in the execution table?
AStep 4 would fail
BStep 2 would be skipped
CStep 3 would not have network_id input
DNo changes
💡 Hint
Dependency inversion means passing outputs as inputs; removing that breaks input in step 3
Concept Snapshot
Terraform modules can depend on each other by passing outputs as inputs.
Low-level modules provide resources and outputs.
High-level modules use those outputs as inputs.
Root module calls high-level modules.
This inverts dependency: modules depend on abstractions, not details.
Terraform orders execution based on these dependencies.
Full Transcript
This visual execution shows how Terraform modules use dependency inversion. The root module calls a low-level network module first, which creates a network and outputs its id. Then the root calls a high-level app module, passing the network id as input. Terraform runs the network module first, then the app module, ensuring dependencies are respected. Variables track the network id being passed along. This pattern means modules depend on abstractions (outputs) rather than creating resources themselves, making infrastructure modular and flexible.

Practice

(1/5)
1. What does dependency inversion mean in Terraform modules?
easy
A. Modules cannot accept variables
B. Modules depend on inputs instead of creating resources themselves
C. Modules always create all resources internally
D. Modules must be written in the root configuration

Solution

  1. Step 1: Understand module dependency principle

    Dependency inversion means modules should not create resources directly but rely on inputs.
  2. Step 2: Identify correct description

    Modules depend on inputs instead of creating resources themselves correctly states modules depend on inputs, making them flexible and reusable.
  3. Final Answer:

    Modules depend on inputs instead of creating resources themselves -> Option B
  4. Quick Check:

    Dependency inversion = Modules use inputs [OK]
Hint: Modules get resource info via inputs, not by creating resources [OK]
Common Mistakes:
  • Thinking modules must create all resources internally
  • Assuming modules cannot accept variables
  • Believing modules must be in root config
2. Which of the following is the correct way to pass a resource ID to a module in Terraform?
easy
A. module "example" { resource_id = aws_instance.example.id }
B. module "example" { input_id = aws_instance.example.id }
C. module "example" { instance_id = var.instance_id }
D. module "example" { instance_id = aws_instance.example.id }

Solution

  1. Step 1: Identify correct variable passing syntax

    Modules accept variables by name; the value can be a resource attribute like aws_instance.example.id.
  2. Step 2: Check option correctness

    module "example" { instance_id = aws_instance.example.id } correctly passes instance_id with the resource ID aws_instance.example.id.
  3. Final Answer:

    module "example" { instance_id = aws_instance.example.id } -> Option D
  4. Quick Check:

    Pass resource ID as variable = module "example" { instance_id = aws_instance.example.id } [OK]
Hint: Use variable name = resource.attribute to pass IDs [OK]
Common Mistakes:
  • Using undefined variable names like resource_id or input_id
  • Passing resource IDs without variable names
  • Confusing variable and resource references
3. Given this Terraform root module snippet:
resource "aws_vpc" "main" {
  cidr_block = "10.0.0.0/16"
}

module "network" {
  source = "./modules/network"
  vpc_id = aws_vpc.main.id
}

What is the expected behavior of the network module?
medium
A. It ignores the VPC ID and creates a subnet only
B. It creates a new VPC inside the module
C. It uses the existing VPC ID passed as input
D. It fails because VPC ID cannot be passed as input

Solution

  1. Step 1: Analyze root module resource and module call

    The root module creates an aws_vpc resource and passes its ID to the network module as vpc_id.
  2. Step 2: Understand module behavior with input

    The network module uses the passed vpc_id to configure resources inside that VPC, not create a new one.
  3. Final Answer:

    It uses the existing VPC ID passed as input -> Option C
  4. Quick Check:

    Module uses input vpc_id = It uses the existing VPC ID passed as input [OK]
Hint: Modules use passed IDs to link resources, not recreate them [OK]
Common Mistakes:
  • Assuming module creates a new VPC ignoring input
  • Thinking passing resource IDs is invalid
  • Believing module fails without explicit VPC creation
4. You have this module call:
module "db" {
  source = "./modules/db"
  subnet_id = aws_subnet.app.id
}

Inside the module, the variable is declared as variable "subnet" { type = string }. What error will occur?
medium
A. Error: Unknown variable 'subnet_id' in module
B. Error: Variable 'subnet' not provided
C. No error, variable names can differ
D. Error: aws_subnet.app.id is invalid

Solution

  1. Step 1: Compare variable name and input argument

    The module expects a variable named 'subnet' but the input is 'subnet_id'.
  2. Step 2: Understand Terraform variable matching

    Terraform matches input arguments to variable names exactly. 'subnet_id' does not match any variable, causing an unsupported argument error.
  3. Final Answer:

    Error: Unknown variable 'subnet_id' in module -> Option A
  4. Quick Check:

    Variable name mismatch causes unknown variable error [OK]
Hint: Variable names must match exactly between module and call [OK]
Common Mistakes:
  • Assuming variable names can differ
  • Confusing variable name with resource attribute name
  • Ignoring error messages about missing variables
5. You want to create a reusable module for an AWS security group that attaches to any VPC. Which approach follows dependency inversion best?
hard
A. Module accepts a VPC ID as input and creates security group in that VPC
B. Module hardcodes a VPC ID inside the module code
C. Module requires the user to create security group outside and passes its ID
D. Module creates its own VPC and security group inside

Solution

  1. Step 1: Understand dependency inversion for modules

    Modules should not create dependent resources like VPCs but accept them as inputs.
  2. Step 2: Evaluate options for best practice

    Module accepts a VPC ID as input and creates security group in that VPC accepts VPC ID as input and creates the security group inside that VPC, following dependency inversion.
  3. Final Answer:

    Module accepts a VPC ID as input and creates security group in that VPC -> Option A
  4. Quick Check:

    Pass dependencies as inputs for flexibility [OK]
Hint: Pass VPC ID as input; module creates resources inside it [OK]
Common Mistakes:
  • Hardcoding resource IDs inside modules
  • Modules creating dependent resources themselves
  • Requiring users to create resources outside without module help