Terraform 基础设施即代码

让 Agent 按远程状态后端、工作区或目录分环境、可复用模块与变量校验编写 Terraform,并强调 plan 评审与破坏性变更标注。

SKILL 定义 provider 版本锁定、required_version、以及 .tfvars 与 CI 注入敏感变量的方式;禁止在仓库中提交明文密钥。

模块接口:输入输出文档、moved/import 块在重构时的使用;lifecycleprevent_destroy 对关键资源的保护策略写清。

与策略即代码(OPA/Sentinel)或 cost 估算工具的集成若存在,说明 Agent 需在 PR 描述中附带 plan 摘要要点。

  • 格式化:terraform fmt、validate、tflint 在流水线中的顺序。
  • 状态:锁、漂移检测与手动 state rm 的审批流程。
  • 多云/多 region:provider alias 与模块调用的目录约定。

IaC 主流程(skill-flow-block)

  [ 锁定:terraform / provider / 模块 source 版本 ]
                    │
                    ▼
         [ 变量:类型约束、敏感标记、.tfvars / CI 密钥注入 ]
                    │
                    ▼
    [ fmt → validate →(可选 tflint)→ plan -out=tfplan ]
                    │
           ┌────────┴────────┐
           ▼                 ▼
  [ 人类或策略:审 plan 摘要、标 destroy/replace ]     [ 合并阻塞项:未批准不改状态 ]
           │                 │
           └────────┬────────┘
                    ▼
         [ apply 限定:指定 plan 文件 / 受控环境 / 回滚说明 ]
Agent 输出应显式区分「仅 plan」与「可 apply」;含 -destroy、replace、或敏感资源变更时,必须在说明中引用对应资源地址与风险。

状态:远程后端、锁与漂移

  • 远程后端:在 SKILL 中写清 bucket / table / workspace 前缀或等价资源名规则;禁止多人共用会互相覆盖的本地 terraform.tfstate 作为事实来源。
  • 状态锁:说明 CI 与本地并发时的锁超时、失败重试与「谁可 force-unlock」的审批;Agent 不应默认建议强制解锁而不经人工确认。
  • 漂移:约定周期或发布前的 plan 无变更基线;发现非 Terraform 管理侧修改时,记录是 import、刷新还是人工对齐。
  • 危险操作:state rmstate mv、替换 provider 或后端迁移须附检查清单与回滚路径。

远程 backend 配置(S3 + DynamoDB 锁)与变量类型约束示例:

# backend.tf — S3 远程状态 + DynamoDB 分布式锁
terraform {
  required_version = ">= 1.6.0"

  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"   # 锁定大版本,小版本可升级
    }
  }

  backend "s3" {
    bucket         = "mycompany-terraform-state"
    key            = "services/myapp/production/terraform.tfstate"
    region         = "us-east-1"
    encrypt        = true
    dynamodb_table = "mycompany-terraform-locks"  # 防并发写

    # 角色假设(OIDC + role)替代长期 key
    role_arn = "arn:aws:iam::123456789012:role/TerraformStateRole"
  }
}

# variables.tf — 类型约束与 validation block
variable "environment" {
  type        = string
  description = "部署目标环境(dev/staging/production)"

  validation {
    condition     = contains(["dev", "staging", "production"], var.environment)
    error_message = "environment 必须是 dev、staging 或 production 之一。"
  }
}

variable "instance_count" {
  type        = number
  description = "EC2 实例数量(1-20)"
  default     = 1

  validation {
    condition     = var.instance_count >= 1 && var.instance_count <= 20
    error_message = "instance_count 必须在 1 到 20 之间。"
  }
}

variable "allowed_cidr_blocks" {
  type        = list(string)
  description = "允许访问的 CIDR 列表"
  default     = []

  validation {
    condition = alltrue([
      for cidr in var.allowed_cidr_blocks :
      can(cidrhost(cidr, 0))
    ])
    error_message = "allowed_cidr_blocks 中的每个值必须是合法的 CIDR 格式。"
  }
}

模块:接口、版本与重构

  • 接口:variables.tf / outputs.tf 用描述字段文档化必填项、默认值与破坏性变更;跨团队模块建议发布语义化版本或固定 Git ref。
  • 组合:根模块编排环境差异(dev/stage/prod)时,优先目录或 workspace 策略二选一并写死,避免同一套 root 隐式混用。
  • 重构:使用 movedimport 块减少无意义 destroy/create;大范围重命名在 PR 中附 state 迁移或分步计划。
  • 关键资源:对数据库、证书、生产入口等使用 lifecycle { prevent_destroy = true } 等护栏时,在 SKILL 中列出例外审批流程。

Terraform module 完整示例(inputs / outputs / main.tf):

# modules/ec2-service/variables.tf
variable "service_name" {
  type        = string
  description = "服务名称,用于资源命名前缀"
}

variable "environment" {
  type        = string
  description = "部署环境(dev/staging/production)"
}

variable "instance_type" {
  type        = string
  description = "EC2 实例类型"
  default     = "t3.micro"
}

variable "subnet_ids" {
  type        = list(string)
  description = "部署的子网 ID 列表(至少 2 个可用区)"
}

# modules/ec2-service/main.tf
resource "aws_security_group" "this" {
  name        = "${var.service_name}-${var.environment}"
  description = "Security group for ${var.service_name}"

  lifecycle {
    create_before_destroy = true   # 先创新 SG,再删旧的,保证零停机
  }

  tags = {
    Name        = "${var.service_name}-${var.environment}"
    Environment = var.environment
    ManagedBy   = "terraform"
  }
}

