terraform-module-builder

Terraform Module Builder

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "terraform-module-builder" with this command: npx skills add dexploarer/claudius-skills/dexploarer-claudius-skills-terraform-module-builder

Terraform Module Builder

Generates production-ready, reusable Terraform modules with best practices for multi-cloud infrastructure as code.

When to Use

  • "Create Terraform module"

  • "Generate infrastructure module"

  • "Setup Terraform for AWS/Azure/GCP"

  • "Create reusable IaC module"

  • "Generate Terraform boilerplate"

Instructions

  1. Module Structure

terraform-aws-vpc/ ├── main.tf # Main resources ├── variables.tf # Input variables ├── outputs.tf # Output values ├── versions.tf # Provider versions ├── README.md # Documentation ├── examples/ # Usage examples │ └── complete/ │ ├── main.tf │ └── variables.tf └── tests/ # Terratest └── vpc_test.go

  1. AWS VPC Module Example

main.tf:

main.tf

locals { name = var.name != "" ? var.name : "${var.environment}-vpc"

common_tags = merge( var.tags, { Environment = var.environment ManagedBy = "Terraform" Module = "terraform-aws-vpc" } ) }

resource "aws_vpc" "this" { cidr_block = var.vpc_cidr enable_dns_hostnames = var.enable_dns_hostnames enable_dns_support = var.enable_dns_support

tags = merge( local.common_tags, { Name = local.name } ) }

resource "aws_subnet" "public" { count = length(var.public_subnet_cidrs)

vpc_id = aws_vpc.this.id cidr_block = var.public_subnet_cidrs[count.index] availability_zone = var.availability_zones[count.index] map_public_ip_on_launch = true

tags = merge( local.common_tags, { Name = "${local.name}-public-${var.availability_zones[count.index]}" Type = "public" } ) }

resource "aws_subnet" "private" { count = length(var.private_subnet_cidrs)

vpc_id = aws_vpc.this.id cidr_block = var.private_subnet_cidrs[count.index] availability_zone = var.availability_zones[count.index]

tags = merge( local.common_tags, { Name = "${local.name}-private-${var.availability_zones[count.index]}" Type = "private" } ) }

resource "aws_internet_gateway" "this" { count = length(var.public_subnet_cidrs) > 0 ? 1 : 0

vpc_id = aws_vpc.this.id

tags = merge( local.common_tags, { Name = "${local.name}-igw" } ) }

resource "aws_eip" "nat" { count = var.enable_nat_gateway ? length(var.availability_zones) : 0

domain = "vpc"

tags = merge( local.common_tags, { Name = "${local.name}-nat-${var.availability_zones[count.index]}" } )

depends_on = [aws_internet_gateway.this] }

resource "aws_nat_gateway" "this" { count = var.enable_nat_gateway ? length(var.availability_zones) : 0

allocation_id = aws_eip.nat[count.index].id subnet_id = aws_subnet.public[count.index].id

tags = merge( local.common_tags, { Name = "${local.name}-nat-${var.availability_zones[count.index]}" } )

depends_on = [aws_internet_gateway.this] }

resource "aws_route_table" "public" { count = length(var.public_subnet_cidrs) > 0 ? 1 : 0

vpc_id = aws_vpc.this.id

tags = merge( local.common_tags, { Name = "${local.name}-public" } ) }

resource "aws_route" "public_internet_gateway" { count = length(var.public_subnet_cidrs) > 0 ? 1 : 0

route_table_id = aws_route_table.public[0].id destination_cidr_block = "0.0.0.0/0" gateway_id = aws_internet_gateway.this[0].id

timeouts { create = "5m" } }

resource "aws_route_table_association" "public" { count = length(var.public_subnet_cidrs)

subnet_id = aws_subnet.public[count.index].id route_table_id = aws_route_table.public[0].id }

resource "aws_route_table" "private" { count = length(var.private_subnet_cidrs)

vpc_id = aws_vpc.this.id

tags = merge( local.common_tags, { Name = "${local.name}-private-${var.availability_zones[count.index]}" } ) }

resource "aws_route" "private_nat_gateway" { count = var.enable_nat_gateway ? length(var.private_subnet_cidrs) : 0

route_table_id = aws_route_table.private[count.index].id destination_cidr_block = "0.0.0.0/0" nat_gateway_id = aws_nat_gateway.this[count.index].id

timeouts { create = "5m" } }

resource "aws_route_table_association" "private" { count = length(var.private_subnet_cidrs)

subnet_id = aws_subnet.private[count.index].id route_table_id = aws_route_table.private[count.index].id }

