kb/modules
A Terraform module is a reusable piece of HCL with a contract of input variables and output values. Sources (local, git, registry, archive, S3), version constraints, module composition, and passing providers are what you need to turn copy-paste into an infrastructure library.
A module can call other modules inside its own main.tf, using the same `module` block. The address in state becomes `module.A.module.B.<type>.<name>`. Providers are inherited by default, but with several aliases (multi-region, multi-account) you pass them explicitly through `providers = { aws = aws.eu }`. `for_each` over a module works with TF 0.13+.
`source` tells Terraform where to fetch a module's code. There are five main types: a local path (`./modules/x`), a git repo (`git::https://...`), the Terraform Registry (`hashicorp/consul/aws`), an archive (`https://.../v1.0.zip`), and an S3 object (`s3::https://...`). Use local for code in your own repo, the Registry for public modules, and git for private ones.
A module is any directory that contains `.tf` files and can be referenced via a `module` block. The root module is the directory where you run terraform. Child modules are the ones being called. The module contract: input variables (what it accepts), output values (what it exposes). Everything else is an implementation detail.
From the outside, a module is visible only through its input variables (what it accepts) and its output values (what it returns). Everything else is implementation detail. A good module hides its resources behind this contract so you can rewrite the body without touching the calls in the root module.