refactor-module by hashicorp/agent-skills
npx skills add https://github.com/hashicorp/agent-skills --skill refactor-module此技能指导 AI 代理遵循 HashiCorp 的模块设计原则和社区最佳实践,将单体 Terraform 配置转换为可重用、可维护的模块。
代理将分析现有的 Terraform 代码,并系统地将其重构为结构良好的模块,具备:
| 参数 | 类型 | 必需 | 描述 |
|---|---|---|---|
source_directory | string | 是 | 现有 Terraform 配置的路径 |
module_name | string | 是 |
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
| 新模块的名称 |
abstraction_level | string | 否 | "simple"、"intermediate"、"advanced"(默认:intermediate) |
preserve_state | boolean | 是 | 是否保持状态兼容性 |
target_registry | string | 否 | 目标模块注册表(local、private、public) |
识别重构候选对象
复杂性评估
# 定义清晰的输入契约
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."
}
}
# 定义输出契约
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 }
}
模块中应包含的内容:
应保持独立的内容:
# 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"
}
}
# 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]
}
# 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]'
# VPC Module
## 概述
跨多个可用区创建具有可配置公共和私有子网的 VPC。
## 特性
- 多可用区子网部署
- 可选的 NAT 网关配置
- VPC 流日志集成
- 可自定义的 CIDR 分配
## 用法
```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"
}
}
| 名称 | 版本 |
|---|---|
| terraform | >= 1.5.0 |
| aws | ~> 5.0 |
| 名称 | 描述 | 类型 | 默认值 | 必需 |
|---|---|---|---|---|
| name | 资源名称前缀 | string | n/a | 是 |
| cidr_block | VPC CIDR 块 | string | n/a | 是 |
| availability_zones | 可用区列表 | list(string) | n/a | 是 |
| 名称 | 描述 |
|---|---|
| vpc_id | VPC 标识符 |
| public_subnet_ids | 公共子网 ID 映射 |
| private_subnet_ids | 私有子网 ID 映射 |
查看 examples/ 目录以获取完整用法示例。
### 6. 测试
使用技能 terraform-test
**测试文件**:包含测试配置和运行块的 `.tftest.hcl` 或 `.tftest.json` 文件,用于验证您的 Terraform 配置。
**测试块**:定义测试范围设置的可选配置块(自 Terraform 1.6.0 起可用)。
**运行块**:定义单个测试场景,包含可选的变量、提供程序配置和断言。每个测试文件至少需要一个运行块。
**断言块**:包含必须评估为真才能使测试通过的条件。失败的断言会导致测试失败。
**模拟提供程序**:模拟提供程序行为而不创建真实基础设施(自 Terraform 1.7.0 起可用)。
**测试模式**:测试在应用模式(默认,创建真实基础设施)或计划模式(验证逻辑而不创建资源)下运行。
#### 文件结构
Terraform 测试文件使用 `.tftest.hcl` 或 `.tftest.json` 扩展名,通常组织在 `tests/` 目录中。使用清晰的命名约定来区分单元测试(计划模式)和集成测试(应用模式):
my-module/ ├── main.tf ├── variables.tf ├── outputs.tf └── tests/ ├── unit_test.tftest.hcl # 单元测试 (plan mode) └── integration_test.tftest.hcl # 集成测试 (apply mode - creates real resources)
## 重构模式
### 模式 1:资源分组
将相关资源提取到内聚的模块中:
* 网络(VPC、子网、路由表)
* 计算(ASG、启动模板、负载均衡器)
* 数据(RDS、ElastiCache、S3)
### 模式 2:配置分层
```hcl
# 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
}
# 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
}
# ❌ 不要创建过于通用的模块
variable "resources" {
type = map(map(any)) # Too flexible, hard to validate
}
# ✅ 使用特定的、类型化的接口
variable "database_config" {
type = object({
engine = string
instance_class = string
})
}
# ❌ 不要通过直接引用耦合模块
# 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
}
# ✅ 通过根模块传递依赖关系
module "compute" {
source = "./modules/compute"
}
resource "aws_eip" "app" {
instance = module.compute.instance_id
}
始终先在非生产环境中测试迁移:
# 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
| 版本 | 日期 | 变更 |
|---|---|---|
| 1.0.0 | 2025-11-07 | 初始技能定义 |
每周安装量
973
仓库
GitHub 星标数
477
首次出现
Jan 26, 2026
安全审计
安装于
github-copilot772
opencode764
codex747
gemini-cli739
kimi-cli636
amp636
This skill guides AI agents in transforming monolithic Terraform configurations into reusable, maintainable modules following HashiCorp's module design principles and community best practices.
The agent will analyze existing Terraform code and systematically refactor it into well-structured modules with:
| Parameter | Type | Required | Description |
|---|---|---|---|
source_directory | string | Yes | Path to existing Terraform configuration |
module_name | string | Yes | Name for the new module |
abstraction_level | string | No | "simple", "intermediate", "advanced" (default: intermediate) |
preserve_state | boolean | Yes | Whether to maintain state compatibility |
target_registry | string | No | Target module registry (local, private, public) |
**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
# 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
# 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"
}
}
# 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]
}
# 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]'
# 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.
Use skill terraform-test
Test File : A .tftest.hcl or .tftest.json file containing test configuration and run blocks that validate your Terraform configuration.
Test Block : Optional configuration block that defines test-wide settings (available since Terraform 1.6.0).
Run Block : Defines a single test scenario with optional variables, provider configurations, and assertions. Each test file requires at least one run block.
Assert Block : Contains conditions that must evaluate to true for the test to pass. Failed assertions cause the test to fail.
Mock Provider : Simulates provider behavior without creating real infrastructure (available since Terraform 1.7.0).
Test Modes : Tests run in apply mode (default, creates real infrastructure) or plan mode (validates logic without creating resources).
Terraform test files use the .tftest.hcl or .tftest.json extension and are typically organized in a tests/ directory. Use clear naming conventions to distinguish between unit tests (plan mode) and integration tests (apply mode):
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)
Extract related resources into cohesive modules:
# 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
}
# 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
}
# ❌ 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
})
}
# ❌ 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
}
Always test migration in non-production first:
# 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
| Version | Date | Changes |
|---|---|---|
| 1.0.0 | 2025-11-07 | Initial skill definition |
Weekly Installs
973
Repository
GitHub Stars
477
First Seen
Jan 26, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
github-copilot772
opencode764
codex747
gemini-cli739
kimi-cli636
amp636
Azure 升级评估与自动化工具 - 轻松迁移 Functions 计划、托管层级和 SKU
59,200 周安装