4、HCL语言基础:HCL语法、块(Block)、参数(Argument)、表达式(Expression)、注释
HCL(HashiCorp Configuration Language)是 Terraform 的核心配置语言。理解 HCL 的语法结构是编写可维护、可扩展基础设施代码的前提。本章将系统讲解 HCL 的四大基本构成要素:块、参数、表达式和注释。
4.1 HCL 语法概览
HCL 是一种声明式语言,旨在描述“期望状态”而非“执行步骤”。其语法设计强调可读性和层次结构,天然适合描述云资源之间的依赖关系。
核心原则:HCL 文件(.tf)中,块定义资源或配置的边界,参数为块内的键值对赋值,表达式动态计算值,注释为代码提供上下文说明。
以下是一个典型的 HCL 代码片段,展示了四种元素的协作关系:
# 这是一个注释:定义 AWS EC2 实例
resource "aws_instance" "web_server" { # 块开始
ami = "ami-0c55b159cbfafe1f0" # 参数:静态字符串
instance_type = var.instance_type # 参数:引用变量表达式
tags = { # 参数:Map 类型
Name = "Web-${var.environment}" # 参数:字符串插值表达式
}
} # 块结束
4.2 块(Block)
块是 HCL 中最基本的容器结构,用于将相关的配置项组织在一起。每个块由块类型、可选的标签(Label)和花括号 { } 内的主体组成。
| 块类型 | 标签数量 | 典型用途 | 示例 |
|---|---|---|---|
resource |
2(类型、名称) | 定义具体的基础设施资源 | resource "aws_s3_bucket" "data" { } |
variable |
1(名称) | 声明输入变量 | variable "region" { } |
output |
1(名称) | 定义输出值 | output "instance_ip" { } |
provider |
1(名称) | 配置云提供商 | provider "aws" { } |
module |
1(名称) | 调用子模块 | module "vpc" { } |
terraform |
0 | 全局 Terraform 设置 | terraform { } |
最佳实践:块标签(Label)在 Terraform 中作为资源的逻辑标识符,在同一模块内必须唯一。命名时建议使用下划线分隔的语义化名称,如
web_server、db_replica。
块可以嵌套,形成父子层级关系。例如,resource 块内部可以包含 provisioner 块或 lifecycle 块:
resource "aws_instance" "example" {
ami = "ami-123456"
provisioner "local-exec" { # 嵌套块
command = "echo ${self.private_ip} >> /tmp/ips.txt"
}
lifecycle { # 嵌套块
create_before_destroy = true
}
}
4.3 参数(Argument)
参数是块内部的键值对,用于为资源或配置项赋值。参数的值可以是静态的,也可以是动态计算的表达式结果。
- 基本语法:
参数名称 = 值 - 值类型:字符串、数字、布尔值、列表(List)、映射(Map)、对象(Object)、空值(null)
- 必选与可选:每个资源块都有其必填参数(如
ami)和可选参数(如tags),由 Provider 文档定义
resource "aws_security_group" "sg" {
name = "web-sg" # 字符串
description = "Security group for web" # 字符串
vpc_id = var.vpc_id # 引用变量
ingress { # 嵌套块中的参数
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"] # 列表
}
tags = { # 映射
Environment = "production"
Team = "platform"
}
}
4.4 表达式(Expression)
表达式是 HCL 中用于动态计算值的语法单元。Terraform 在解析配置时会对表达式求值,生成最终传递给 Provider 的具体值。
4.4.1 表达式类型
| 表达式类型 | 语法示例 | 说明 |
|---|---|---|
| 字面量 | "hello", 42, true |
直接写死的值 |
| 引用 | var.region, aws_instance.web.id |
引用变量或资源属性 |
| 字符串插值 | "Name-${var.env}" |
在字符串中嵌入表达式 |
| 条件表达式 | var.env == "prod" ? "t3.large" : "t3.medium" |
三元运算符,根据条件选择值 |
| 函数调用 | upper("hello"), join(",", var.list) |
调用内置函数处理值 |
| for 表达式 | [for k, v in var.map : k => v] |
遍历集合生成新集合 |
| 展开表达式 | var.list[*].id |
提取列表中所有元素的某个属性 |
4.4.2 常用表达式示例
# 字符串插值
resource "aws_iam_role" "role" {
name = "app-${var.environment}-role"
}
# 条件表达式
resource "aws_instance" "app" {
instance_type = var.env == "production" ? "m5.large" : "t3.medium"
}
# 函数调用
locals {
env_upper = upper(var.environment)
subnet_ids = join(",", aws_subnet.public[*].id)
}
# for 表达式
locals {
instance_names = [for i in range(3) : "web-${i}"]
}
# 展开表达式
output "all_public_ips" {
value = aws_instance.app[*].public_ip
}
常见陷阱:在条件表达式中,确保两个分支的返回值类型一致。例如
var.env == "prod" ? "t3.large" : 42 会导致类型错误,因为 Terraform 要求条件表达式的两个结果类型必须兼容。
4.5 注释(Comment)
HCL 支持三种注释风格,用于提高代码的可读性和可维护性。
| 注释风格 | 语法 | 适用场景 |
|---|---|---|
| 单行注释 | # 注释内容 |
最常用,用于解释单行参数或块的作用 |
| 单行注释(替代) | // 注释内容 |
与 # 等价,部分开发者偏好此风格 |
| 多行注释 | /* 注释内容 */ |
用于大段说明、禁用代码块或版权声明 |
# 单行注释:定义 VPC 资源
resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
}
// 这也是单行注释
resource "aws_subnet" "public" {
vpc_id = aws_vpc.main.id
cidr_block = "10.0.1.0/24"
}
/*
多行注释:
以下资源用于数据库子网,
在生产环境中需要启用加密。
*/
resource "aws_subnet" "private" {
vpc_id = aws_vpc.main.id
cidr_block = "10.0.2.0/24"
}
注释规范建议:
- 使用
#作为首选注释符号,保持与 Terraform 官方文档一致 - 注释应解释“为什么这样做”,而非“做了什么”(代码本身应表达后者)
- 避免在注释中描述过时的逻辑,及时更新注释与代码同步
- 对于复杂的表达式或条件逻辑,使用注释说明其业务含义
4.6 HCL 语法结构总览
下图展示了 HCL 配置文件中各元素的层级关系与交互方式:
4.7 综合示例:CI/CD 自助服务基础设施片段
以下示例将本章知识点整合到一个真实的 CI/CD 自助服务场景中:
# ============================================
# 模块:ci_cd_runner
# 描述:为自助服务平台创建 CI/CD 执行环境
# ============================================
# 输入变量
variable "runner_name" {
description = "CI/CD runner 的名称"
type = string
}
variable "instance_count" {
description = "Runner 实例数量"
type = number
default = 1
}
variable "environment" {
description = "部署环境(dev/staging/prod)"
type = string
validation {
condition = contains(["dev", "staging", "prod"], var.environment)
error_message = "环境必须是 dev、staging 或 prod。"
}
}
# 本地计算值
locals {
# 根据环境选择实例规格
instance_type = var.environment == "prod" ? "c5.xlarge" : "t3.medium"
# 生成实例名称列表
instance_names = [for i in range(var.instance_count) : "${var.runner_name}-${i}"]
}
# 创建 EC2 实例作为 Runner
resource "aws_instance" "runner" {
count = var.instance_count
ami = "ami-0c55b159cbfafe1f0" # Amazon Linux 2
instance_type = local.instance_type
tags = {
Name = local.instance_names[count.index]
Environment = var.environment
ManagedBy = "terraform"
Service = "ci-cd-runner"
}
# 仅在非生产环境启用终止保护
lifecycle {
prevent_destroy = var.environment == "prod" ? true : false
}
}
# 输出 Runner 的 IP 地址
output "runner_public_ips" {
description = "所有 Runner 实例的公网 IP 列表"
value = aws_instance.runner[*].public_ip
}
关键知识点回顾:
- 块(Block):
variable、locals、resource、output分别承担不同职责 - 参数(Argument):如
instance_type = local.instance_type是键值对赋值 - 表达式(Expression):条件表达式
var.environment == "prod" ? true : false、for 表达式[for i in range(...) : ...]、展开表达式[*].public_ip - 注释(Comment):使用
#和/* */为模块、变量和复杂逻辑提供说明
掌握 HCL 的这四个基础元素后,你已经具备了阅读和编写 Terraform 配置的能力。接下来的章节将深入变量与数据类型,进一步扩展你对 HCL 表达能力的理解。