Community程式設計與開發github.com

Terraform 標準化技能

將原始、棕地或由 Terraformer 匯入的 Terraform 程式碼(AWS、GCP、Azure)標準化為符合 HashiCorp 標準的生產級配置:隔離 provider.tf、versions.tf 和 backend.tf,將硬編碼字串參數化為帶類別的 variables.tf 與 locals.tf,以動態參考取代靜態雲端 ID,透過 moved 區塊強制執行 ForceNew 與零漂移防護,並執行標準的 HCL 格式化、驗證與計劃差異摘要。

Terraform 標準化技能 是什麼?

此技能引導 AI 代理將原始、棕地、匯入(例如來自 Terraformer 或 `terraform import`)或舊版的 Terraform 程式碼重構為模組化、生產就緒的 HashiCorp 標準架構。它適用於任何 Terraform 程式碼庫,尤其能有效處理含有硬編碼字串、靜態雲端 ID 或單一大型檔案的配置。此技能適用於 AWS、GCP 及 Azure 環境,並支援 HashiCorp Terraform 與 OpenTofu。其核心工作流程包含五個階段:稽核並識別程式碼異味、建立標準檔案架構(provider.tf、versions.tf、variables.tf 等)、將文字參數化為變數與區域變數、以動態參考連結取代靜態 ID,最後進行格式化、驗證與漂移稽核。其顯著特色在於對棕地與 Terraformer 匯入資源的處理,特別強調維持零漂移轉換,並針對 ForceNew 屬性、資源標籤及已計算的唯讀狀態屬性提供明確的防護與清理指南。此技能也提供處理 Terraform 計劃輸出的詳細範本,確保對所有增減變更進行結構化審查與記錄。

相容平台~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/daily-dreamer/terraform-standardize/tree/HEAD/skills/terraform-standardize

Installed? Explore more 程式設計與開發 skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

在你喜歡的 AI 中提問

開啟一個已預先載入此 Agent Skill 的新對話。

說明文件

Terraform 標準化技能 (Terraform Standardization Skill)

此技能引導代理將原始、棕地、匯入(例如來自 Terraformer 或 terraform import)、或舊版的 Terraform 程式碼重構為模組化、符合生產等級的 HashiCorp 標準架構。

使用時機 (When to Use This Skill)

當以下情況發生時,請啟用此技能:

  • 使用者要求「標準化」、「重構」、「清理」或「模組化」Terraform 配置。
  • 原始 Terraform 程式碼包含硬編碼字串、魔術數字或靜態雲端 ID(ami-*、vpc-*、subnet-*、sg-*、arn:*)。
  • 單一檔案的配置(所有東西都在 main.tf)需要分離成標準的多檔案佈局(provider.tf、variables.tf、locals.tf、outputs.tf、backend.tf)。
  • 雲端資源需要標準化的標籤分類法(例如 Environment、Project、ManagedBy = "terraform")。
  • 程式碼庫缺少變數定義、型別約束或描述區塊。

標準化工作流程 (Standardization Workflow)

標準化任何 Terraform 程式碼庫時,請遵循以下 5 個階段程序:

┌────────────────────────────────────────────────────────┐
│ 第1階段:稽核與識別程式碼異味 (Audit & Identify        │
│ Code Smells)                                           │
│ • 偵測硬編碼的文字值、ID、區域和 CIDR                 │
│ • 檢查現有檔案結構與標準佈局之間的差異                 │
└──────────────────────────┬─────────────────────────────┘
                           │
                           ▼
┌────────────────────────────────────────────────────────┐
│ 第2階段:建立標準檔案架構 (Establish Standard File     │
│ Architecture)                                          │
│ • 抽取 provider.tf、versions.tf 和 backend.tf          │
│ • 建立 variables.tf、locals.tf 和 outputs.tf            │
└──────────────────────────┬─────────────────────────────┘
                           │
                           ▼
┌────────────────────────────────────────────────────────┐
│ 第3階段:將文字參數化為變數與區域變數 (Parameterize    │
│ Literals into Variables & Locals)                      │
│ • 提取帶有型別的環境/區域變數                          │
│ • 為統一的資源標籤建立 local.common_tags                │
│ • 建立 terraform.tfvars.example                        │
└──────────────────────────┬─────────────────────────────┘
                           │
                           ▼
