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 配置文件中各元素的层级关系与交互方式:

HCL 配置文件结构(.tf) 根模块(Root Module) terraform { } required_version required_providers backend provider "aws" { } region profile assume_role variable { } type default description resource "type" "name" { } 参数(Argument):键值对 表达式(Expression):动态值 嵌套块(Nested Block) 元参数(count / for_each) output { } value description sensitive module { } source version inputs var.xxx 引用 属性引用 💡 注释(# / // / /* */)可出现在任何位置,不影响执行逻辑,仅用于文档说明。 表达式(Expression)在运行时求值,支持引用、插值、条件、函数、for 循环等。

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 表达能力的理解。