2. Terraform配置静态检查入门

在 Terraform 工程中,很多问题并不需要真正执行 terraform apply 才能发现。缩进混乱、参数拼写错误、变量类型不匹配、资源属性缺失、Provider 版本约束不合理,这些问题都可以在“静态阶段”被拦截。所谓静态检查,就是在不访问真实云资源、不产生任何基础设施变更的前提下,对配置文件的格式、语法、类型和规范进行校验。本节围绕三个最常用的本地工具展开:terraform fmt、terraform validate 和 tflint,并说明如何把它们组合成本地质量门禁。

三个工具各自解决什么问题

这三个工具并不是互相替代的关系,而是覆盖了从“外观”到“语义”再到“规范”的三层检查。理解它们的边界,才能合理编排执行顺序。

  • terraform fmt:只负责代码格式。它会按照 HCL 官方风格调整缩进、对齐等号、规范空格,不检查语义是否正确。它回答的问题是“代码写得整不整齐”。
  • terraform validate:负责语法与内部一致性。它会解析配置、检查资源类型是否存在、必填参数是否缺失、变量引用是否合法、表达式类型是否匹配。它回答的问题是“这份配置在 Terraform 看来是否自洽”。
  • tflint:负责规范与最佳实践。它能发现 Provider 相关的非法实例规格、未使用的变量、过时的语法、命名不规范等问题。它回答的问题是“这份配置是否符合团队和云厂商的规范”。

三者的检查深度是递进的:格式问题最表层,语法问题居中,规范问题最贴近真实工程约束。因此本地门禁的推荐顺序也是 fmt → validate → tflint,前一层不通过就没有必要进入下一层。

Terraform 本地静态检查门禁:三层递进 格式 → 语法/类型 → 规范/最佳实践,逐层拦截,前层不过不进入后层 ① terraform fmt 检查层级:代码格式 缩进 / 等号对齐 / 空格 不检查语义正确性 命令:terraform fmt -check -recursive ② terraform validate 检查层级:语法与内部一致性 资源类型 / 必填参数 / 变量引用 表达式类型匹配 命令:terraform validate ③ tflint 检查层级:规范与最佳实践 实例规格 / 未使用变量 过时语法 / 命名规范 命令:tflint --recursive 全部通过 → 允许进入 terraform plan / apply 阶段 格式不通过 执行 terraform fmt 自动修复 语法/类型不通过 按报错定位资源与参数并修正 规范不通过 按规则调整或配置忽略 门禁原则:任一环节失败即阻断,修复后重新从①开始; 建议在 pre-commit 或 CI 中固化该顺序,避免人工遗漏。

terraform fmt:统一格式,消除无意义差异

terraform fmt 是最轻量的检查。它按照 HCL 官方风格重写文件,主要动作包括:把缩进统一为两个空格、把属性赋值中的等号对齐、规范块之间的空行。它不会改变任何语义,因此可以放心地让它自动修改文件。

在本地使用时,有两种典型模式。第一种是直接修复:

terraform fmt -recursive

加上 -recursive 后,它会递归处理当前目录下所有子目录中的 .tf 和 .tfvars 文件。第二种是只检查不修改,用于门禁场景:

terraform fmt -check -recursive

当存在格式不一致的文件时,该命令会列出文件名并以非零状态码退出。这正是 CI 中需要的“失败信号”。如果团队希望看到具体差异,可以再加 -diff 参数,它会输出类似 git diff 的对比内容,便于快速定位。

格式检查的价值不在于“好看”,而在于减少代码评审中的噪声。当所有人的提交都经过 fmt 统一后,评审者看到的差异就只剩真正的逻辑变更,而不是缩进或对齐的抖动。

terraform validate:语法与内部一致性校验

terraform validate 需要先执行 terraform init,因为它要加载 Provider 的 schema 才能判断资源类型和参数是否合法。它的检查范围包括:HCL 语法是否正确、资源类型是否被 Provider 支持、必填参数是否缺失、变量和输出引用是否指向已声明对象、表达式类型是否匹配。

例如下面这段配置中,instance_type 被写成了 instance_typ,同时 ami 缺失:

resource "aws_instance" "web" {
  instance_typ = "t3.micro"
  tags = {
    Name = "web-server"
  }
}

执行 terraform validate 后,会得到类似下面的报错(示例输出,具体措辞随版本略有差异):

Error: Unsupported argument
  on main.tf line 2, in resource "aws_instance" "web":
   2:   instance_typ = "t3.micro"
An argument named "instance_typ" is not expected here.

Error: Missing required argument
  on main.tf line 1, in resource "aws_instance" "web":
The argument "ami" is required, but no definition was found.

这两条报错分别对应“参数名拼写错误”和“必填参数缺失”,都是 validate 能稳定捕获的典型问题。修正后再次执行,输出 Success! The configuration is valid. 即表示通过。

terraform validate 不会访问云 API,也不会校验参数取值是否真实存在。例如把 instance_type 写成 t3.microx,validate 可能通过,但 apply 时会失败。这类问题需要交给 tflint 或 plan 阶段处理。

tflint:把规范问题挡在本地

tflint 是第三方静态检查工具,它通过插件机制接入各云厂商的规则集。安装后需要先初始化插件:

tflint --init

然后执行检查:

tflint --recursive

以 AWS 为例,启用 tflint-ruleset-aws 后,它能发现诸如“实例类型不存在”“可用区名称不合法”“未使用的变量声明”“过时的 aws_instance 参数”等问题。下面是一个典型输出示例:

1 issue(s) found:

Warning: "t3.microx" is an invalid EC2 instance type. (aws_instance_invalid_type)
  on main.tf line 2:
   2:   instance_type = "t3.microx"

可以看到,tflint 捕获的正是 validate 无法发现的“取值合法性”问题。此外,tflint 还支持通过 .tflint.hcl 配置文件启用或禁用规则、设置变量值、指定 Provider 版本,使检查结果更贴合项目实际。

组合成本地质量门禁

把三个工具串起来,就形成了一条本地质量门禁。推荐在项目根目录放置一个脚本或 Makefile 目标,按固定顺序执行:

#!/usr/bin/env bash
set -euo pipefail

echo "==> 1/3 格式检查"
terraform fmt -check -recursive

echo "==> 2/3 语法与一致性检查"
terraform init -backend=false
terraform validate

echo "==> 3/3 规范检查"
tflint --init
tflint --recursive

echo "全部静态检查通过"

这里有两个细节值得注意。第一,terraform init 使用了 -backend=false,表示只初始化 Provider 而不连接远程后端,适合在本地和 CI 中快速执行。第二,set -euo pipefail 保证任一命令失败即中断,避免后续检查在错误状态下继续运行。

如果团队使用 Git,可以把这套检查接入 pre-commit 钩子,在提交前自动运行;如果使用 CI,则把它作为流水线的第一个 Job,只有通过后才允许进入 plan 和 apply 阶段。这样,格式、语法和规范问题都会在最早的时间点被拦截,而不是等到部署失败才回头排查。

静态检查的目标不是追求零告警,而是建立一条稳定、可重复、可自动化的第一道防线。fmt 保证一致性,validate 保证可解析性,tflint 保证规范性,三者组合后,本地就能过滤掉大部分低级错误,让后续的 plan 和 apply 聚焦在真正的变更逻辑上。