Terraform 基础 – 第6周:构建和使用你的第一个 Terraform 模块
Source: Dev.to
前几周回顾
在过去的五周里,我们在学习 Terraform 核心概念的同时,构建了一个小而真实的 Azure 环境。我们先部署了第一台虚拟机(VM),随后引入了变量和 tfvars 文件,使配置更加灵活。我们通过网络安全组(NSG)和动态块添加了安全性,最后通过输出值(output)公开了有用的信息。
下面是我们目前基础设施的可视化表示:
Visual Studio Code 中的文件夹结构:
项目文件夹中的关键文件
nsg.tf– 网络安全组及其关联。outputs.tf– 输出值。providers.tf– AzureRM 提供程序块。resource-group.tf– 资源组定义。terraform.tfvars– 项目变量值。variables.tf– 变量定义。virtual-machine.tf– 虚拟机和网络接口卡(NIC)资源。virtual-network.tf– 虚拟网络和子网资源。
此时我们已经拥有一个完整可用、参数化且安全的 VM 部署。随着配置的增长,重复的模式变得显而易见,使得管理多个相似资源变得困难。这正是引入 Terraform 模块的最佳时机。
前几周
系列的 GitHub 仓库:
什么是 Terraform 模块,为什么在 Terraform 中使用模块?
Terraform 模块是一组资源的集合,能够作为单个单元重复使用。与其为每次部署都重新编写相同的 VM、NIC 或 NSG 块,我们只需在模块内部定义一次,然后在需要新实例时调用该模块。
包含主 Terraform 文件的文件夹是 根模块。任何包含 .tf 文件的子文件夹都成为 子模块,且不能单独运行。根模块提供输入,子模块返回输出。这种关系实现了基础设施模式的清晰复用,而无需复制代码。
在真实环境中,模块是必不可少的。想象一个团队需要在开发、预发布和生产环境中管理十台 VM。如果不使用模块,每个环境都会拥有一份相同资源块的副本。一次简单的更改——比如更新标签或调整 NSG 规则——必须在多个地方重复执行,导致漂移并增加维护工作量。
通过将逻辑集中在模块中,我们可以实现:
- 环境之间的一致性。
- 减少重复。
- 更容易的维护和基础设施扩展。
理解模块的输入和输出
模块输入
输入是模块创建资源所需的值。它们在模块内部被定义为变量,根模块在调用模块时提供实际值。对于 VM 模块,典型的输入包括:
- 资源组名称和位置
- 子网 ID
- VM 大小
- 管理员凭证
- NIC 设置
- 允许的端口
模块输出
输出将子模块内部的值暴露给根模块。这样根配置就可以使用模块内部创建的信息,而无需直接访问其内部资源。常见的 VM 模块输出有:
- VM ID
- NIC 名称
- 私有 IP 地址
这些输出可以在配置的其他位置引用,或在部署后显示。
创建我们的第一个 Terraform 模块
注意: 这是一种将已有代码重构为可复用模块的做法,而不是从零构建模块。它映射了真实场景中将一组资源转化为可复用组件的常见过程。
我们将把 VM、NIC 和 NSG 资源移动到 modules/vm 文件夹,同时保留根配置中的资源组、虚拟网络和子网。现有的 variables.tf、outputs.tf 和 terraform.tfvars 仍然保留在根目录,并继续为模块提供值。
步骤 1:创建模块文件夹结构
modules/
└── vm/
├── main.tf
├── variables.tf
└── outputs.tf
步骤 2:在模块中填充 main.tf
将原始 virtual-machine.tf 和 nsg.tf 文件中的虚拟机、NIC、NSG 以及 NSG 关联资源块复制到 modules/vm/main.tf 中。
(图片展示了复制的资源定义。)
步骤 3:定义模块变量
在 modules/vm/variables.tf 中声明模块所需的所有输入(例如 resource_group_name、subnet_id、vm_size 等)。使用与根配置中先前定义的变量名称和类型保持一致。
步骤 4:定义模块输出
在 modules/vm/outputs.tf 中暴露有用的值,如 VM ID、NIC 名称和私有 IP 地址。
步骤 5:在根配置中调用模块
在根文件夹中添加一个 module 块(例如在 main.tf 或专门的 vm-module.tf 文件中),引用新模块并传递所需变量:
module "vm" {
source = "./modules/vm"
resource_group_name = var.resource_group_name
location = var.location
subnet_id = azurerm_subnet.main.id
vm_size = var.vm_size
admin_username = var.admin_username
admin_password = var.admin_password
allowed_ports = var.allowed_ports
}
根目录中现有的 outputs.tf 现在可以引用模块的输出,例如:
output "vm_private_ip" {
value = module.vm.private_ip
}
有了模块,整体配置仍然保持功能完整,同时获得了复用性和更易维护的优势。