┌────────────────────────────────────────────────────────┐
│ 第4階段:動態參考連結與重新命名 (Dynamic Reference     │
│ Linking & Renaming)                                    │
│ • 以動態參考取代靜態資源 ID                            │
│ • 以資料來源取代硬編碼的 AMI/子網路                    │
│ • 標準化資源標籤(snake_case,不包含型別前綴)         │
└──────────────────────────┬─────────────────────────────┘
                           │
                           ▼
┌────────────────────────────────────────────────────────┐
│ 第5階段:格式化、驗證與確認 (Format, Validate, and    │
│ Verify)                                                │
│ • 執行 `terraform fmt -recursive`                     │
│ • 執行 `terraform validate`                           │
│ • 使用 `terraform plan` 進行驗證(零漂移)            │
└────────────────────────────────────────────────────────┘

逐步操作說明 (Step-by-Step Instructions)

1. 第1階段:稽核與探索 (Phase 1: Audit & Discovery)

在進行編輯前,掃描目標目錄以識別:

  1. CLI 可用性:執行 terraform version(對 OpenTofu 則執行 tofu version)來判斷是否能在第5階段執行 CLI 格式化、驗證與計劃檢查。
  2. 缺少標準檔案:檢查 versions.tf、provider.tf、variables.tf、locals.tf 與 outputs.tf 是否存在。
  3. 硬編碼 ID 與文字值:
    • 雲端識別碼:ami-[a-f0-9]+、vpc-[a-f0-9]+、subnet-[a-f0-9]+、sg-[a-f0-9]+、igw-[a-f0-9]+、完整的 GCP 自我連結(https://www.googleapis.com/compute/...)、靜態 project ID
    • CIDR 區塊:"10.0.0.0/16"、"0.0.0.0/0"
    • 執行個體類型:"t3.micro"、"m5.large"、"e2-standard-4"
    • 靜態區域與帳號:"us-east-1"、"us-central1"、"123456789012"
    • 原始憑證或未加密的機密資訊
  4. 格式化基準:若已安裝 Terraform,檢查檔案是否符合 terraform fmt -check -recursive。

2. 第2階段:建立標準核心檔案 (Phase 2: Create Standard Core Files)

確保目標配置已拆分為多個單一職責的檔案:

provider.tf / versions.tf

集中管理 Terraform 設定、最低 CLI 版本與所需的供應商(根據 AWS、GCP 或 Azure 調整):

# versions.tf
terraform {
  required_version = ">= 1.5.0"

  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
    # GCP 範例:
    # google = {
    #   source  = "hashicorp/google"
    #   version = "~> 5.5"
    # }
  }
}

# provider.tf (AWS)
provider "aws" {
  region = var.aws_region

  default_tags {
    tags = local.common_tags
  }
}

# provider.tf (GCP 替代方案)
# provider "google" {
#   project        = var.project_id
#   region         = var.region
#   default_labels = local.common_labels
# }

單一標記/標籤策略:使用供應商層級的 default_tags / default_labels(僅將資源特有的鍵放在個別資源上)或在每個資源上使用 merge(local.common_tags, ...) / merge(local.common_labels, ...) — 切勿同時使用兩者。

backend.tf(若使用遠端狀態)

分離狀態儲存配置(AWS 用 s3,GCP 用 gcs,Azure 用 azurerm):

terraform {
  backend "s3" {
    bucket         = "myorg-terraform-state"
    key            = "services/app/terraform.tfstate"
    region         = "us-east-1"
    dynamodb_table = "terraform-locks"
    encrypt        = true
  }
}

# GCP 範例:
# terraform {
#   backend "gcs" {
#     bucket = "myorg-terraform-state"
#     prefix = "services/app"
#   }
# }

3. 第3階段:將純文字字串轉換為變數與區域變數 (Phase 3: Convert Plain Strings to Variables and Locals)

將所有硬編碼參數抽取到 variables.tf 與 locals.tf 中:

variables.tf 規則:

  • 始終宣告 type:(string、number、bool、list(string)、map(string))。
  • 始終包含 description:說明用途與預期格式。
  • 僅在合理時提供 default:像 environment 或 aws_region 這類設定選項可以有預設值,但敏感值不應有硬編碼的預設值。
  • 將機密標記為 sensitive = true:密碼、Token 與機密變數必須標記為敏感。

variables.tf 範例:

variable "aws_region" {
  type        = string
  description = "將佈建資源的 AWS 區域。"
  default     = "us-east-1"
}

variable "environment" {
  type        = string
  description = "部署環境名稱(例如 dev、staging、prod)。"
  default     = "production"

  validation {
    condition     = contains(["dev", "staging", "production"], var.environment)
    error_message = "環境必須是以下之一:dev、staging、production。"
  }
}

variable "instance_type" {
  type        = string
  description = "應用程式叢集的 EC2 執行個體大小。"
  default     = "t3.medium"
}

variable "vpc_cidr" {
  type        = string
  description = "虛擬私有雲端的基礎 CIDR 區塊。"
  default     = "10.0.0.0/16"
}

locals.tf 規則:

使用 locals 定義內部衍生值、環境前綴與通用標籤:

locals {
  name_prefix = "${var.project_name}-${var.environment}"

  common_tags = {
    Project     = var.project_name
    Environment = var.environment
    ManagedBy   = "terraform"
  }
}

terraform.tfvars.example

始終建立一個範例變數值檔案,讓團隊成員知道如何提供值:

aws_region   = "us-east-1"
environment  = "production"
project_name = "checkout-service"

4. 第4階段:以動態參考取代靜態值(零漂移防護)(Phase 4: Replace Static Values with Dynamic References (Zero-Drift Guardrails))

將靜態雲端 ID 與不相關的資源參考轉換為原生的 HCL 表達式,同時保護現有基礎設施不被意外取代:

不可變性與 ForceNew 防護(對棕地 / Terraformer 至關重要)

  • 區分 Terraform 位址與雲端 name 屬性:使用 moved {} 區塊重新命名 Terraform 資源標籤(tfer--prod-main-vpc → main)是安全的且停機時間為零。更改雲端 name 屬性(例如 google_compute_network.name、google_compute_subnetwork.name、google_compute_firewall.name、google_compute_instance.name 或 aws_s3_bucket.bucket)是**ForceNew** 操作,會銷毀並重建現有基礎設施。應在 variables.tf / terraform.tfvars 中將現有的雲端名稱參數化,而不是將它們改寫為 "${local.name_prefix}-..."。
  • 在棕地匯入時鎖定已匯入的映像/AMI:將硬編碼的 ami-* 或 debian-12-bookworm-v20240515 切換為 most_recent = true / family data 查詢,若存在較新映像,將會取代現有的 VM。當需要零漂移時,請透過變數/data 過濾器鎖定確切的匯入映像/AMI 名稱或 ID,且僅在使用者選擇升級映像時才啟用 most_recent = true。
  • 將安全性強化與零漂移重構分開:不要在零漂移遷移期間悄悄地更改行為/安全屬性(例如 http_tokens "optional" → "required"、public_access_prevention "inherited" → "enforced" 或 VM OAuth scopes)。將強化視為明確的選擇加入,或明確列出每一個有意的差異。
  • 遷移時保留舊版標籤/標籤鍵:如果現有資源使用 env = "prod",則在批准取代鍵之前,請保留現有鍵(或在它們旁邊添加標準化鍵)。

Terraformer 特定清理檢查清單

  • 移除已計算的唯讀狀態屬性(AWS 上的 tags_all;GCP 上的 effective_labels 與 terraform_labels)以及空的/預設屬性(""、[]、{}、false、0)。
  • 移除衝突的狀態傾印子區塊(例如當設定了 subnet_id 時,EC2 主要的 network_interface)。
  • 將舊式的 ${data.terraform_remote_state.*.outputs.*} 內插與完整的自我連結 URL 替換為直接的資源參考(例如 google_compute_network.main.id)或 data 查詢。
  • 將編號的重複資源(vm-01、vm-02)折疊為單一 for_each 資源,並搭配 moved 區塊(to = google_compute_instance.api["01"])。
  • 將內嵌的 \n 跳脫字元 user_data / startup-script 字串提取到 scripts/ 檔案中,並透過 file() 或 templatefile() 載入。
  • 在變更 GCP VM 的 scopes 或 machine_type 時,如果打算就地更新,請確保設定了 allow_stopping_for_update = true。

