关于 refactor-module
refactor-module 是一项指导 AI Agent 将单体 Terraform 配置拆分为可复用模块的技能,涵盖模块接口设计、代码重构、文档、测试和状态迁移规划。
主要功能
- 01分析资源与依赖关系
- 02设计模块变量和输出
- 03将资源分组为模块
- 04规划状态迁移
- 05编写模块文档和测试
- 06检查迁移计划
适用场景
- 01将单体配置拆分为模块
- 02重构时保留资源状态
- 03为不同环境准备可复用模块
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ hashicorp/ agent-skills --skill refactor-module安装后在 Claude Code 中输入/refactor-module确认已加载。
详细介绍
技能:重构模块
概述
本技能将指导 AI Agent 按照 HashiCorp 的模块设计原则和社区最佳实践,将单体 Terraform 配置转换为可复用、易维护的模块。
能力说明
Agent 将分析现有 Terraform 代码,并系统地将其重构为结构良好的模块,其中包含:
- 清晰的接口契约(变量和输出)
- 恰当的封装和抽象
- 版本管理和文档
- 测试框架
- 现有状态的迁移路径
先决条件
- 要重构的现有 Terraform 配置
- 对资源依赖关系的了解
- 可通过
terraform state list/terraform show -json检查当前状态(用于规划迁移) - 对模块注册表模式的了解
输入参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
source_directory | string | 是 | 现有 Terraform 配置的路径 |
module_name | string | 是 | 新模块的名称 |
abstraction_level | string | 否 | "simple"、"intermediate"、"advanced"(默认值:intermediate) |
preserve_state | boolean | 是 | 是否保持状态兼容性 |
target_registry | string | 否 | 目标模块注册表(local、private、public) |
执行步骤
1. 分析阶段
**Identify Refactoring Candidates**
- Group resources by logical function
- Identify repeated patterns
- Map resource dependencies
- Detect configuration coupling
- Analyze variable usage patterns
**Complexity Assessment**
- Count resource relationships
- Measure variable propagation depth
- Identify cross-resource references
- Evaluate state migration complexity
2. 模块设计
接口设计
# Define clear input contract
variable "network_config" {
description = "Network configuration parameters"
type = object({
cidr_block = string
availability_zones = list(string)
enable_nat = bool
})
validation {
condition = can(cidrhost(var.network_config.cidr_block, 0))
error_message = "CIDR block must be valid IPv4 CIDR."
}
}
# Define output contract
output "vpc_id" {
description = "ID of the created VPC"
value = aws_vpc.main.id
}
output "private_subnet_ids" {
description = "List of private subnet IDs"
value = { for k, v in aws_subnet.private : k => v.id }
}
封装策略
**What to Include in Module:**
- Tightly coupled resources (VPC + subnets)
- Resources with shared lifecycle
- Configuration with clear boundaries
**What to Keep Separate:**
- Cross-cutting concerns (monitoring, tagging)
- Resources with different lifecycles
- Provider-specific configurations
3. 代码转换
转换前:单体配置
# main.tf (monolithic)
resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
enable_dns_hostnames = true
tags = {
Name = "production-vpc"
Environment = "prod"
}
}
resource "aws_subnet" "public_1" {
vpc_id = aws_vpc.main.id
cidr_block = "10.0.1.0/24"
availability_zone = "us-east-1a"
tags = {
Name = "public-subnet-1"
Type = "public"
}
}
resource "aws_subnet" "public_2" {
vpc_id = aws_vpc.main.id
cidr_block = "10.0.2.0/24"
availability_zone = "us-east-1b"
tags = {
Name = "public-subnet-2"
Type = "public"
}
}
resource "aws_internet_gateway" "main" {
vpc_id = aws_vpc.main.id
tags = {
Name = "production-igw"
}
}
# ... more repetitive subnet and routing resources
转换后:模块化结构
# modules/vpc/main.tf
locals {
subnet_count = length(var.availability_zones)
}
resource "aws_vpc" "main" {
cidr_block = var.cidr_block
enable_dns_hostnames = var.enable_dns_hostnames
enable_dns_support = var.enable_dns_support
tags = merge(
var.tags,
{
Name = var.name
}
)
}
resource "aws_subnet" "public" {
for_each = var.create_public_subnets ? toset(var.availability_zones) : []
vpc_id = aws_vpc.main.id
cidr_block = cidrsubnet(var.cidr_block, 8, index(var.availability_zones, each.value))
availability_zone = each.value
map_public_ip_on_launch = true
tags = merge(
var.tags,
{
Name = "${var.name}-public-${each.value}"
Type = "public"
}
)
}
resource "aws_internet_gateway" "main" {
count = var.create_public_subnets ? 1 : 0
vpc_id = aws_vpc.main.id
tags = merge(
var.tags,
{
Name = "${var.name}-igw"
}
)
}
# modules/vpc/variables.tf
variable "name" {
description = "Name prefix for all resources"
type = string
}
variable "cidr_block" {
description = "CIDR block for the VPC"
type = string
validation {
condition = can(cidrhost(var.cidr_block, 0))
error_message = "Must be a valid IPv4 CIDR block."
}
}
variable "availability_zones" {
description = "List of availability zones"
type = list(string)
}
variable "create_public_subnets" {
description = "Whether to create public subnets"
type = bool
default = true
}
variable "enable_dns_hostnames" {
description = "Enable DNS hostnames in the VPC"
type = bool
default = true
}
variable "enable_dns_support" {
description = "Enable DNS support in the VPC"
type = bool
default = true
}
variable "tags" {
description = "Tags to apply to all resources"
type = map(string)
default = {}
}
# modules/vpc/outputs.tf
output "vpc_id" {
description = "ID of the VPC"
value = aws_vpc.main.id
}
output "vpc_cidr_block" {
description = "CIDR block of the VPC"
value = aws_vpc.main.cidr_block
}
output "public_subnet_ids" {
description = "Map of availability zones to public subnet IDs"
value = { for k, v in aws_subnet.public : k => v.id }
}
output "internet_gateway_id" {
description = "ID of the internet gateway"
value = try(aws_internet_gateway.main[0].id, null)
}
# Root configuration using module
module "vpc" {
source = "./modules/vpc"
name = "production"
cidr_block = "10.0.0.0/16"
availability_zones = ["us-east-1a", "us-east-1b", "us-east-1c"]
tags = {
Environment = "production"
ManagedBy = "Terraform"
}
}
4. 状态迁移
检查当前状态
在编写 moved 块或 state mv 命令之前,请检查当前状态以映射现有资源地址。优先使用有文档说明、稳定且更节省 Token 的命令,而不是读取原始状态文件:
# Enumerate current resource addresses (the inputs for `moved` / `state mv`)
terraform state list
# Inspect resolved attribute values when you need them
terraform show -json | jq '.values.root_module'
terraform show -json 需要安装 provider(terraform init),因为它会根据 provider 架构呈现值。仅当 provider 不可用且无法运行 init、只需要粗略信息(地址、输出、serial/lineage),或者必须避免执行 Terraform 时,才回退到原始状态(terraform state pull / terraform.tfstate)。避免将原始的第 4 版状态格式解析为稳定接口。**注意:**所有格式的状态都以明文包含敏感值;切勿将状态内容输出到日志或其他输出中。
生成迁移计划
# migration.tf
# Use moved blocks for state refactoring (Terraform 1.1+)
moved {
from = aws_vpc.main
to = module.vpc.aws_vpc.main
}
moved {
from = aws_subnet.public_1
to = module.vpc.aws_subnet.public["us-east-1a"]
}
moved {
from = aws_subnet.public_2
to = module.vpc.aws_subnet.public["us-east-1b"]
}
moved {
from = aws_internet_gateway.main
to = module.vpc.aws_internet_gateway.main[0]
}
手动迁移状态(Terraform 1.1 之前)
# Generate state migration commands
terraform state mv aws_vpc.main module.vpc.aws_vpc.main
terraform state mv aws_subnet.public_1 'module.vpc.aws_subnet.public["us-east-1a"]'
terraform state mv aws_subnet.public_2 'module.vpc.aws_subnet.public["us-east-1b"]'
terraform state mv aws_internet_gateway.main 'module.vpc.aws_internet_gateway.main[0]'
5. 模块文档
# VPC Module
## Overview
Creates a VPC with configurable public and private subnets across multiple availability zones.
## Features
- Multi-AZ subnet deployment
- Optional NAT gateway configuration
- VPC Flow Logs integration
- Customizable CIDR allocation
## Usage
\`\`\`hcl
module "vpc" {
source = "./modules/vpc"
name = "my-vpc"
cidr_block = "10.0.0.0/16"
availability_zones = ["us-east-1a", "us-east-1b"]
create_public_subnets = true
create_private_subnets = true
enable_nat_gateway = true
tags = {
Environment = "production"
}
}
\`\`\`
## Requirements
| Name | Version |
|------|---------|
| terraform | >= 1.5.0 |
| aws | ~> 5.0 |
## Inputs
| Name | Description | Type | Default | Required |
|------|-------------|------|---------|----------|
| name | Name prefix for resources | `string` | n/a | yes |
| cidr_block | VPC CIDR block | `string` | n/a | yes |
| availability_zones | List of AZs | `list(string)` | n/a | yes |
## Outputs
| Name | Description |
|------|-------------|
| vpc_id | VPC identifier |
| public_subnet_ids | Map of public subnet IDs |
| private_subnet_ids | Map of private subnet IDs |
## Examples
See [examples/](./examples/) directory for complete usage examples.
6. 测试
使用技能 terraform-test
测试文件:包含测试配置和 run 块的 .tftest.hcl 或 .tftest.json 文件,用于验证 Terraform 配置。
Test 块:用于定义测试级设置的可选配置块(Terraform 1.6.0 起提供)。
Run 块:定义单个测试场景,可包含可选变量、provider 配置和断言。每个测试文件至少需要一个 run 块。
Assert 块:包含必须计算为 true 才能通过测试的条件。断言失败会导致测试失败。
Mock Provider:模拟 provider 行为,但不会创建真实基础设施(Terraform 1.7.0 起提供)。
测试模式:测试以 apply 模式(默认,会创建真实基础设施)或 plan 模式(验证逻辑但不创建资源)运行。
文件结构
Terraform 测试文件使用 .tftest.hcl 或 .tftest.json 扩展名,通常放在 tests/ 目录中。采用清晰的命名约定,以区分单元测试(plan 模式)和集成测试(apply 模式):
my-module/
├── main.tf
├── variables.tf
├── outputs.tf
└── tests/
├── unit_test.tftest.hcl # Unit test (plan mode)
└── integration_test.tftest.hcl # Integration test (apply mode - creates real resources)
重构模式
模式 1:资源分组
将相关资源提取到内聚的模块中:
- 网络(VPC、子网、路由表)
- 计算(ASG、启动模板、负载均衡器)
- 数据(RDS、ElastiCache、S3)
模式 2:配置分层
# Base module with defaults
module "vpc_base" {
source = "./modules/vpc-base"
# Minimal required inputs
}
# Environment-specific wrapper
module "vpc_prod" {
source = "./modules/vpc-production"
# Inherits from base, adds prod-specific config
}
模式 3:组合
# Small, focused modules
module "vpc" {
source = "./modules/vpc"
}
module "security_groups" {
source = "./modules/security-groups"
vpc_id = module.vpc.vpc_id
}
module "application" {
source = "./modules/application"
vpc_id = module.vpc.vpc_id
subnet_ids = module.vpc.private_subnet_ids
sg_ids = module.security_groups.app_sg_ids
}
常见陷阱
1. 过度抽象
# ❌ Don't create overly generic modules
variable "resources" {
type = map(map(any)) # Too flexible, hard to validate
}
# ✅ Do use specific, typed interfaces
variable "database_config" {
type = object({
engine = string
instance_class = string
})
}
2. 紧密耦合
# ❌ Don't couple modules through direct references
# module A
output "instance_id" { value = aws_instance.app.id }
# module B (in same config)
resource "aws_eip" "app" {
instance = module.a.instance_id # Tight coupling
}
# ✅ Do pass dependencies through root module
module "compute" {
source = "./modules/compute"
}
resource "aws_eip" "app" {
instance = module.compute.instance_id
}
3. 状态迁移错误
始终先在非生产环境中测试迁移:
# Create plan to verify no changes after migration
terraform plan -out=migration.tfplan
# Review carefully
terraform show migration.tfplan
# Apply only if plan shows no changes
terraform apply migration.tfplan
版本控制策略
# Use semantic versioning for modules
module "vpc" {
source = "git::https://github.com/org/terraform-modules.git//vpc?ref=v1.2.0"
version = "~> 1.2"
}
# Pin to specific versions in production
# Use version ranges in development
成功标准
- 模块具有单一且定义明确的职责
- 所有变量都有描述和类型
- 验证规则可防止无效配置
- 输出为使用者提供足够的信息
- 文档包含使用示例
- 测试可验证模块行为
- 状态迁移已完成,且未重新创建资源
- 重构后计划没有差异
相关技能
- Terraform 代码生成 - 新 Terraform 模块的风格指南
- Azure Verified Modules - Azure 推荐的模块规范
资源
修订历史
| 版本 | 日期 | 变更 |
|---|---|---|
| 1.0.0 | 2025-11-07 | 初始技能定义 |