resource "aws_instance" "this" {
  ami           = data.aws_ami.al2023.id
  instance_type = var.instance_type
  subnet_id     = var.subnet_ids[0]

  vpc_security_group_ids = [aws_security_group.this.id]

  lifecycle {
    prevent_destroy = true   # 生产关键资源护栏;例外审批流程见 runbook
    ignore_changes  = [ami]  # AMI 更新由重建流程管理,不在 plan 中体现
  }
}

# modules/ec2-service/outputs.tf
output "instance_id" {
  description = "EC2 实例 ID"
  value       = aws_instance.this.id
}

output "security_group_id" {
  description = "安全组 ID,供上层模块引用"
  value       = aws_security_group.this.id
  sensitive   = false
}

# 根模块调用示例(environments/production/main.tf)
# module "web_service" {
#   source = "../../modules/ec2-service"
#   # 版本控制:固定 Git ref 或发布 tag
#   # source = "git::https://github.com/myorg/tf-modules.git//ec2-service?ref=v1.2.0"
#
#   service_name  = "web"
#   environment   = "production"
#   instance_type = "t3.medium"
#   subnet_ids    = module.vpc.private_subnet_ids
# }

流水线:fmt / validate / plan

  • 顺序建议:terraform fmt -checkterraform validate →(可选)tflint / tfsec → 非交互 plan(只读凭证或 mock)。
  • PR 工件:保存 plan 文本摘要或结构化输出,便于审查机器人与人类对齐「变更条数 / destroy 计数」。
  • 敏感输出:对含密钥的 plan 使用 CI 密文区或脱敏展示;禁止将完整 state 或明文 secret 贴进评论。

Terraform plan 的 CI 集成与 infracost 成本估算:

# .github/workflows/terraform.yml
jobs:
  plan:
    runs-on: ubuntu-24.04
    permissions:
      id-token: write
      contents: read
      pull-requests: write   # 用于评论 plan 摘要
    steps:
      - uses: actions/checkout@v4

      - uses: hashicorp/setup-terraform@v3
        with:
          terraform_version: "1.7.0"

      - name: Configure AWS credentials (OIDC)
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: ${{ vars.TF_PLAN_ROLE_ARN }}   # 只读角色
          aws-region: us-east-1

      - name: Terraform fmt check
        run: terraform fmt -check -recursive

      - name: Terraform validate
        run: terraform validate

      - name: tflint
        uses: terraform-linters/setup-tflint@v4
        with:
          tflint_version: v0.50.0
      - run: tflint --recursive

      - name: Terraform plan
        id: plan
        run: |
          terraform plan \
            -out=tfplan \
            -no-color \
            -input=false \
            2>&1 | tee plan.txt
          # 提取摘要:变更数与 destroy 数
          echo "summary=$(grep -E 'Plan:|No changes' plan.txt | tail -1)" >> "$GITHUB_OUTPUT"

      - name: Infracost cost estimate
        uses: infracost/actions/setup@v3
        with:
          api-key: ${{ secrets.INFRACOST_API_KEY }}
      - run: |
          infracost breakdown --path tfplan --format json > infracost.json
          infracost comment github \
            --path infracost.json \
            --github-token ${{ secrets.GITHUB_TOKEN }} \
            --pull-request ${{ github.event.pull_request.number }} \
            --behavior update

      - name: Comment plan on PR
        uses: marocchino/sticky-pull-request-comment@v2
        with:
          message: |
            ### Terraform Plan Summary
            ${{ steps.plan.outputs.summary }}
            <details><summary>Full plan</summary>

            \`\`\`
            ${{ steps.plan.outputs.stdout }}
            \`\`\`
            </details>

Workspace 与资源地址校验

左侧输入 workspace 名(常用字母、数字、-_)或 资源地址(如 module.vpc.aws_subnet.private[0])。解析在浏览器本地完成,不上传。


              

Workspace:非空、长度 ≤ 256、仅 [A-Za-z0-9_-]。资源地址:无点号时按 workspace 校验;含点号时为 类型.本地名module…类型.本地名 或恰好三段的 data.类型.本地名;各段可为标识符加可选的 [非负整数]

---
name: terraform-iac
description: 模块化 Terraform、远程状态与 plan/apply 安全习惯
tags: [terraform, iac, devops, aws]
---
# 状态管理
1. 远程后端:S3 bucket + DynamoDB table 实现状态存储与分布式锁
2. 后端 role_arn 通过 OIDC 获取,禁止长期 AWS Access Key 进 CI
3. 每个环境(dev/staging/prod)使用独立 state key,禁止共用
4. 漂移检测:周期或发布前 plan,无变更为基线;非 TF 修改必须 import 或对齐

# 模块设计
5. variables.tf:description 文档化必填项、类型约束与 validation block
6. outputs.tf:sensitive = true 标记敏感输出,不在 plan 中明文显示
7. lifecycle.prevent_destroy = true 保护生产关键资源(DB、证书)
8. moved 块重命名资源,避免 destroy/create;import 块纳管已有资源

# CI 流水线
9. 顺序:terraform fmt -check → validate → tflint → plan -out=tfplan
10. plan 摘要(变更数/destroy 数)评论到 PR,人工审批后才能 apply
11. infracost breakdown 估算成本变化,自动评论到 PR
12. apply 必须指定 plan 文件(terraform apply tfplan),禁止无 plan apply

# 安全与治理
13. 禁止明文 secret 在 .tf 文件或 plan 输出中出现
14. sensitive = true 变量不进 plan 文本,敏感 state 字段通过 CI 密文展示
15. state rm/mv 须附检查清单与回滚路径,经过人工审批
16. tfsec / checkov 扫描安全配置问题(如公开 S3 bucket、未加密存储)

返回技能库 更多技能入口