反模式(原始 / 硬編碼):

# 錯誤:硬編碼 ID 與文字字串
resource "aws_instance" "tfer--i-0123456789abcdef0" {
  ami           = "ami-0c55b159cbfafe1f0"
  instance_type = "t3.medium"
  subnet_id     = "subnet-01234567"
  vpc_security_group_ids = [
    "sg-0abcdef1234567890"
  ]
}

標準模式(重構後):

# 1. 狀態保留:告訴 Terraform 此資源已被重新命名(防止銷毀與重建)
moved {
  from = aws_instance.tfer--i-0123456789abcdef0
  to   = aws_instance.web
}

# 2. 基礎映像的動態查詢(為保持零漂移棕地鎖定確切名稱,或為綠地使用 most_recent)
data "aws_ami" "ubuntu" {
  most_recent = true
  owners      = ["099720109477"] # Canonical

  filter {
    name   = "name"
    values = [var.ami_name_pattern]
  }
}

# 3. 帶有動態參考的標準化資源定義
resource "aws_instance" "web" {
  ami                    = data.aws_ami.ubuntu.id
  instance_type          = var.instance_type
  subnet_id              = aws_subnet.private[0].id
  vpc_security_group_ids = [aws_security_group.web.id]

  # 如果 provider.tf 已經設定了 default_tags,則在此省略 common_tags
  tags = {
    Name = var.instance_name
  }
}

5. 第5階段:格式化、漂移稽核與驗證 (Phase 5: Formatting, Drift Audit, and Validation)

  1. 計劃前漂移稽核檢查清單(始終執行,即使本機未安裝 terraform CLI 亦然):

    • 將每個資源的雲端 name、image/ami、location/zone、tags/labels 以及安全/網路設定與匯入來源進行比較。
    • 驗證沒有意外變更任何 ForceNew 欄位,且每個 tfer--* 資源都有一個對應的 moved {} 區塊。
    • 將任何有意的差異(例如安全性強化)與零漂移重構分開並報告。
  2. 執行 Terraform CLI 檢查(已安裝時):

# 1. 格式化程式碼為標準的 HCL 縮排
terraform fmt -recursive

# 2. 初始化供應商外掛程式(離線/無狀態驗證時使用 -backend=false)並驗證語法
terraform init -backend=false
terraform validate

# 3. 若在具有狀態的活躍環境中工作,請確保計劃不會產生非預期的差異
terraform plan
  1. 摘要 terraform plan 變更(偵測到任何差異時): 如果 terraform plan 回報的內容不是 No changes,請在建議 apply 之前停止,並提出結構化摘要:
    • 計劃總計:說明確切的資源計數(X 個新增,Y 個變更,Z 個銷毀)以及任何 moved 狀態轉換。
    • 先列出高風險變更(-/+ 取代,- 銷毀):突出顯示每個標記為取代或刪除的資源,指出導致該操作的 ForceNew 屬性(例如 name、image、boot_disk),並說明如何修復(例如新增缺少的 moved {} 區塊,或在 tfvars 中還原原始值)。
    • 就地更新(~)與建立(+):列出每個受影響的資源以及正在變更的確切屬性(例如 labels、tags、scopes、public_access_prevention)。
    • 有意的差異 vs. 非預期的漂移:清楚標記哪些差異是有意的(選擇加入的安全性強化或標籤標準化),哪些是非預期的棕地漂移,以便使用者批准或還原。

詳細指南與參考資料 (Detailed Guidelines & References)

  • 有關領域分組模式,請參閱標準檔案佈局。
  • 有關命名、ForceNew 防護、標籤/標籤以及反模式檢查清單,請參閱重構規則。

相關技能