linuxlab.io
Tutorials▾
  • Linux & networking
    File system, processes, TCP/IP, BGP and OSPF
    →
  • Terraform & IaC
    HCL, state, plan/apply on a LocalStack sandbox
    →
  • Git & GitHub
    Object model, plumbing, branching, GitHub Actions
    →
All tutorials →
PricingAboutSign inCreate account
/
Intro
Lessons
Footer
linuxlab-TutorialsPricingAboutPrivacy & cookies
Copyright © 2026 LinuxLab. All rights reserved.
linuxlab.io
Tutorials▾
  • Linux & networking
    File system, processes, TCP/IP, BGP and OSPF
    →
  • Terraform & IaC
    HCL, state, plan/apply on a LocalStack sandbox
    →
  • Git & GitHub
    Object model, plumbing, branching, GitHub Actions
    →
All tutorials →
PricingAboutSign inCreate account
/
  • Introduction
  • Lessons
  • How it works
  • Knowledge base
  • Cheat sheet
  • Capstone
  • Interview prep
home/terraform/how/tf-module-io-flow

how/modules

Modules: how variables flow down and outputs flow up

What happens at the root → child module boundary: input through variable, output through output, and why a module stays reusable only if it does not reach for global providers.

A module in Terraform is a function. It has parameters (variable), it has a body (resource), it has a return value (output). The root config calls the module through a module "..." block. That is the "function call."

Press ▶ to see how one and the same S3 bucket can be described with a reusable module, and what Terraform does at each step.

step 1/6·00 · root declares the module
ROOT: MAIN.TFтвой проектMODULES/BUCKETreusable HCLmodule "bucket" { source = "./modules/bucket" name = "linuxlab-prod" versioning = true}# использование output:output "site_url" { value = module.bucket.url}variable "name" {}variable "versioning" {}resource "aws_s3_bucket" "this" { bucket = var.name versioning { enabled = var.versioning }}output "url" { value = aws_s3_bucket.this.website_endpoint}root зовёт модуль block'ом · модуль рядом, ещё не активирован

§ steps

  1. main.tf has a module "bucket" block. source says where to get the module code: a local folder, git, the Terraform Registry.

    The module itself (modules/bucket/) sits next to it, but is not activated yet. Terraform only discovered it during init. At this stage the module is just downloaded files, no resources are created.

recap

What matters to remember:

  • A module does not see anything that is not passed to it through variable. No global state, no "magic" providers, only what you declared explicitly. That is what makes a module reusable.
  • A module's outputs are the only channel through which root gets data from inside. If there are no outputs, treat the module as a black box.
  • sensitive = true on a variable or output is preserved when it crosses the boundary. Terraform masks the value in logs and in the plan. See tf-sensitive.
  • You can (and should) pass a provider into a module through providers = { aws = aws.us-east-1 }. Otherwise the module uses root's default provider, and multi-region scenarios break.
  • Versioning modules through [[tf-module-versioning|source = "...?ref=v1.2.0"]] is mandatory for production. Without it any commit to the source can change your infrastructure.

Next: tf-remote-backend-lock on how two engineers work with the same state at the same time.

§ dig into the knowledge base

  • tf-module-basicsModule basics
  • tf-module-inputs-outputsvariable and output in modules
  • tf-module-sourcesWhere to get modules: local, git, registry
  • tf-module-versioningVersioning modules
  • tf-sensitivesensitive = true across the module boundary

§ try it hands-on

  • ›tf-intermediate-01-first-module- Your first module
  • ›tf-intermediate-02-module-from-registry- A module from the Terraform Registry
  • ›tf-intermediate-03-module-for-each- for_each on a module
Footer
linuxlab-
Copyright © 2026 LinuxLab. All rights reserved.
Tutorials
Pricing
About
Privacy & cookies