Ravindra BagaleCourses & study guides मराठी

Chapter 35: Terraform Modules Writing Using Registry and Versioning

35.1 Why modules — stop copying folders

In short: A module is a folder of .tf files with inputs and outputs that you call like a function.

Story. An Infosys client had eleven copies of the same web-server code, one per project, and a security fix had to be applied eleven times. A module is a folder of .tf files with inputs and outputs that you call like a function. The root module is the folder where you run Terraform; every other module is a child. Write the pattern once, review it once, and every caller gets the fix by bumping a version.

root module module "web" modules/ web-server resources outputs back
The root module calls a child module with inputs and receives outputs back.
modules/
  web-server/
    main.tf
    variables.tf
    outputs.tf
    versions.tf
envs/
  dev/
    main.tf

What each line does

  • modules/ — Holds reusable child modules.
  • web-server/ — One module, one job: a web server and its security group.
  • main.tf — The resources the module creates.
  • variables.tf — The inputs callers must or may pass.
  • outputs.tf — The values the module returns to callers.
  • versions.tf — The provider requirement; child modules declare a range but no provider block.
  • envs/dev/main.tf — The root module for dev that calls the child module.

When it is green.

$ terraform init
Initializing modules...
- web in ../../modules/web-server
Terraform has been successfully initialized!

When it is red.

Error: Module not installed
  on main.tf line 1: module "web"
This module is not yet installed. Run "terraform init" to install all modules.

Fix. Every time you add or change a module source, run terraform init again.

What: every term in plain English

  • module — A folder of .tf files. You call it with inputs and it gives back outputs.
  • root module — The folder where you run terraform init, plan and apply.
  • child module — Any module that the root module calls with a module block.
  • source — Where Terraform finds the module code: a local path, the registry, or Git.
  • input — A variable the module declares. The caller sets it in the module block.
  • output — A value the module gives back, read as module.NAME.OUTPUT.
  • .terraform/modules — The hidden folder where terraform init copies or downloads modules.
  • modules.json — A file inside .terraform/modules that records which module came from where.

Example 1: See the copy-paste problem first

A TCS project team had three folders, dev, test and prod, each with the same 60 lines for a web server. A security group fix was made in dev and forgotten in prod. Let us see the problem with a quick count.

  1. Make three folders that each hold the same main.tf.
  2. Count the lines that are the same.
  3. Change one port in dev only.
  4. See how prod now differs.
mkdir -p copy-lab/dev copy-lab/test copy-lab/prod
cat > copy-lab/dev/main.tf <<'EOF'
resource "aws_security_group" "web" {
  name = "web-sg"
  ingress {
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }
}
EOF
cp copy-lab/dev/main.tf copy-lab/test/main.tf
cp copy-lab/dev/main.tf copy-lab/prod/main.tf
sed -i 's/= 80/= 443/' copy-lab/dev/main.tf
diff copy-lab/dev/main.tf copy-lab/prod/main.tf

Output

4,5c4,5
<     from_port   = 443
<     to_port     = 443
---
>     from_port   = 80
>     to_port     = 80

What it shows: One fix, three places to remember. A module puts the code in one place, so a fix reaches every caller.

Example 2: Turn the folder into a module

The same team moved the code into modules/web-sg and called it three times. Now the port is an input.

  1. Create modules/web-sg with variables.tf, main.tf and outputs.tf.
  2. Call it from envs/dev/main.tf.
  3. Run init and see the module installed.
# modules/web-sg/variables.tf
variable "name" {
  type = string
}
variable "port" {
  type    = number
  default = 443
}

