直接复用模块是Terraform工程落地最短路径:source须为可解析路径(本地相对路径、Git仓库或Registry)、变量需显式传入且类型严格匹配、输出值必须被根模块显式引用,多环境应通过变量而非workspace区分。

直接复用模块是 Terraform 工程落地的最短路径——只要 source 路径对、变量传得准、输出接得住,就能把 VPC、Lambda、RDS 这类标准化架构“当函数调用”,彻底告别复制粘贴。
module 块里 source 必须指向可解析的路径或注册表地址
source 是模块调用的唯一入口,它不接受相对路径模糊写法(比如 "../modules/vpc" 在某些工作目录下会失效),也不支持未声明的本地路径别名。
- 本地模块:用相对于当前
.tf文件的相对路径,推荐以../../modules/xxx开头,确保从根模块调用时路径稳定 - Git 仓库:格式为
"git::https://github.com/org/repo.git//path/to/module?ref=v1.2.0",//后必须指定子目录,?ref=强制锁定版本,避免main分支漂移 - Registry 模块:如
"terraform-aws-modules/vpc/aws",需提前在required_providers中声明对应 provider 版本 - 错误现象:
Error: Module not found或Failed to download module,基本都是source路径拼错、网络不通或 ref 不存在
variables.tf 定义的变量必须全部显式传入,没有“默认值兜底”这回事
Terraform 不会自动 fallback 到 variables.tf 中的 default 值,除非你明确没传该变量。但一旦你在 module 块里写了变量名(哪怕赋空值),Terraform 就认为你要覆盖它——此时若类型不匹配或值非法,立刻报错。
- 常见坑:
variable "enable_nat_gateway" { type = bool },你传enable_nat_gateway = "false"(字符串)而非false(布尔),Terraform 直接拒绝执行 - 安全做法:所有必填变量(即没设
default的)必须在module块中显式赋值;可选变量若依赖默认行为,就完全不写那一行 - 调试技巧:运行
terraform console后输入module.my_vpc可查看实际生效的变量快照,比翻文件更可靠
outputs.tf 输出值必须被根模块显式引用,否则等于没导出
模块内部定义了 output "vpc_id",不代表根模块能直接用 vpc_id。必须通过 module. 语法访问,且该引用必须出现在某个资源属性、本地值或另一个输出中,否则 Terraform 会在 plan 阶段静默忽略。
- 典型误用:只在
outputs.tf里写output "db_endpoint",但根模块没任何地方用到它 → plan 里看不到这个值,apply 后也无法查 - 正确链路:
module "rds" { source = "./modules/rds" ... }→ 根模块中写resource "aws_security_group_rule" "allow_db" { ... from_port = module.rds.db_port } - 性能注意:如果模块输出大量字段(如
subnet_ids列表含 6 个 ID),而你只用其中第一个,别写module.vpc.subnet_ids[0]——Terraform 仍会完整计算整个列表,可能拖慢 plan
多环境复用时,不要靠 workspace 切换模块逻辑
terraform workspace 只隔离 state 文件,不改变模块代码、变量值或 source 路径。想让 stage 和 prod 用同一套模块但不同配置,必须靠变量驱动,而不是幻想 “切到 prod workspace 后 module 自动读 prod 变量”。
- 可行方案:用
tfvars文件分离配置,terraform apply -var-file=stage.tfvarsvs-var-file=prod.tfvars,两个文件里传不同的cluster_name、vpc_cidr等 - 反模式:在
module块里写count = terraform.workspace == "prod" ? 1 : 0—— 这会让模块在 dev workspace 下彻底不创建资源,违背“逻辑复用”初衷 - 关键提醒:模块本身不应包含任何
terraform.workspace判断逻辑,它的职责只是“按给定参数建资源”,环境差异必须收口到变量层
真正卡住人的从来不是模块语法,而是变量作用域的隐式传递、输出值的延迟求值、以及本地路径在不同 CI 环境下的解析差异——这些细节不写进 source 和 variables.tf,文档里也几乎不提。











