跳到主要内容

基础语法

Terraform 使用 HashiCorp Configuration Language(HCL)作为配置语言。HCL 是一种声明式语言,设计目标是既易于人类阅读和编写,又易于机器解析。本章将详细介绍 HCL 的语法和 Terraform 配置的核心概念。

HCL 基础

基本语法结构

HCL 文件使用 .tf 扩展名,基本语法包含以下几个要素:

# 这是单行注释

/*
这是
多行注释
*/

# 块(Block)- Terraform 配置的基本结构
block_type "label1" "label2" {
# 块内容
argument_name = argument_value
}

# 参数(Argument)- 赋值语句
name = "value"

# 表达式
count = 3 + 2
enabled = true

数据类型

HCL 支持以下基本数据类型:

字符串(String)

# 双引号字符串,支持转义和插值
name = "web-server"
description = "Server for ${var.environment} environment"

# 多行字符串(heredoc 语法)
policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "s3:GetObject",
"Resource": "*"
}
]
}
EOF

数字(Number)

# 整数
count = 5

# 浮点数
price = 19.99

# 负数
temperature = -10

布尔值(Bool)

enabled = true
destroy = false

列表(List/Tuple)

# 列表使用方括号,元素可以是不同类型
availability_zones = ["us-east-1a", "us-east-1b", "us-east-1c"]

# 空列表
empty_list = []

# 混合类型列表(不推荐)
mixed = ["string", 123, true]

对象/映射(Object/Map)

# 映射使用花括号,键值对形式
tags = {
Name = "WebServer"
Environment = "Production"
Owner = "DevOps"
}

# 空映射
empty_map = {}

# 嵌套映射
server_config = {
cpu = 4
memory = 8
disk = {
size = 100
type = "ssd"
}
}

Null

# 表示未设置或省略
optional_value = null

Terraform 配置块

terraform 块

terraform 块用于配置 Terraform 本身的行为:

terraform {
# 指定所需的 Terraform 版本
required_version = ">= 1.0"

# 指定所需的 Provider 及其版本
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
azurerm = {
source = "hashicorp/azurerm"
version = "~> 3.0"
}
}

# 配置后端存储(远程状态)
backend "s3" {
bucket = "my-terraform-state"
key = "prod/terraform.tfstate"
region = "us-east-1"
}
}

版本约束说明:

约束含义
= 1.2.3精确匹配 1.2.3
!= 1.2.3排除 1.2.3
> 1.2.3大于 1.2.3
>= 1.2.3大于或等于 1.2.3
< 1.2.3小于 1.2.3
<= 1.2.3小于或等于 1.2.3
~> 1.2.3兼容版本(>= 1.2.3, < 1.3.0)
~> 1.2兼容版本(>= 1.2.0, < 2.0.0)

provider 块

provider 块配置与特定基础设施平台的连接:

# AWS Provider
provider "aws" {
region = "us-east-1"

# 默认标签
default_tags {
tags = {
Environment = "Production"
ManagedBy = "Terraform"
}
}
}

# 多个相同类型的 Provider(别名)
provider "aws" {
alias = "west"
region = "us-west-2"
}

# 使用别名 Provider
resource "aws_instance" "example" {
provider = aws.west
# ...
}

resource 块

resource 块定义要创建的基础设施组件:

resource "aws_instance" "web_server" {
# 资源参数
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"

# 标签
tags = {
Name = "WebServer"
}

# 生命周期规则
lifecycle {
prevent_destroy = true
ignore_changes = [tags]
}
}

资源块语法:

resource "<PROVIDER>_<TYPE>" "<NAME>" {
[CONFIG ...]
}
  • PROVIDER:提供商名称(如 aws、azurerm、google)
  • TYPE:资源类型(如 instance、bucket、vpc)
  • NAME:资源的本地标识符(在同一模块中唯一)

引用资源属性:

# 创建 VPC
resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
}

# 在子网中引用 VPC ID
resource "aws_subnet" "public" {
vpc_id = aws_vpc.main.id # 引用 vpc 资源的 id 属性
cidr_block = "10.0.1.0/24"
}

data 块

data 块用于查询现有资源的数据:

# 查询现有的 AMI
data "aws_ami" "amazon_linux" {
most_recent = true
owners = ["amazon"]

filter {
name = "name"
values = ["amzn2-ami-hvm-*-x86_64-gp2"]
}

filter {
name = "virtualization-type"
values = ["hvm"]
}
}

# 使用查询结果
resource "aws_instance" "web" {
ami = data.aws_ami.amazon_linux.id
instance_type = "t2.micro"
}

variable 块

variable 块定义输入变量:

# 基本变量定义
variable "instance_type" {
description = "EC2 instance type"
type = string
default = "t2.micro"
}

# 带验证的变量
variable "environment" {
description = "Environment name"
type = string

validation {
condition = contains(["dev", "staging", "prod"], var.environment)
error_message = "Environment must be dev, staging, or prod."
}
}

# 复杂类型变量
variable "subnets" {
description = "Subnet configuration"
type = list(object({
name = string
cidr = string
az = string
}))
default = []
}

# 敏感变量
variable "db_password" {
description = "Database password"
type = string
sensitive = true # 在输出中隐藏值
}

变量类型:

# 基本类型
string
number
bool

# 集合类型
list(<TYPE>)
set(<TYPE>)
map(<TYPE>)
object({<ATTR_NAME> = <TYPE>, ...})
tuple([<TYPE>, ...])
any

