4. HCL语法精要与网络资源定义

在 Terraform 中,HCL(HashiCorp Configuration Language)是描述基础设施的唯一入口。网络资源自动化部署的可靠性,很大程度上取决于我们能否用 HCL 准确表达“要创建什么、依赖什么、暴露什么”。本节以企业网络场景为主线,把资源块、变量、输出、数据类型、表达式和内置函数串成一条可落地的配置链。

资源块:网络资源的声明单元

资源块是 Terraform 管理对象的最小声明单位,语法为 resource "类型" "本地名" { ... }。类型决定调用哪个 Provider 的 API,本地名用于在同一模块内引用。以阿里云 VPC 与交换机为例:

resource "alicloud_vpc" "prod" {
  vpc_name   = "prod-vpc"
  cidr_block = "10.20.0.0/16"
}

resource "alicloud_vswitch" "app" {
  vpc_id       = alicloud_vpc.prod.id
  cidr_block   = "10.20.1.0/24"
  zone_id      = "cn-hangzhou-h"
  vswitch_name = "app-switch"
}

这里 alicloud_vpc.prod.id 是隐式引用,Terraform 会据此建立依赖关系:先建 VPC,再建交换机。资源块中的参数名必须与 Provider Schema 一致,写错会在 terraform validate 阶段报错,而不是等到 apply 才失败。

变量:把网络参数从代码中抽离

企业网络通常按环境、地域、网段划分。把这些值硬编码在资源块里,会导致复制粘贴和人为错误。变量块 variable 用于声明输入,支持类型、默认值、描述和校验。

variable "env" {
  type        = string
  description = "环境标识"
  default     = "prod"
}

variable "vpc_cidr" {
  type    = string
  default = "10.20.0.0/16"

  validation {
    condition     = can(cidrnetmask(var.vpc_cidr))
    error_message = "vpc_cidr 必须是合法 CIDR。"
  }
}

variable "azs" {
  type    = list(string)
  default = ["cn-hangzhou-h", "cn-hangzhou-i"]
}

变量值可通过 -var、terraform.tfvars 或环境变量 TF_VAR_ 传入。优先级从高到低为:命令行 -var、*.auto.tfvars、terraform.tfvars、环境变量、默认值。生产环境建议把 terraform.tfvars 纳入版本控制,敏感值用 Vault 或 CI 变量注入。

数据类型:约束网络参数的形状

HCL 的基础类型包括 string、number、bool,复合类型包括 list、set、map、object、tuple。网络场景中,网段列表用 list(string),可用区到网段的映射用 map(string),而包含名称、CIDR、网关的完整子网定义更适合 object:

variable "subnets" {
  type = map(object({
    cidr = string
    az   = string
  }))
  default = {
    app = { cidr = "10.20.1.0/24", az = "cn-hangzhou-h" }
    db  = { cidr = "10.20.2.0/24", az = "cn-hangzhou-i" }
  }
}

类型约束越精确,错误越早暴露。若把 subnets 写成 map(any),虽然能通过,但后续引用 each.value.cidr 时缺少类型保护,容易在运行时才报错。

表达式与内置函数:让网络定义具备计算能力

表达式用于在资源块中做引用、条件判断和集合遍历。常用形式包括:

  • 条件表达式:var.env == "prod" ? "10.20.0.0/16" : "10.30.0.0/16"
  • for 表达式:[for s in var.azs : "${var.env}-${s}"]
  • 展开表达式:alicloud_vswitch.app[*].id

内置函数在网络资源定义中尤其高频。例如用 cidrsubnet 从 VPC 网段自动切分子网,避免手工计算:

resource "alicloud_vswitch" "app" {
  vpc_id     = alicloud_vpc.prod.id
  cidr_block = cidrsubnet(var.vpc_cidr, 8, 1)
  zone_id    = var.azs[0]
}

当 var.vpc_cidr = "10.20.0.0/16" 时,cidrsubnet(..., 8, 1) 得到 10.20.1.0/24。类似地,cidrhost 可计算网关地址,lookup 可从 map 中按 key 取值,merge 可合并标签,try 可处理可选属性。

输出:把网络结果交给下游

输出块 output 用于暴露资源属性,供其他模块、CI 流水线或人工查看。网络模块通常输出 VPC ID、交换机 ID 列表和网段信息:

output "vpc_id" {
  value = alicloud_vpc.prod.id
}

output "vswitch_ids" {
  value = [for s in alicloud_vswitch.app : s.id]
}

输出值可以是敏感值,此时应加 sensitive = true,避免在日志中明文打印。跨模块引用时,通过 module.network.vpc_id 获取,形成模块间的数据流。

综合示例:一条完整的网络定义链

把上述元素组合起来,一个可复用的网络模块大致如下:变量接收环境与网段,资源块创建 VPC 和多个交换机,表达式与函数完成子网切分,输出暴露关键 ID。其数据流与依赖关系可用下图表示:

HCL 网络资源定义链:变量 → 资源 → 输出 变量 variable env: string vpc_cidr: string azs: list(string) subnets: map(object) 校验:can(cidrnetmask(...)) 表达式与内置函数 cidrsubnet(vpc_cidr, 8, 1) cidrhost(cidr, 1) for s in azs : ... lookup / merge / try 条件:env == "prod" ? ... 资源 resource alicloud_vpc.prod alicloud_vswitch.app alicloud_vswitch.db 隐式依赖:vpc_id 引用 显式依赖:depends_on 输出 output vpc_id = alicloud_vpc.prod.id vswitch_ids = [for s in ... : s.id] sensitive = true(按需) 下游模块 / CI module.network.vpc_id

图中左侧是输入变量,中间是表达式与函数对变量做变换,右侧是资源块消费变换结果,底部输出把资源属性交给下游。这条链的每一环都可以独立测试:terraform console 可验证函数结果,terraform plan 可观察资源变更,terraform output 可确认暴露值。

实践建议:先用 terraform console 验证 cidrsubnet、lookup 等函数在目标网段上的输出,再写入资源块,可显著减少 plan 阶段的反复调整。
注意:变量默认值不要包含真实生产密钥;sensitive = true 只控制 CLI 输出,不会加密 state 文件,state 仍需远端加密存储。