Generates reusable Terraform modules with best practices for AWS, Azure, GCP infrastructure as code. Use when user asks to "create Terraform module", "generate IaC module", "setup Terraform", or "create infrastructure module".
Generates production-ready, reusable Terraform modules with best practices for multi-cloud infrastructure as code.
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
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"
}
}
}
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
}
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
}
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"
}
}
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))
}
# 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
| Name | Description | Type | Default | Required |
|------|-------------|------|---------|----------|
| name | VPC name | string | - | yes |
| environment | Environment | string | - | yes |
| vpc_cidr | VPC CIDR | string | "10.0.0.0/16" | no |
| availability_zones | AZs | list(string) | - | yes |
| enable_nat_gateway | Enable NAT | bool | true | no |
## Outputs
| Name | Description |
|------|-------------|
| vpc_id | VPC ID |
| public_subnet_ids | Public subnet IDs |
| private_subnet_ids | Private subnet IDs |
## Requirements
| Name | Version |
|------|---------|
| terraform | >= 1.0 |
| aws | >= 5.0 |
DO:
DON'T:
npx skills add Dexploarer/terraform-module-builder下载完整 Skill 目录,包含 SKILL.md 及所有相关文件
Search for places (restaurants, cafes, etc.) via Google Places API proxy on localhost.
Interact with GitHub using the `gh` CLI. Use `gh issue`, `gh pr`, `gh run`, and `gh api` for issues, PRs, CI runs, and advanced queries.
Create or update AgentSkills. Use when designing, structuring, or packaging skills with scripts, references, and assets.
Start voice calls via the OpenClaw voice-call plugin.
Notion API for creating and managing pages, databases, and blocks.
Gemini CLI for one-shot Q&A, summaries, and generation.
Category:developer