resource "aws_flow_log" "this" { count = var.enable_flow_logs ? 1 : 0

iam_role_arn = aws_iam_role.flow_logs[0].arn log_destination = aws_cloudwatch_log_group.flow_logs[0].arn traffic_type = "ALL" vpc_id = aws_vpc.this.id

tags = merge( local.common_tags, { Name = "${local.name}-flow-logs" } ) }

resource "aws_cloudwatch_log_group" "flow_logs" { count = var.enable_flow_logs ? 1 : 0

name = "/aws/vpc/${local.name}" retention_in_days = var.flow_logs_retention_days

tags = local.common_tags }

resource "aws_iam_role" "flow_logs" { count = var.enable_flow_logs ? 1 : 0

name = "${local.name}-flow-logs"

assume_role_policy = jsonencode({ Version = "2012-10-17" Statement = [ { Action = "sts:AssumeRole" Effect = "Allow" Principal = { Service = "vpc-flow-logs.amazonaws.com" } } ] })

tags = local.common_tags }

resource "aws_iam_role_policy" "flow_logs" { count = var.enable_flow_logs ? 1 : 0

name = "flow-logs" role = aws_iam_role.flow_logs[0].id

policy = jsonencode({ Version = "2012-10-17" Statement = [ { Action = [ "logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents", "logs:DescribeLogGroups", "logs:DescribeLogStreams" ] Effect = "Allow" Resource = "*" } ] }) }

variables.tf:

variables.tf

variable "name" { description = "Name to be used on all resources as prefix" type = string default = "" }

variable "environment" { description = "Environment name" type = string validation { condition = contains(["dev", "staging", "production"], var.environment) error_message = "Environment must be dev, staging, or production." } }

variable "vpc_cidr" { description = "CIDR block for VPC" type = string default = "10.0.0.0/16"

validation { condition = can(cidrhost(var.vpc_cidr, 0)) error_message = "VPC CIDR must be a valid IPv4 CIDR block." } }

variable "availability_zones" { description = "List of availability zones" type = list(string) }

variable "public_subnet_cidrs" { description = "CIDR blocks for public subnets" type = list(string) default = [] }

variable "private_subnet_cidrs" { description = "CIDR blocks for private subnets" type = list(string) default = [] }

variable "enable_dns_hostnames" { description = "Enable DNS hostnames in VPC" type = bool default = true }

variable "enable_dns_support" { description = "Enable DNS support in VPC" type = bool default = true }

variable "enable_nat_gateway" { description = "Enable NAT Gateway for private subnets" type = bool default = true }

variable "enable_flow_logs" { description = "Enable VPC Flow Logs" type = bool default = false }

variable "flow_logs_retention_days" { description = "Flow logs retention in days" type = number default = 7 }

variable "tags" { description = "Additional tags for all resources" type = map(string) default = {} }

outputs.tf:

outputs.tf

output "vpc_id" { description = "ID of the VPC" value = aws_vpc.this.id }

output "vpc_cidr" { description = "CIDR block of the VPC" value = aws_vpc.this.cidr_block }

output "public_subnet_ids" { description = "IDs of public subnets" value = aws_subnet.public[*].id }

output "private_subnet_ids" { description = "IDs of private subnets" value = aws_subnet.private[*].id }

output "nat_gateway_ids" { description = "IDs of NAT Gateways" value = aws_nat_gateway.this[*].id }

output "internet_gateway_id" { description = "ID of Internet Gateway" value = try(aws_internet_gateway.this[0].id, null) }

output "public_route_table_ids" { description = "IDs of public route tables" value = aws_route_table.public[*].id }

output "private_route_table_ids" { description = "IDs of private route tables" value = aws_route_table.private[*].id }

versions.tf:

versions.tf

terraform { required_version = ">= 1.0"

required_providers { aws = { source = "hashicorp/aws" version = ">= 5.0" } } }

  1. Usage Example

examples/complete/main.tf:

provider "aws" { region = "us-west-2" }

module "vpc" { source = "../../"

name = "my-app" environment = "production"

vpc_cidr = "10.0.0.0/16" availability_zones = ["us-west-2a", "us-west-2b", "us-west-2c"]

public_subnet_cidrs = [ "10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24", ]

private_subnet_cidrs = [ "10.0.11.0/24", "10.0.12.0/24", "10.0.13.0/24", ]

enable_nat_gateway = true enable_flow_logs = true

tags = { Project = "MyApp" Owner = "Platform Team" } }

output "vpc_id" { value = module.vpc.vpc_id }

  1. Multi-Cloud: Azure Module

main.tf (Azure):

resource "azurerm_resource_group" "this" { name = "${var.name}-rg" location = var.location

tags = var.tags }

