$npx -y skills add hashicorp/agent-skills --skill refactor-modulerefactor-module is an agent skill published from hashicorp/agent-skills, installable through the skills CLI.
| 1 | # Skill: Refactor Module |
| 2 | |
| 3 | ## Overview |
| 4 | This skill guides AI agents in transforming monolithic Terraform configurations into reusable, maintainable modules following HashiCorp's module design principles and community best practices. |
| 5 | |
| 6 | ## Capability Statement |
| 7 | The agent will analyze existing Terraform code and systematically refactor it into well-structured modules with: |
| 8 | - Clear interface contracts (variables and outputs) |
| 9 | - Proper encapsulation and abstraction |
| 10 | - Versioning and documentation |
| 11 | - Testing frameworks |
| 12 | - Migration path for existing state |
| 13 | |
| 14 | ## Prerequisites |
| 15 | - Existing Terraform configuration to refactor |
| 16 | - Understanding of resource dependencies |
| 17 | - Access to current state file (for migration planning) |
| 18 | - Knowledge of module registry patterns |
| 19 | |
| 20 | ## Input Parameters |
| 21 | |
| 22 | | Parameter | Type | Required | Description | |
| 23 | |-----------|------|----------|-------------| |
| 24 | | `source_directory` | string | Yes | Path to existing Terraform configuration | |
| 25 | | `module_name` | string | Yes | Name for the new module | |
| 26 | | `abstraction_level` | string | No | "simple", "intermediate", "advanced" (default: intermediate) | |
| 27 | | `preserve_state` | boolean | Yes | Whether to maintain state compatibility | |
| 28 | | `target_registry` | string | No | Target module registry (local, private, public) | |
| 29 | |
| 30 | ## Execution Steps |
| 31 | |
| 32 | ### 1. Analysis Phase |
| 33 | ```markdown |
| 34 | **Identify Refactoring Candidates** |
| 35 | - Group resources by logical function |
| 36 | - Identify repeated patterns |
| 37 | - Map resource dependencies |
| 38 | - Detect configuration coupling |
| 39 | - Analyze variable usage patterns |
| 40 | |
| 41 | **Complexity Assessment** |
| 42 | - Count resource relationships |
| 43 | - Measure variable propagation depth |
| 44 | - Identify cross-resource references |
| 45 | - Evaluate state migration complexity |
| 46 | ``` |
| 47 | |
| 48 | ### 2. Module Design |
| 49 | |
| 50 | #### Interface Design |
| 51 | ```hcl |
| 52 | # Define clear input contract |
| 53 | variable "network_config" { |
| 54 | description = "Network configuration parameters" |
| 55 | type = object({ |
| 56 | cidr_block = string |
| 57 | availability_zones = list(string) |
| 58 | enable_nat = bool |
| 59 | }) |
| 60 | |
| 61 | validation { |
| 62 | condition = can(cidrhost(var.network_config.cidr_block, 0)) |
| 63 | error_message = "CIDR block must be valid IPv4 CIDR." |
| 64 | } |
| 65 | } |
| 66 | |
| 67 | # Define output contract |
| 68 | output "vpc_id" { |
| 69 | description = "ID of the created VPC" |
| 70 | value = aws_vpc.main.id |
| 71 | } |
| 72 | |
| 73 | output "private_subnet_ids" { |
| 74 | description = "List of private subnet IDs" |
| 75 | value = { for k, v in aws_subnet.private : k => v.id } |
| 76 | } |
| 77 | ``` |
| 78 | |
| 79 | #### Encapsulation Strategy |
| 80 | ```markdown |
| 81 | **What to Include in Module:** |
| 82 | - Tightly coupled resources (VPC + subnets) |
| 83 | - Resources with shared lifecycle |
| 84 | - Configuration with clear boundaries |
| 85 | |
| 86 | **What to Keep Separate:** |
| 87 | - Cross-cutting concerns (monitoring, tagging) |
| 88 | - Resources with different lifecycles |
| 89 | - Provider-specific configurations |
| 90 | ``` |
| 91 | |
| 92 | ### 3. Code Transformation |
| 93 | |
| 94 | #### Before: Monolithic Configuration |
| 95 | ```hcl |
| 96 | # main.tf (monolithic) |
| 97 | resource "aws_vpc" "main" { |
| 98 | cidr_block = "10.0.0.0/16" |
| 99 | enable_dns_hostnames = true |
| 100 | |
| 101 | tags = { |
| 102 | Name = "production-vpc" |
| 103 | Environment = "prod" |
| 104 | } |
| 105 | } |
| 106 | |
| 107 | resource "aws_subnet" "public_1" { |
| 108 | vpc_id = aws_vpc.main.id |
| 109 | cidr_block = "10.0.1.0/24" |
| 110 | availability_zone = "us-east-1a" |
| 111 | |
| 112 | tags = { |
| 113 | Name = "public-subnet-1" |
| 114 | Type = "public" |
| 115 | } |
| 116 | } |
| 117 | |
| 118 | resource "aws_subnet" "public_2" { |
| 119 | vpc_id = aws_vpc.main.id |
| 120 | cidr_block = "10.0.2.0/24" |
| 121 | availability_zone = "us-east-1b" |
| 122 | |
| 123 | tags = { |
| 124 | Name = "public-subnet-2" |
| 125 | Type = "public" |
| 126 | } |
| 127 | } |
| 128 | |
| 129 | resource "aws_internet_gateway" "main" { |
| 130 | vpc_id = aws_vpc.main.id |
| 131 | |
| 132 | tags = { |
| 133 | Name = "production-igw" |
| 134 | } |
| 135 | } |
| 136 | |
| 137 | # ... more repetitive subnet and routing resources |
| 138 | ``` |
| 139 | |
| 140 | #### After: Modular Structure |
| 141 | ```hcl |
| 142 | # modules/vpc/main.tf |
| 143 | locals { |
| 144 | subnet_count = length(var.availability_zones) |
| 145 | } |
| 146 | |
| 147 | resource "aws_vpc" "main" { |
| 148 | cidr_block = var.cidr_block |
| 149 | enable_dns_hostnames = var.enable_dns_hostnames |
| 150 | enable_dns_support = var.enable_dns_support |
| 151 | |
| 152 | tags = merge( |
| 153 | var.tags, |
| 154 | { |
| 155 | Name = var.name |
| 156 | } |
| 157 | ) |
| 158 | } |
| 159 | |
| 160 | resource "aws_subnet" "public" { |
| 161 | for_each = var.create_public_subnets ? toset(var.availability_zones) : [] |
| 162 | |
| 163 | vpc_id = aws_vpc.main.id |
| 164 | cidr_block = cidrsubnet(var.cidr_block, 8, index(var.availability_zones, each.value)) |
| 165 | availability_zone = each.value |
| 166 | map_public_ip_on_launch = true |
| 167 | |
| 168 | tags = merge( |
| 169 | var.tags, |
| 170 | { |
| 171 | Name = "${var.name}-public-${each.value}" |
| 172 | Type = "public" |
| 173 | } |
| 174 | ) |
| 175 | } |
| 176 | |
| 177 | resource "aws_internet_gateway" "main" { |
| 178 | count = var.create_public_subnets ? 1 : 0 |
| 179 | vpc_id = aws_vpc.main.id |
| 180 | |
| 181 | tags = merge( |
| 182 | var.tags, |
| 183 | { |
| 184 | Name = "${var.name}-igw" |
| 185 | } |
| 186 | ) |
| 187 | } |
| 188 | |
| 189 | # modules/vpc/variables.tf |
| 190 | variable "name" { |
| 191 | description = "Name prefix for all resources" |
| 192 | type = string |
| 193 | } |
| 194 | |
| 195 | variable "cidr_block" { |
| 196 | descr |