# modules/web-sg/main.tf
resource "aws_security_group" "this" {
  name = var.name
  ingress {
    from_port   = var.port
    to_port     = var.port
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

# modules/web-sg/outputs.tf
output "id" {
  value = aws_security_group.this.id
}
# envs/dev/main.tf
provider "aws" {
  region = "ap-south-1"
}

module "web_sg" {
  source = "../../modules/web-sg"
  name   = "dev-web-sg"
}

output "sg_id" {
  value = module.web_sg.id
}
cd module-lab/envs/dev
terraform init
terraform plan

Output

Initializing modules...
- web_sg in ../../modules/web-sg

Initializing provider plugins...
- Installing hashicorp/aws v5.x.x...
Terraform has been successfully initialized!

  # module.web_sg.aws_security_group.this will be created
  + resource "aws_security_group" "this" {
      + name = "dev-web-sg"
      ...
    }

Plan: 1 to add, 0 to change, 0 to destroy.

What it shows: The address now starts with module.web_sg. That tells you the resource lives inside a module.

Example 3: Call one module three times

A Flipkart-style team needs a group for web, admin and api. One module, three calls, three different inputs.

module "web_sg" {
  source = "../../modules/web-sg"
  name   = "dev-web-sg"
  port   = 443
}

module "admin_sg" {
  source = "../../modules/web-sg"
  name   = "dev-admin-sg"
  port   = 8443
}

module "api_sg" {
  source = "../../modules/web-sg"
  name   = "dev-api-sg"
  port   = 8080
}
terraform init
terraform plan | grep "will be created"

Output

# module.admin_sg.aws_security_group.this will be created
# module.api_sg.aws_security_group.this will be created
# module.web_sg.aws_security_group.this will be created

What it shows: Every new module block needs terraform init once, even when the source is the same folder.

Example 4: Look inside .terraform/modules

When something odd happens, check what init really installed.

ls .terraform/modules
cat .terraform/modules/modules.json
terraform state list

Output

modules.json
{"Modules":[{"Key":"","Source":"","Dir":"."},
 {"Key":"admin_sg","Source":"../../modules/web-sg","Dir":"../../modules/web-sg"},
 {"Key":"api_sg","Source":"../../modules/web-sg","Dir":"../../modules/web-sg"},
 {"Key":"web_sg","Source":"../../modules/web-sg","Dir":"../../modules/web-sg"}]}
module.admin_sg.aws_security_group.this
module.api_sg.aws_security_group.this
module.web_sg.aws_security_group.this

What it shows: Local modules are not copied; Dir points to the real folder. Registry and Git modules are downloaded into .terraform/modules.

Command cheat sheet

Command What it does When to use it
terraform init Install or link every module and provider After adding or changing any module block
terraform get Install modules only Quick refresh of modules without providers
terraform state list Show addresses, including module.NAME. After apply
terraform plan Preview what the module calls would build Every change
cat .​terraform/​modules/​modules.​json See where each module came from Debugging a wrong source

Verify: did it work?

  1. Module is installed

    terraform init
    

    Output

    - web_sg in ../../modules/web-sg
    
  2. Resources live inside the module

    terraform state list
    

    Output

    module.web_sg.aws_security_group.this
    
  3. Output works

    terraform output sg_id
    

    Output

    "sg-0abc1234def567890"
    

When it is red 1

Error: Module not installed
  on main.tf line 5: module "admin_sg"
This module is not yet installed. Run "terraform init" to install all modules.

Cause. You added a new module block after the last init.

Fix, step by step

  1. Run terraform init.
  2. Run terraform plan again.

When it is red 2

Error: Unreadable module directory
Unable to evaluate directory symlink: lstat ../../module/web-sg: no such file or directory

Cause. The source path has a typo, here module instead of modules.

Fix, step by step

  1. Check the path from the root folder with ls ../../modules.
  2. Fix source and run terraform init.

When it is red 3

Error: Duplicate module call
  on main.tf line 12: module "web_sg"
A module call named "web_sg" was already defined at main.tf:1.

Cause. Two module blocks have the same label.

Fix, step by step

  1. Give each call a unique name such as web_sg and api_sg.

Cleanup

terraform destroy
rm -rf copy-lab

Ravindra Bagale's Tip

Make a module when the same code appears a second time, not the tenth time. Small modules with one job are easy to test.

Practice tasks

Easy Spot the copy

Why: Find the problem first.

What to do: Find two folders in your project that hold the same resource.

How:
  1. Use diff on the two files.

Check: You can name the lines that repeat.

Medium First module

Why: Turn repeat code into one place.

What to do: Move one security group into modules/web-sg and call it.

How:
  1. Add variables, main and outputs.
  2. Call it from envs/dev.

Check: plan shows module.web_sg.

Medium Three calls

Why: Reuse with inputs.

What to do: Call the module three times with different ports.

How:
  1. Change only name and port.

Check: 3 to add.

Hard Read modules.json

Why: Know what init did.

What to do: Explain each line of modules.json to a friend.

How:
  1. Run init, then cat the file.

Check: You can say which folder each call uses.

Easy Say it in two lines

Why: If you can say it simply, you understand it.

What to do: Write two short lines about this lesson for a fresher.

How:
  1. Write the problem it solves.
  2. Write the one command or file you use.
  3. Read it aloud.

Check: A fresher understands it without a question.

Easy Start a safe lab folder

Why: Practice needs a throwaway place.

What to do: Make a new folder with versions.tf and a random_pet resource, so no AWS cost.

How:
  1. mkdir tf-lab and cd into it.
  2. Add the hashicorp/random provider and one random_pet.
  3. Run terraform init and terraform apply.

Check: Apply complete! with 1 added.

Medium Break it on purpose

Why: You must know the red output by face.

What to do: Cause one error from the red part of this lesson and fix it.

How:
  1. Follow one red section.
  2. Copy the exact message to your notes.
  3. Write the fix in one line.

Check: You fixed it without searching the web.

Medium Run it twice

Why: A second run must be quiet.

What to do: Run the same plan two times with no edits.

How:
  1. Run terraform plan.
  2. Run it again.
  3. Explain any change it still shows.

Check: The second plan says No changes, or you can explain each line.

Medium Write a runbook page

Why: At 2 a.m. nobody remembers flags.

What to do: Write a one-page runbook for this lesson.

How:
  1. Title, when to use, exact commands, how to check, how to undo.

Check: A teammate can follow it alone.

Medium Run it from a pipeline

Why: Real teams run Terraform from CI, not from laptops.

What to do: Run the main command of this lesson inside a GitHub Actions or Jenkins job.

How:
  1. Add a job with hashicorp/setup-terraform or a Terraform Docker image.
  2. Give it AWS access through an IAM role, never a key in the file.
  3. Run init, then the command from this lesson.

Check: The job log shows the same output you saw on your laptop.

Medium Check the cost

Why: Labs left running cost real money.

What to do: Find what this lesson would cost per day if you forgot to destroy it.

How:
  1. Open the AWS Pricing Calculator.
  2. Add each resource from your plan.
  3. Write the daily cost in your notes.

Check: You know the daily cost and you ran terraform destroy.

Hard Draw the flow top to bottom

Why: A drawing shows the order of work.

What to do: Draw this lesson as boxes from top to bottom.

How:
  1. Start at your code, go through plan and state, end at AWS.
  2. Mark where each command reads or writes.

Check: Each box matches a real step you ran.

Hard Explain it like an interview

Why: Interviewers want real stories.

What to do: Record a 60 second answer about this lesson.

How:
  1. Say the problem, the command, and one error you hit.

Check: Your answer has a real command and a real error message.

Homework: try at home

Try at home

Look at your last project. Write down three blocks you copied more than once. For each, write the inputs a module would need.

Recap

  • A module is a folder with inputs, resources and outputs.
  • The root module is where you run Terraform.
  • Use module.NAME.OUTPUT to read values.
  • Run terraform init after every new module block.
  • Fix once in the module and every caller gets it.

FAQ

Is every Terraform folder a module?

Yes. The folder where you run Terraform is the root module. Folders it calls are child modules.

Should a child module have a provider block?

No. Declare required_providers only. The root passes the provider.

Can a module call another module?

Yes, but keep it to one or two levels. Deep nesting is hard to debug.

Do I need terraform init for local modules?

Yes. init records the module even though nothing is downloaded.

How small should a module be?

One clear job, such as a web server or a bucket with logging.

Interview questions

  1. What is a Terraform module?

    A folder of .tf files with inputs and outputs, called with a module block so the same code is reused.

  2. Root module vs child module?

    The root is where you run commands. A child is called by the root with a module block.

  3. Why not just copy folders?

    Copies drift apart. A fix has to be made many times. A module fixes it once.

  4. How do you read a value from a module?

    With module.NAME.OUTPUT, for example module.web_sg.id.

  5. What does terraform init do for modules?

    It links local modules and downloads registry or Git modules into .terraform/modules.

Quick quiz

Q1. Where do you run terraform apply?

In the root module.

Q2. How do you read output id from module web_sg?

module.web_sg.id

Q3. What must you run after adding a module block?

terraform init

Q4. Does a child module need a provider block?

No.

Q5. Where are downloaded modules kept?

.terraform/modules

Samajla ka?

Ravi Sir's Cards for this lesson

मराठी cards