resource "azurerm_virtual_network" "this" { name = "${var.name}-vnet" resource_group_name = azurerm_resource_group.this.name location = azurerm_resource_group.this.location address_space = [var.vnet_cidr]

tags = var.tags }

resource "azurerm_subnet" "public" { count = length(var.public_subnet_cidrs)

name = "${var.name}-public-${count.index + 1}" resource_group_name = azurerm_resource_group.this.name virtual_network_name = azurerm_virtual_network.this.name address_prefixes = [var.public_subnet_cidrs[count.index]] }

resource "azurerm_subnet" "private" { count = length(var.private_subnet_cidrs)

name = "${var.name}-private-${count.index + 1}" resource_group_name = azurerm_resource_group.this.name virtual_network_name = azurerm_virtual_network.this.name address_prefixes = [var.private_subnet_cidrs[count.index]] }

resource "azurerm_network_security_group" "this" { name = "${var.name}-nsg" location = azurerm_resource_group.this.location resource_group_name = azurerm_resource_group.this.name

tags = var.tags }

  1. State Management

backend.tf:

backend.tf - S3 backend

terraform { backend "s3" { bucket = "my-terraform-state" key = "vpc/terraform.tfstate" region = "us-west-2" encrypt = true dynamodb_table = "terraform-locks" } }

Remote state (Azure):

terraform { backend "azurerm" { resource_group_name = "terraform-state-rg" storage_account_name = "tfstate" container_name = "tfstate" key = "vpc.terraform.tfstate" } }

  1. Testing with Terratest

tests/vpc_test.go:

package test

import ( "testing" "github.com/gruntwork-io/terratest/modules/terraform" "github.com/stretchr/testify/assert" )

func TestVPCModule(t *testing.T) { t.Parallel()

terraformOptions := &terraform.Options{
    TerraformDir: "../examples/complete",
    Vars: map[string]interface{}{
        "environment": "test",
    },
}

defer terraform.Destroy(t, terraformOptions)

terraform.InitAndApply(t, terraformOptions)

vpcID := terraform.Output(t, terraformOptions, "vpc_id")
assert.NotEmpty(t, vpcID)

publicSubnets := terraform.OutputList(t, terraformOptions, "public_subnet_ids")
assert.Equal(t, 3, len(publicSubnets))

}

  1. Documentation (README.md)

AWS VPC Terraform Module

Creates a VPC with public and private subnets across multiple availability zones.

Features

  • Multi-AZ deployment
  • Public and private subnets
  • NAT Gateways (optional)
  • VPC Flow Logs (optional)
  • Customizable CIDR blocks
  • Comprehensive tagging

Usage

```hcl module "vpc" { source = "github.com/your-org/terraform-aws-vpc"

name = "my-vpc" environment = "production"

vpc_cidr = "10.0.0.0/16" availability_zones = ["us-west-2a", "us-west-2b"]

public_subnet_cidrs = ["10.0.1.0/24", "10.0.2.0/24"] private_subnet_cidrs = ["10.0.11.0/24", "10.0.12.0/24"]

enable_nat_gateway = true enable_flow_logs = true

tags = { Project = "MyApp" } } ```

Inputs

NameDescriptionTypeDefaultRequired
nameVPC namestring-yes
environmentEnvironmentstring-yes
vpc_cidrVPC CIDRstring"10.0.0.0/16"no
availability_zonesAZslist(string)-yes
enable_nat_gatewayEnable NATbooltrueno

Outputs

NameDescription
vpc_idVPC ID
public_subnet_idsPublic subnet IDs
private_subnet_idsPrivate subnet IDs

Requirements

NameVersion
terraform>= 1.0
aws>= 5.0

Best Practices

DO:

  • Use semantic versioning

  • Document all variables

  • Provide examples

  • Add validation rules

  • Use locals for DRY code

  • Tag all resources

  • Use remote state

  • Write tests

  • Follow naming conventions

DON'T:

  • Hardcode values

  • Skip validation

  • Use default values in production

  • Ignore dependencies

  • Skip documentation

  • Commit .tfstate files

  • Use latest provider versions

  • Forget outputs

Checklist

  • Module structure created

  • Variables defined with validation

  • Resources created

  • Outputs defined

  • Documentation written

  • Examples provided

  • Tests written

  • Versioned and tagged

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

threejs-scene-builder

No summary provided by upstream source.

Repository SourceNeeds Review
General

database-query-optimizer

No summary provided by upstream source.

Repository SourceNeeds Review
General

react-component-generator

No summary provided by upstream source.

Repository SourceNeeds Review
General

import-organizer

No summary provided by upstream source.

Repository SourceNeeds Review