HCL 语法基础:语言结构、变量、输出值、局部值与数据类型

HCL(HashiCorp Configuration Language)是 Terraform 的核心配置语言,所有基础设施资源、变量、输出和逻辑都通过 HCL 表达。掌握 HCL 的语法结构、变量系统、输出值、局部值以及数据类型,是编写可维护、可复用 Terraform 配置的基石。本章将系统拆解这些核心概念,并通过表格、代码示例和结构图帮助你快速建立清晰认知。

2.1 HCL 语言结构

HCL 配置文件以 .tf 为扩展名,采用声明式语法。一个典型的 Terraform 配置由以下顶级块(Block)组成:

  • terraform {}:配置 Terraform 自身行为,如后端存储、所需 Provider 版本。
  • provider {}:声明云平台或服务提供商(如 AWS、Azure、阿里云)。
  • resource {}:定义具体的基础设施资源(如虚拟机、VPC、数据库)。
  • variable {}:声明输入变量,使配置可参数化。
  • output {}:定义输出值,用于向外部暴露资源属性。
  • locals {}:定义局部值,用于简化表达式和避免重复。
  • data {}:引用外部已有资源的数据源。

HCL 块的基本语法格式如下:

<块类型> "<标签>" "<名称>" {
  <参数> = <值>
  <嵌套块> {
    <参数> = <值>
  }
}

例如,定义一个阿里云 ECS 实例:

resource "alicloud_instance" "web" {
  instance_name   = "web-server"
  image_id        = "ubuntu_22_04_x64_20G_alibase_20240926.vhd"
  instance_type   = "ecs.g6.large"
  security_groups = [alicloud_security_group.default.id]
}
💡 提示: HCL 是声明式语言,你只需描述“最终状态”,Terraform 自动计算如何从当前状态迁移到目标状态。这与 Ansible、Shell 等过程式工具形成鲜明对比。

2.2 变量(variable)

变量是 Terraform 配置的输入参数,用于将环境差异、敏感信息、部署参数从代码中抽离出来,实现配置的通用化与复用。

2.2.1 变量声明

变量通过 variable 块声明,支持以下元参数:

元参数 说明 是否必填
type 指定变量的数据类型,如 string、number、list(string)、map(string) 等 否(默认 any)
default 设置默认值,若未赋值则使用该值 否
description 变量的描述信息,用于文档生成和团队协作 否
validation 自定义校验规则,确保输入值符合预期 否
sensitive 标记为敏感变量,在日志和输出中隐藏值 否(默认 false)
nullable 是否允许变量为 null 否(默认 true)

变量声明示例:

variable "region" {
  description = "部署目标地域"
  type        = string
  default     = "cn-hangzhou"
}

variable "instance_count" {
  description = "ECS 实例数量"
  type        = number
  default     = 2
  validation {
    condition     = var.instance_count > 0 && var.instance_count <= 10
    error_message = "实例数量必须在 1 到 10 之间。"
  }
}

variable "tags" {
  description = "资源标签"
  type        = map(string)
  default = {
    Environment = "dev"
    Project     = "terraform-course"
  }
}

variable "db_password" {
  description = "数据库密码"
  type        = string
  sensitive   = true
}

2.2.2 变量赋值方式

Terraform 支持多种变量赋值方式,优先级从高到低排列:

  1. 命令行 -var 参数:terraform apply -var="region=us-east-1"
  2. 变量文件 .tfvars:自动加载 terraform.tfvars 或 *.auto.tfvars
  3. 环境变量 TF_VAR_ 前缀:export TF_VAR_region=ap-southeast-1
  4. 默认值:变量声明中的 default
⚠️ 注意: 敏感变量(如密码、密钥)不要写在 .tfvars 文件中并提交到版本控制。建议使用环境变量或 Terraform Cloud 的敏感变量功能。

2.3 输出值(output)

输出值用于从 Terraform 状态中提取资源属性,供外部系统、CI/CD 管道或运维人员使用。输出值在 terraform apply 后显示在终端,也可通过 terraform output 命令随时查询。

输出值声明语法:

output "<名称>" {
  value       = <表达式>
  description = "输出描述"
  sensitive   = true   # 可选,隐藏输出值
  depends_on  = []     # 可选,显式依赖
}

实际示例:

output "instance_public_ip" {
  description = "ECS 实例公网 IP"
  value       = alicloud_instance.web.public_ip
}

output "rds_connection_string" {
  description = "RDS 数据库连接地址"
  value       = alicloud_db_instance.default.connection_string
  sensitive   = false
}

output "db_password" {
  description = "数据库密码(仅用于初始化)"
  value       = var.db_password
  sensitive   = true
}
📌 最佳实践: 将所有对外暴露的资源标识(IP、域名、连接串、ARN)都定义为输出值。这样团队成员无需登录云控制台,即可通过 terraform output 获取部署结果。

2.4 局部值(local)