output 块

output 块定义输出值:

# 基本输出
output "instance_ip" {
description = "The public IP of the instance"
value = aws_instance.web.public_ip
}

# 敏感输出
output "db_connection_string" {
description = "Database connection string"
value = "postgresql://${var.db_user}:${var.db_password}@${aws_db_instance.main.endpoint}/mydb"
sensitive = true
}

# 条件输出
output "instance_id" {
value = var.create_instance ? aws_instance.web[0].id : null
}

locals 块

locals 块定义本地值,用于简化配置:

locals {
# 基本本地值
common_tags = {
Environment = var.environment
Project = var.project_name
ManagedBy = "Terraform"
}

# 计算本地值
name_prefix = "${var.project_name}-${var.environment}"

# 条件本地值
instance_count = var.environment == "prod" ? 3 : 1

# 组合本地值
all_tags = merge(
local.common_tags,
var.additional_tags
)
}

# 使用本地值
resource "aws_instance" "web" {
count = local.instance_count

tags = local.all_tags
}

表达式和函数

字符串插值

name = "${var.project}-${var.environment}"

# 使用 format 函数
name = format("%s-%s-%03d", var.project, var.environment, var.index)

条件表达式

# 条件 ? true_value : false_value
instance_type = var.environment == "prod" ? "t2.large" : "t2.micro"

# 嵌套条件
count = var.enabled ? (var.high_availability ? 3 : 1) : 0

for 表达式

# 列表推导
[for s in var.subnets : s.cidr_block]

# 带条件的列表推导
[for s in var.subnets : s.name if s.public]

# 映射推导
{for s in var.subnets : s.name => s.cidr_block}

# 带条件的映射推导
{for s in var.subnets : s.name => s.cidr_block if s.public}

常用内置函数

# 字符串函数
upper("hello") # "HELLO"
lower("WORLD") # "world"
substr("hello", 0, 3) # "hel"
join("-", ["a", "b"]) # "a-b"
split(",", "a,b,c") # ["a", "b", "c"]
replace("hello", "l", "x") # "hexxo"

# 数字函数
max(1, 5, 3) # 5
min(1, 5, 3) # 1
ceil(1.2) # 2
floor(1.8) # 1

# 集合函数
length(["a", "b"]) # 2
concat(["a"], ["b"]) # ["a", "b"]
distinct(["a", "a", "b"]) # ["a", "b"]
contains(["a", "b"], "a") # true
keys({a=1, b=2}) # ["a", "b"]
values({a=1, b=2}) # [1, 2]
merge({a=1}, {b=2}) # {a=1, b=2}

# 类型转换函数
tostring(123) # "123"
tonumber("123") # 123
tobool("true") # true
tolist([1, 2, 3]) # [1, 2, 3]
tomap({a=1}) # {a=1}

# 文件函数
file("script.sh") # 读取文件内容
filebase64("file") # Base64 编码文件内容
fileexists("file") # 检查文件是否存在
filemd5("file") # 计算文件 MD5

# 编码函数
jsonencode({a=1}) # '{"a":1}'
jsondecode('{"a":1}') # {a=1}
yamlencode({a=1}) # "a: 1\n"
yamldecode("a: 1") # {a=1}
base64encode("text") # "dGV4dA=="
base64decode("dGV4dA==") # "text"

# 路径函数
path.module # 模块路径
path.root # 根模块路径
path.cwd # 当前工作目录

# 时间和哈希函数
timestamp() # 当前时间戳
uuid() # 生成 UUID
md5("text") # 计算 MD5
sha256("text") # 计算 SHA256

动态块

动态块用于根据条件或集合动态生成嵌套块:

# 动态生成 ingress 规则
resource "aws_security_group" "example" {
name = "example-sg"

dynamic "ingress" {
for_each = var.ingress_rules
content {
from_port = ingress.value.port
to_port = ingress.value.port
protocol = ingress.value.protocol
cidr_blocks = ingress.value.cidr_blocks
}
}
}

# 变量定义
variable "ingress_rules" {
type = list(object({
port = number
protocol = string
cidr_blocks = list(string)
}))
default = [
{
port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
},
{
port = 443
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
]
}

完整示例

下面是一个完整的 Terraform 配置示例,展示了本章介绍的各种语法元素:

# terraform.tf
terraform {
required_version = ">= 1.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}

# variables.tf
variable "environment" {
description = "Environment name"
type = string
default = "dev"

validation {
condition = contains(["dev", "staging", "prod"], var.environment)
error_message = "Environment must be dev, staging, or prod."
}
}

variable "instance_count" {
description = "Number of instances"
type = number
default = 1
}

# locals.tf
locals {
common_tags = {
Environment = var.environment
ManagedBy = "Terraform"
CreatedAt = timestamp()
}

name_prefix = "app-${var.environment}"
}

# main.tf
resource "aws_instance" "web" {
count = var.instance_count

ami = data.aws_ami.amazon_linux.id
instance_type = var.environment == "prod" ? "t2.medium" : "t2.micro"

tags = merge(
local.common_tags,
{
Name = "${local.name_prefix}-web-${count.index + 1}"
}
)

lifecycle {
create_before_destroy = true
}
}

# outputs.tf
output "instance_ips" {
description = "Public IPs of instances"
value = aws_instance.web[*].public_ip
}

下一步

掌握了基础语法后,我们将学习 Provider 和资源,深入了解如何与各种云平台交互。