skills/.curated/terraform-module-scaffold/SKILL.md
Create production-ready, reusable Terraform modules with complete documentation, examples, and tests
npx skillsauth add guicedee/ai-rules terraform-module-scaffoldInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
You are a Terraform module development expert. When this skill is invoked, you help users create complete, production-ready Terraform modules following HashiCorp best practices and community standards.
When a user requests a new Terraform module:
Gather Requirements:
Generate Module Structure:
terraform-{module-name}/
├── main.tf # Main resource definitions
├── variables.tf # Input variable declarations
├── outputs.tf # Output value declarations
├── versions.tf # Terraform and provider version constraints
├── README.md # Complete module documentation
├── examples/
│ └── basic/
│ ├── main.tf # Example usage
│ └── README.md # Example documentation
├── tests/ # Optional: automated tests
│ └── basic_test.go
└── .gitignore
Follow Module Best Practices:
terraform {
required_version = ">= 1.6"
required_providers {
{provider} = {
source = "hashicorp/{provider}"
version = ">= {min_version}"
}
}
}
Follow this pattern for all variables:
variable "name" {
description = "Clear description of what this variable does"
type = string
validation {
condition = length(var.name) >= 3 && length(var.name) <= 24
error_message = "Name must be between 3 and 24 characters."
}
}
variable "location" {
description = "Azure region where resources will be created"
type = string
}
variable "resource_group_name" {
description = "Name of the resource group"
type = string
}
variable "tags" {
description = "Tags to apply to all resources"
type = map(string)
default = {}
}
variable "enable_feature" {
description = "Whether to enable optional feature"
type = bool
default = false
}
Always include:
output "id" {
description = "ID of the {resource}"
value = azurerm_{resource}.main.id
}
output "name" {
description = "Name of the {resource}"
value = azurerm_{resource}.main.name
}
output "resource" {
description = "Full {resource} object for advanced use cases"
value = azurerm_{resource}.main
}
Structure resources logically:
# Local values for computed/combined variables
locals {
# Combine tags with defaults
tags = merge(
{
Module = "terraform-{module-name}"
ManagedBy = "Terraform"
},
var.tags
)
# Resource naming
name = var.name_override != null ? var.name_override : "${var.project}-${var.environment}-${var.name_suffix}"
}
# Main resource(s)
resource "azurerm_{resource}" "main" {
name = local.name
location = var.location
resource_group_name = var.resource_group_name
# Required arguments
required_arg = var.required_arg
# Optional features
dynamic "optional_block" {
for_each = var.enable_feature ? [1] : []
content {
# Block configuration
}
}
tags = local.tags
}
# Supporting resources
resource "azurerm_{supporting_resource}" "support" {
count = var.create_supporting_resource ? 1 : 0
name = "${azurerm_{resource}.main.name}-support"
# ...
}
Create comprehensive documentation:
# Terraform Module: {Module Name}
{Brief description of what this module does}
## Features
- Feature 1
- Feature 2
- Feature 3
## Usage
### Basic Example
```hcl
module "{module_name}" {
source = "path/to/module"
name = "my-resource"
location = "eastus"
resource_group_name = "my-rg"
tags = {
Environment = "Production"
}
}
module "{module_name}" {
source = "path/to/module"
name = "my-resource"
location = "eastus"
resource_group_name = "my-rg"
enable_feature = true
additional_config = "value"
tags = {
Environment = "Production"
}
}
| Name | Version | |------|---------| | terraform | >= 1.6 | | {provider} | >= {version} |
| Name | Version | |------|---------| | {provider} | >= {version} |
| Name | Type | |------|------| | {resource_type}.{name} | resource |
| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| name | Name of the resource | string | n/a | yes |
| location | Azure region | string | n/a | yes |
| tags | Resource tags | map(string) | {} | no |
| Name | Description | |------|-------------| | id | Resource ID | | name | Resource name |
See the examples directory for complete examples.
Contributions are welcome! Please see CONTRIBUTING.md.
[License Type]
### examples/basic/main.tf
```hcl
terraform {
required_version = ">= 1.6"
}
# Example resource group (in real use, this might already exist)
resource "azurerm_resource_group" "example" {
name = "rg-example"
location = "eastus"
}
# Module usage
module "example" {
source = "../.." # Points to root of module
name = "example-resource"
location = azurerm_resource_group.example.location
resource_group_name = azurerm_resource_group.example.name
tags = {
Environment = "Example"
Purpose = "Testing"
}
}
# Outputs to verify
output "resource_id" {
value = module.example.id
}
# Basic Example
This example demonstrates the basic usage of the {module-name} module.
## Usage
1. Update the variables in this file as needed
2. Run:
```bash
terraform init
terraform plan
terraform apply
terraform destroy
## Module Design Principles
### 1. Single Responsibility
Each module should do ONE thing well.
**Good**: `terraform-azurerm-virtual-network` creates a VNet
**Bad**: `terraform-azurerm-infrastructure` creates VNet, VMs, databases, etc.
### 2. Sensible Defaults
Provide defaults for optional variables:
```hcl
variable "enable_https" {
description = "Enable HTTPS"
type = bool
default = true # Secure by default
}
Validate inputs to fail fast:
variable "environment" {
type = string
validation {
condition = contains(["dev", "staging", "prod"], var.environment)
error_message = "Environment must be dev, staging, or prod."
}
}
Use dynamic blocks for optional features:
dynamic "network_rules" {
for_each = var.network_rules != null ? [var.network_rules] : []
content {
default_action = network_rules.value.default_action
# ...
}
}
Users might need any attribute:
output "resource" {
description = "Complete resource object"
value = azurerm_resource.main
}
resource "azurerm_resource" "optional" {
count = var.create_resource ? 1 : 0
# ...
}
output "resource_id" {
value = var.create_resource ? azurerm_resource.optional[0].id : null
}
resource "azurerm_resource" "multiple" {
for_each = var.instances
name = each.key
# Use each.value for configuration
}
resource "azurerm_resource" "main" {
name = var.name
sku_name = var.environment == "prod" ? "Premium" : "Standard"
}
Follow this pattern:
terraform-{provider}-{resource}terraform-azurerm-virtual-networkterraform-aws-vpcterraform-google-compute-networkstorage_account_name not sannetwork_*, security_*enable_*, create_*, use_*If scripts/scaffold-module.js exists, use it:
node scripts/scaffold-module.js \
--name virtual-network \
--provider azurerm \
--description "Create and manage Azure Virtual Networks" \
--output ./modules
For critical modules, add automated tests using Terratest:
package test
import (
"testing"
"github.com/gruntwork-io/terratest/modules/terraform"
)
func TestBasicExample(t *testing.T) {
terraformOptions := &terraform.Options{
TerraformDir: "../examples/basic",
}
defer terraform.Destroy(t, terraformOptions)
terraform.InitAndApply(t, terraformOptions)
// Add assertions here
}
Before publishing a module, ensure:
development
Install Codex skills into $CODEX_HOME/skills from a curated list or a GitHub repo path. Use when a user asks to list installable skills, install a curated skill, or install a skill from another repo (including private repos).
tools
Guide for creating effective skills. This skill should be used when users want to create a new skill (or update an existing skill) that extends Codex's capabilities with specialized knowledge, workflows, or tool integrations.
development
WebAwesome icon integration for JWebMP — modern, open-source icon library. Provides 1,500+ icons with solid/regular styles, sizing, rotation, animation, and CSS utilities. Drop-in FontAwesome alternative with fresh designs. Use when working with WebAwesome icons, modern icon designs, or as FontAwesome alternative in JWebMP applications.
development
WebAwesome Pro integration for JWebMP with premium icons and features. Extends jwebmp-webawesome with additional styles, premium icons, and advanced features. Use when working with WebAwesome Pro icons or premium WebAwesome features in JWebMP applications.