局部值类似于编程语言中的局部变量,用于在模块内部简化复杂表达式、避免重复计算、提高可读性。局部值只在当前模块内可见,不会暴露给调用方。

局部值声明语法:

locals {
  <名称> = <表达式>
  <名称> = <表达式>
}

实际示例:

locals {
  # 组合资源名称前缀
  name_prefix = "${var.project}-${var.environment}"

  # 根据环境选择实例规格
  instance_type = var.environment == "production" ? "ecs.g7.4xlarge" : "ecs.g6.large"

  # 通用标签
  common_tags = {
    Name        = local.name_prefix
    Environment = var.environment
    ManagedBy   = "Terraform"
  }

  # 计算子网 CIDR 列表
  subnet_cidrs = [for i in range(var.subnet_count) : cidrsubnet(var.vpc_cidr, 8, i)]
}

在资源中引用局部值:

resource "alicloud_instance" "app" {
  instance_name = local.name_prefix
  instance_type = local.instance_type
  tags          = local.common_tags
}
💡 提示: 当同一个表达式在多个资源或输出中重复出现时,优先使用 locals 提取为局部值。这不仅能减少代码冗余,还能让后续修改只需改动一处。

2.5 数据类型与表达式

HCL 支持丰富的数据类型和表达式,用于构建灵活、动态的配置。

2.5.1 基本数据类型

类型 关键字 示例
字符串 string "cn-hangzhou"
数字 number 42、3.14
布尔值 bool true、false
空值 null null

2.5.2 复合数据类型

类型 关键字 示例 说明
列表 list(<类型>) ["dev", "staging", "prod"] 有序集合,索引从 0 开始
映射 map(<类型>) { env = "dev", region = "cn-hangzhou" } 键值对集合,键为字符串
对象 object({...}) { name = string, age = number } 结构化类型,字段名和类型固定
元组 tuple([...]) ["us-east-1", 80, true] 有序集合,每个元素类型可不同
集合 set(<类型>) ["dev", "prod"] 无序不重复集合

2.5.3 常用表达式

  • 条件表达式:condition ? true_value : false_value
  • for 表达式:[for item in list : item.id] 或 {for k, v in map : k => v}
  • 字符串插值:"${var.name}-${var.env}"
  • 函数调用:length(var.subnets)、cidrsubnet("10.0.0.0/16", 8, 1)
  • 属性引用:alicloud_instance.web.id、var.region
  • 合并与查找:merge(map1, map2)、lookup(map, key, default)

表达式综合示例:

locals {
  # 条件选择实例规格
  instance_type = var.environment == "production" ? "ecs.g7.4xlarge" : "ecs.g6.large"

  # 从 CIDR 列表生成子网名称
  subnet_names = [for i, cidr in local.subnet_cidrs : "${local.name_prefix}-subnet-${i}"]

  # 合并默认标签和自定义标签
  all_tags = merge(local.common_tags, var.extra_tags)
}

2.6 HCL 核心概念关系图

下图展示了 HCL 配置中变量、局部值、资源、输出值之间的数据流向与依赖关系:

HCL 核心概念数据流向图 HCL 核心概念数据流向图 variable 输入参数 locals 局部计算 resource 基础设施资源 output 对外暴露 data 外部数据源 📌 数据流向说明: 1. variable 提供外部输入,locals 基于 variable 和字面量进行计算。 2. resource 引用 variable、locals 和 data 的值来定义基础设施。 3. output 从 resource 或 data 中提取属性,对外暴露部署结果。

2.7 变量与局部值对比

对比维度 variable locals
来源 外部输入(命令行、文件、环境变量) 模块内部定义
作用域 全局(可被子模块引用) 仅当前模块
可变性 每次 apply 可重新赋值 定义后不可变
敏感标记 支持 sensitive = true 不支持
默认值 支持 default 必须显式赋值
典型用途 环境差异、密码、地域、实例规格 名称拼接、标签合并、CIDR 计算

2.8 数据类型选择与校验最佳实践

  • 优先使用强类型:为变量指定具体类型(如 string、number、list(string)),避免使用 any,以便 Terraform 在 plan 阶段捕获类型错误。
  • 使用 validation 块:对变量值进行范围、长度、正则校验,将错误提前暴露。
  • 对象类型用于复杂结构:当变量包含多个关联字段时,使用 object({...}) 而非多个独立变量,提高可读性。
  • 避免在 locals 中执行敏感计算:locals 的值会在日志和状态文件中明文存储,敏感信息应通过 variable 的 sensitive 标记处理。
📌 本章小结: HCL 的变量、输出值、局部值和数据类型构成了 Terraform 配置的“参数化骨架”。变量负责接收外部输入,局部值负责内部计算与简化,输出值负责对外暴露结果,数据类型与表达式则提供了构建动态配置的语法工具。掌握这些基础,后续章节的模块化、多环境管理和弹性伸缩编排将水到渠成。