HCL 语法与 Provider 配置
核心问题:Terraform 用什么语言描述基础设施?怎样连接到 AWS / GCP / Azure 并开始声明资源?
什么是 HCL
HCL(HashiCorp Configuration Language)是 Terraform 的配置语言,设计目标是人类可读、机器可写:
# 这是注释
# 块(Block)—— HCL 的基本结构
resource "aws_instance" "web" { # 块类型 标签1 标签2
ami = "ami-0c55b159cbfafe1f0" # 参数(字符串)
instance_type = "t3.medium" # 参数(字符串)
tags = { # 参数(对象/Map)
Name = "web-server"
Env = "production"
}
}
HCL 数据类型
# 字符串
name = "my-server"
multiline = <<-EOT
第一行
第二行
EOT
# 数字
port = 8080
cpu = 2.5
# 布尔值
enabled = true
# 列表
availability_zones = ["ap-southeast-1a", "ap-southeast-1b"]
# Map(对象)
tags = {
Name = "web-server"
Environment = "production"
}
# 引用其他资源的属性(字符串插值)
subnet_id = aws_subnet.public.id
name = "server-${var.env}-${count.index}"
文件组织约定
Terraform 会读取目录下所有 .tf 文件,约定俗成的拆分方式:
project/
├── main.tf # 核心资源(EC2、RDS 等)
├── variables.tf # 输入变量声明
├── outputs.tf # 输出值声明
├── locals.tf # 本地计算值
├── provider.tf # Provider 配置
├── backend.tf # 远程 State 配置
├── versions.tf # 版本约束
└── terraform.tfvars # 变量赋值(可选,不提交生产密钥到 Git)
variables.tf:输入变量
# variables.tf
variable "region" {
description = "AWS 区域"
type = string
default = "ap-southeast-1"
}
variable "env" {
description = "部署环境(dev/staging/production)"
type = string
validation {
condition = contains(["dev", "staging", "production"], var.env)
error_message = "env 必须是 dev、staging 或 production。"
}
}
variable "instance_count" {
description = "Web 服务器数量"
type = number
default = 2
}
variable "allowed_cidrs" {
description = "允许访问的 CIDR 列表"
type = list(string)
default = ["0.0.0.0/0"]
}
variable "tags" {
description = "资源标签"
type = map(string)
default = {}
}
variable "db_password" {
description = "数据库密码"
type = string
sensitive = true # 在输出和日志中隐藏
}
赋值方式(优先级从高到低)
# 1. 命令行 -var
terraform apply -var="env=production" -var="instance_count=3"
# 2. .tfvars 文件(自动加载 terraform.tfvars)
terraform apply -var-file="production.tfvars"
# 3. 环境变量(TF_VAR_ 前缀)
export TF_VAR_env=production
export TF_VAR_db_password=secret123
terraform apply
# 4. default 值(最低优先级)
# production.tfvars
env = "production"
instance_count = 3
allowed_cidrs = ["10.0.0.0/8"]
outputs.tf:输出值
# outputs.tf
output "vpc_id" {
description = "VPC ID"
value = aws_vpc.main.id
}
output "web_public_ips" {
description = "Web 服务器公网 IP 列表"
value = aws_instance.web[*].public_ip
}
output "db_endpoint" {
description = "RDS 连接端点"
value = aws_db_instance.main.endpoint
sensitive = true # 不在 plan/apply 输出中显示
}
# 查看输出
terraform output
terraform output vpc_id
terraform output -json # JSON 格式,方便脚本处理
locals.tf:本地计算值
# locals.tf
locals {
# 统一标签(所有资源都加)
common_tags = merge(var.tags, {
Project = "myapp"
Environment = var.env
ManagedBy = "Terraform"
})
# 计算值
name_prefix = "${var.env}-myapp"
is_prod = var.env == "production"
# 条件值
instance_type = local.is_prod ? "t3.large" : "t3.small"
}
# 使用
resource "aws_instance" "web" {
instance_type = local.instance_type
tags = local.common_tags
}
Provider 配置
Provider 是 Terraform 与各云平台/服务的"适配器",每种 Provider 对应一套资源类型。
versions.tf:版本约束
# versions.tf
terraform {
required_version = ">= 1.6" # Terraform 版本
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0" # ~> 5.0 表示 5.x,不允许 6.x
}
random = {
source = "hashicorp/random"
version = ">= 3.5"
}
}
}
AWS Provider
# provider.tf(AWS)
provider "aws" {
region = var.region
# 凭据优先级(不要在此硬编码!):
# 1. 环境变量 AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY
# 2. ~/.aws/credentials 文件
# 3. IAM Role(EC2 实例 / ECS Task / GitHub OIDC)
# 假设角色(跨账号管理)
assume_role {
role_arn = "arn:aws:iam::${var.target_account_id}:role/TerraformExecutionRole"
}
default_tags {
tags = {
ManagedBy = "Terraform"
}
}
}
# 别名 Provider(同时管理多个区域)
provider "aws" {
alias = "us_east_1"
region = "us-east-1"
}
GCP Provider
provider "google" {
project = var.gcp_project_id
region = "asia-southeast1"
zone = "asia-southeast1-a"
# 凭据:环境变量 GOOGLE_APPLICATION_CREDENTIALS 或 gcloud auth
}
Azure Provider
provider "azurerm" {
features {}
subscription_id = var.azure_subscription_id
# 凭据:az login 或环境变量 ARM_CLIENT_ID 等
}
第一个 Terraform 资源:AWS VPC
# main.tf
resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
enable_dns_hostnames = true
enable_dns_support = true
tags = merge(local.common_tags, {
Name = "${local.name_prefix}-vpc"
})
}
resource "aws_subnet" "public" {
count = 3
vpc_id = aws_vpc.main.id
cidr_block = "10.0.${count.index + 1}.0/24"
availability_zone = data.aws_availability_zones.available.names[count.index]
map_public_ip_on_launch = true
tags = merge(local.common_tags, {
Name = "${local.name_prefix}-public-${count.index + 1}"
Tier = "Public"
})
}
data "aws_availability_zones" "available" {
state = "available"
}
resource "aws_internet_gateway" "main" {
vpc_id = aws_vpc.main.id
tags = merge(local.common_tags, { Name = "${local.name_prefix}-igw" })
}
初始化与基本命令
# 1. 初始化(下载 Provider,配置 Backend)
terraform init
# 2. 格式化代码(保持风格一致)
terraform fmt
# 3. 验证语法(不连接 Provider API)
terraform validate
# 4. 查看执行计划
terraform plan -var-file=production.tfvars
# 5. 应用变更
terraform apply -var-file=production.tfvars
# 6. 销毁资源(谨慎!)
terraform destroy -var-file=production.tfvars
常见错误
| 错误 | 原因 | 解决 |
|---|---|---|
Error: No valid credential sources found | AWS 凭据未配置 | 设置环境变量 AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY,或配置 ~/.aws/credentials |
Error: Provider version constraint | Provider 版本不满足 | 更新 versions.tf 中的版本约束,运行 terraform init -upgrade |
Error: Invalid reference | 引用了不存在的资源属性 | 检查 resource_type.name.attribute 格式 |
Error: Unsupported argument | 参数拼写错误 | 查阅对应 Provider 文档 |