Skip to main content
Cloud & AI Hub
Browse
Glossary AI Directory Playgrounds Models Prompts Explainers Strategy Matrix Benchmark Decoder

Terraform VPC Creator

Use Case: Scaffold a complete AWS VPC module in HCL with subnets, IGW, NAT gateway, and route tables

System Instructions

You are a Terraform and AWS networking expert. Generate production-grade HCL modules with correct resource dependencies, tagging, and remote state compatibility.

User Prompt Template

Generate a Terraform AWS VPC module:

Project: {PROJECT_NAME}
CIDR: {VPC_CIDR}
AZs: {AVAILABILITY_ZONES}
NAT gateway: {NAT_STRATEGY}
Environment: {ENVIRONMENT}

Run This Prompt โ€” SDK Snippets

Implementation Guidelines

What This Prompt Does

This prompt scaffolds a complete, production-ready Terraform AWS VPC module from a minimal set of inputs. The output includes the VPC, public and private subnets across multiple availability zones, an Internet Gateway, NAT Gateways (with cost-optimized single-AZ or HA per-AZ options), route tables, and all necessary associations. The module is structured for reuse across environments (dev, staging, prod) via variable files.

System Prompt

You are a Terraform and AWS networking expert with deep experience in multi-account, 
multi-region AWS architectures and Infrastructure-as-Code best practices.

When generating Terraform HCL:
1. Structure output as a reusable module: main.tf, variables.tf, outputs.tf โ€” separated 
   by "# === filename ===" comments.
2. Use terraform >= 1.5 and aws provider >= 5.0 syntax.
3. Use for_each or count for resources that repeat across AZs โ€” never copy-paste blocks.
4. Every resource must have a "tags" block that merges module-level tags with resource-specific tags.
5. Use depends_on only when implicit dependencies cannot be inferred (rare โ€” explain why).
6. Outputs must export: vpc_id, public_subnet_ids, private_subnet_ids, nat_gateway_ids.
7. NAT strategy:
   - "single": one NAT GW in the first public subnet (cost-optimized for dev)
   - "per_az": one NAT GW per AZ (HA for production)
8. Add inline comments explaining CIDR math and design decisions.
9. Do NOT use deprecated aws_subnet data sources or legacy aws_internet_gateway attachment syntax.

User Prompt Template

Generate a complete Terraform AWS VPC module.

Project name: {PROJECT_NAME}
VPC CIDR block: {VPC_CIDR} (e.g., "10.0.0.0/16")
Availability zones: {AVAILABILITY_ZONES} (e.g., ["us-east-1a", "us-east-1b", "us-east-1c"])
NAT gateway strategy: {NAT_STRATEGY} ("single" or "per_az")
Environment: {ENVIRONMENT} (e.g., "production")
Additional requirements: {SPECIAL_REQUIREMENTS} (e.g., "enable VPC Flow Logs to CloudWatch", "add VPC endpoints for S3 and DynamoDB")

Output main.tf, variables.tf, and outputs.tf.

Example Output

Model output (abridged for readability):

# === variables.tf ===

variable "project_name" {
  description = "Name of the project, used in all resource tags and names."
  type        = string
}

variable "vpc_cidr" {
  description = "CIDR block for the VPC (e.g., 10.0.0.0/16)."
  type        = string
  default     = "10.0.0.0/16"
}

variable "availability_zones" {
  description = "List of AZs to deploy subnets into."
  type        = list(string)
}

variable "nat_strategy" {
  description = "NAT gateway strategy: 'single' (cost-optimized) or 'per_az' (HA)."
  type        = string
  default     = "single"
  validation {
    condition     = contains(["single", "per_az"], var.nat_strategy)
    error_message = "nat_strategy must be 'single' or 'per_az'."
  }
}

variable "environment" {
  description = "Deployment environment (dev, staging, production)."
  type        = string
}

# === main.tf ===

locals {
  # Divide the VPC CIDR into /24 subnets for simplicity.
  # Public subnets: 10.x.0.0/24, 10.x.1.0/24, ...
  # Private subnets: 10.x.10.0/24, 10.x.11.0/24, ...
  public_cidr_blocks  = [for i, az in var.availability_zones : cidrsubnet(var.vpc_cidr, 8, i)]
  private_cidr_blocks = [for i, az in var.availability_zones : cidrsubnet(var.vpc_cidr, 8, i + 10)]

  common_tags = {
    Project     = var.project_name
    Environment = var.environment
    ManagedBy   = "terraform"
  }
}

resource "aws_vpc" "this" {
  cidr_block           = var.vpc_cidr
  enable_dns_hostnames = true
  enable_dns_support   = true
  tags = merge(local.common_tags, { Name = "${var.project_name}-vpc" })
}

resource "aws_internet_gateway" "this" {
  vpc_id = aws_vpc.this.id
  tags   = merge(local.common_tags, { Name = "${var.project_name}-igw" })
}

resource "aws_subnet" "public" {
  for_each = { for i, az in var.availability_zones : az => i }

  vpc_id                  = aws_vpc.this.id
  cidr_block              = local.public_cidr_blocks[each.value]
  availability_zone       = each.key
  map_public_ip_on_launch = true

  tags = merge(local.common_tags, {
    Name = "${var.project_name}-public-${each.key}"
    Tier = "public"
  })
}

resource "aws_subnet" "private" {
  for_each = { for i, az in var.availability_zones : az => i }

  vpc_id            = aws_vpc.this.id
  cidr_block        = local.private_cidr_blocks[each.value]
  availability_zone = each.key

  tags = merge(local.common_tags, {
    Name = "${var.project_name}-private-${each.key}"
    Tier = "private"
  })
}

resource "aws_eip" "nat" {
  # Create one EIP per NAT GW. In "single" mode, only 1 EIP is needed.
  for_each = var.nat_strategy == "per_az" ? aws_subnet.public : { (keys(aws_subnet.public)[0]) = aws_subnet.public[keys(aws_subnet.public)[0]] }
  domain   = "vpc"
  tags     = merge(local.common_tags, { Name = "${var.project_name}-nat-eip-${each.key}" })
}

resource "aws_nat_gateway" "this" {
  for_each      = aws_eip.nat
  allocation_id = aws_eip.nat[each.key].id
  subnet_id     = aws_subnet.public[each.key].id
  tags          = merge(local.common_tags, { Name = "${var.project_name}-nat-${each.key}" })
  depends_on    = [aws_internet_gateway.this]
}

# ... route tables and associations omitted for brevity

# === outputs.tf ===

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

output "public_subnet_ids" {
  description = "List of public subnet IDs."
  value       = [for s in aws_subnet.public : s.id]
}

output "private_subnet_ids" {
  description = "List of private subnet IDs."
  value       = [for s in aws_subnet.private : s.id]
}

Tips & Variations

  • Remote state configuration: Ask for "Also generate a backend.tf with S3 remote state and DynamoDB locking" as part of {SPECIAL_REQUIREMENTS}. The model will produce the correct terraform { backend "s3" { ... } } block.
  • VPC Endpoints for cost savings: Add "Add S3 and DynamoDB gateway VPC endpoints to eliminate NAT costs for AWS service traffic" to requirements. At scale, this can reduce NAT gateway bills by 30โ€“70%.
  • Transit Gateway attachment: For multi-VPC architectures, include "Add a Transit Gateway attachment resource and a route in private route tables pointing 10.0.0.0/8 to the TGW" to wire up hub-and-spoke networking.
  • Terragrunt wrapper: Ask the model to output a terragrunt.hcl alongside the module to handle remote state, AWS provider version pinning, and environment-specific variable injection automatically.