Terraform Demos
Copy and modify these pre-built Terraform configurations for your infrastructure.
AWSAWS VPC with Public and Private Subnets
●AWSUploadedA complete VPC setup with public and private subnets, an internet gateway, and a NAT gateway.
By System · Networking
▾
AWS VPC with Public and Private Subnets
●AWSUploadedA complete VPC setup with public and private subnets, an internet gateway, and a NAT gateway.
By System · Networking
main.tf · variables.tf · outputs.tf
Downloadterraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
provider "aws" {
region = var.aws_region
}
resource "aws_vpc" "main" {
cidr_block = var.vpc_cidr
enable_dns_support = true
enable_dns_hostnames = true
tags = { Name = "${var.environment}-vpc" }
}
resource "aws_internet_gateway" "main" {
vpc_id = aws_vpc.main.id
tags = { Name = "${var.environment}-igw" }
}
resource "aws_subnet" "public" {
count = length(var.public_subnet_cidrs)
vpc_id = aws_vpc.main.id
cidr_block = var.public_subnet_cidrs[count.index]
availability_zone = var.availability_zones[count.index]
map_public_ip_on_launch = true
tags = { Name = "${var.environment}-public-${count.index}" }
}
resource "aws_subnet" "private" {
count = length(var.private_subnet_cidrs)
vpc_id = aws_vpc.main.id
cidr_block = var.private_subnet_cidrs[count.index]
availability_zone = var.availability_zones[count.index]
tags = { Name = "${var.environment}-private-${count.index}" }
}
resource "aws_eip" "nat" {
domain = "vpc"
}
resource "aws_nat_gateway" "main" {
allocation_id = aws_eip.nat.id
subnet_id = aws_subnet.public[0].id
tags = { Name = "${var.environment}-nat" }
}
resource "aws_route_table" "public" {
vpc_id = aws_vpc.main.id
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.main.id
}
}
resource "aws_route_table" "private" {
vpc_id = aws_vpc.main.id
route {
cidr_block = "0.0.0.0/0"
nat_gateway_id = aws_nat_gateway.main.id
}
}
resource "aws_route_table_association" "public" {
count = length(aws_subnet.public)
subnet_id = aws_subnet.public[count.index].id
route_table_id = aws_route_table.public.id
}
resource "aws_route_table_association" "private" {
count = length(aws_subnet.private)
subnet_id = aws_subnet.private[count.index].id
route_table_id = aws_route_table.private.id
}
variable "aws_region" {
type = string
default = "us-east-1"
}
variable "environment" {
type = string
default = "dev"
}
variable "vpc_cidr" {
type = string
default = "10.0.0.0/16"
}
variable "public_subnet_cidrs" {
type = list(string)
default = ["10.0.1.0/24", "10.0.2.0/24"]
}
variable "private_subnet_cidrs" {
type = list(string)
default = ["10.0.101.0/24", "10.0.102.0/24"]
}
variable "availability_zones" {
type = list(string)
default = ["us-east-1a", "us-east-1b"]
}Troubleshooting & Solutions
Common Issues
- CIDR block overlap with an existing VPC
- NAT gateway costs more than expected
- Private subnet has no internet access
Solutions
- Pick a non-overlapping CIDR range
- Use one NAT gateway per environment, not per AZ, for cost-sensitive setups
- Confirm the private route table points at the NAT gateway
Troubleshooting Steps
- Check CIDR blocks don't overlap with peered VPCs
- Verify route table associations are correct per subnet
- Confirm NAT gateway is in a public subnet
- Check availability zone names match the target region
Version compatibility: Terraform >= 1.0, AWS Provider >= 5.0
AWSAWS Application Load Balancer
●AWSUploadedCreate an ALB with a target group and listener for HTTP traffic.
By System · Networking
▾
AWS Application Load Balancer
●AWSUploadedCreate an ALB with a target group and listener for HTTP traffic.
By System · Networking
main.tf · variables.tf · outputs.tf
Downloadresource "aws_lb" "main" {
name = "${var.app_name}-alb"
internal = false
load_balancer_type = "application"
security_groups = [aws_security_group.alb.id]
subnets = var.public_subnet_ids
}
resource "aws_security_group" "alb" {
name = "${var.app_name}-alb-sg"
vpc_id = var.vpc_id
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
resource "aws_lb_target_group" "app" {
name = "${var.app_name}-tg"
port = var.app_port
protocol = "HTTP"
vpc_id = var.vpc_id
health_check {
path = "/health"
healthy_threshold = 2
unhealthy_threshold = 3
interval = 15
}
}
resource "aws_lb_listener" "http" {
load_balancer_arn = aws_lb.main.arn
port = 80
protocol = "HTTP"
default_action {
type = "forward"
target_group_arn = aws_lb_target_group.app.arn
}
}
variable "app_name" {
type = string
}
variable "app_port" {
type = number
default = 3000
}
variable "vpc_id" {
type = string
}
variable "public_subnet_ids" {
type = list(string)
}Troubleshooting & Solutions
Common Issues
- Target group shows unhealthy targets
- ALB times out
- 502 from the ALB
Solutions
- Confirm the health check path actually exists and returns 2xx
- Check the target's security group allows traffic from the ALB's security group
- Verify the target's app is listening on the port the target group expects
Troubleshooting Steps
- Check target group health in the console/CLI
- Confirm the ALB's subnets are public with a route to an IGW
- Verify listener rules point at the right target group
- Check the target instance's own security group, not just the ALB's
Version compatibility: Terraform >= 1.0, AWS Provider >= 5.0
AWSAWS Route53 Hosted Zone and Records
●AWSUploadedCreate a Route53 hosted zone with DNS records.
By System · Networking
▾
AWS Route53 Hosted Zone and Records
●AWSUploadedCreate a Route53 hosted zone with DNS records.
By System · Networking
main.tf · variables.tf · outputs.tf
Downloadresource "aws_route53_zone" "main" {
name = var.domain_name
}
resource "aws_route53_record" "root" {
zone_id = aws_route53_zone.main.zone_id
name = var.domain_name
type = "A"
alias {
name = var.alb_dns_name
zone_id = var.alb_zone_id
evaluate_target_health = true
}
}
resource "aws_route53_record" "www" {
zone_id = aws_route53_zone.main.zone_id
name = "www.${var.domain_name}"
type = "CNAME"
ttl = 300
records = [var.domain_name]
}
variable "domain_name" {
type = string
}
variable "alb_dns_name" {
type = string
}
variable "alb_zone_id" {
type = string
}Troubleshooting & Solutions
Common Issues
- DNS not resolving after apply
- Domain registered elsewhere doesn't pick up records
- Certificate validation stuck pending
Solutions
- DNS propagation can take up to 48 hours even after records are correct — check with `dig`, not just the browser
- Update the domain's nameservers at the registrar to Route53's assigned NS records
- Add the ACM validation CNAME record this hosted zone needs for certificate issuance
Troubleshooting Steps
- Run `dig NS <domain>` to confirm the registrar points at Route53's nameservers
- Run `dig <domain>` to confirm the A/CNAME record resolves
- Check the alias target's zone_id matches the actual ALB/CloudFront zone, not a guessed value
- Verify there's no conflicting record (e.g. both A and CNAME) at the same name
Version compatibility: Terraform >= 1.0, AWS Provider >= 5.0
AzureAzure Virtual Network with Subnets
●AzureUploadedCreate an Azure VNet with public and private subnets, an NSG, and a route table.
By System · Networking
▾
Azure Virtual Network with Subnets
●AzureUploadedCreate an Azure VNet with public and private subnets, an NSG, and a route table.
By System · Networking
main.tf · variables.tf · outputs.tf
Downloadterraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "~> 3.0"
}
}
}
provider "azurerm" {
features {}
}
resource "azurerm_resource_group" "main" {
name = "${var.app_name}-rg"
location = var.location
}
resource "azurerm_virtual_network" "main" {
name = "${var.app_name}-vnet"
address_space = [var.vnet_cidr]
location = azurerm_resource_group.main.location
resource_group_name = azurerm_resource_group.main.name
}
resource "azurerm_subnet" "public" {
name = "public"
resource_group_name = azurerm_resource_group.main.name
virtual_network_name = azurerm_virtual_network.main.name
address_prefixes = [var.public_subnet_cidr]
}
resource "azurerm_subnet" "private" {
name = "private"
resource_group_name = azurerm_resource_group.main.name
virtual_network_name = azurerm_virtual_network.main.name
address_prefixes = [var.private_subnet_cidr]
}
resource "azurerm_network_security_group" "main" {
name = "${var.app_name}-nsg"
location = azurerm_resource_group.main.location
resource_group_name = azurerm_resource_group.main.name
security_rule {
name = "AllowSSH"
priority = 100
direction = "Inbound"
access = "Allow"
protocol = "Tcp"
source_port_range = "*"
destination_port_range = "22"
source_address_prefix = "*"
destination_address_prefix = "*"
}
}
resource "azurerm_subnet_network_security_group_association" "public" {
subnet_id = azurerm_subnet.public.id
network_security_group_id = azurerm_network_security_group.main.id
}
variable "app_name" {
type = string
}
variable "location" {
type = string
default = "East US"
}
variable "vnet_cidr" {
type = string
default = "10.1.0.0/16"
}
variable "public_subnet_cidr" {
type = string
default = "10.1.1.0/24"
}
variable "private_subnet_cidr" {
type = string
default = "10.1.2.0/24"
}Troubleshooting & Solutions
Common Issues
- Subnet delegation conflicts
- NSG rule priority collision
- Resource group location mismatch
Solutions
- Keep subnet address prefixes within the VNet's address_space and non-overlapping
- Give every NSG rule a unique priority number — Azure rejects duplicates
- Make sure every resource in the group uses the same location as the resource group unless intentionally distributed
Troubleshooting Steps
- Run `az network vnet subnet list` to confirm actual subnet ranges
- Check NSG associations with `az network nsg show`
- Verify service principal / CLI auth has Network Contributor role
- Confirm the subscription has quota for the resource types being created
Version compatibility: Terraform >= 1.0, AzureRM Provider >= 3.0
GCPGCP VPC with Firewall Rules
●GCPUploadedCreate a GCP VPC network with custom firewall rules for web and SSH access.
By System · Networking
▾
GCP VPC with Firewall Rules
●GCPUploadedCreate a GCP VPC network with custom firewall rules for web and SSH access.
By System · Networking
main.tf · variables.tf · outputs.tf
Downloadterraform {
required_providers {
google = {
source = "hashicorp/google"
version = "~> 5.0"
}
}
}
provider "google" {
project = var.project_id
region = var.region
}
resource "google_compute_network" "main" {
name = "${var.app_name}-vpc"
auto_create_subnetworks = false
}
resource "google_compute_subnetwork" "main" {
name = "${var.app_name}-subnet"
ip_cidr_range = var.subnet_cidr
region = var.region
network = google_compute_network.main.id
}
resource "google_compute_firewall" "allow_ssh" {
name = "${var.app_name}-allow-ssh"
network = google_compute_network.main.id
allow {
protocol = "tcp"
ports = ["22"]
}
source_ranges = [var.ssh_source_range]
target_tags = ["ssh"]
}
resource "google_compute_firewall" "allow_web" {
name = "${var.app_name}-allow-web"
network = google_compute_network.main.id
allow {
protocol = "tcp"
ports = ["80", "443"]
}
source_ranges = ["0.0.0.0/0"]
target_tags = ["web"]
}
variable "project_id" {
type = string
}
variable "region" {
type = string
default = "us-central1"
}
variable "app_name" {
type = string
}
variable "subnet_cidr" {
type = string
default = "10.2.0.0/24"
}
variable "ssh_source_range" {
type = string
default = "0.0.0.0/0"
}Troubleshooting & Solutions
Common Issues
- Firewall rule has no effect
- Instance unreachable despite allow rule
- auto_create_subnetworks conflict
Solutions
- Firewall rules only apply to instances carrying the matching target_tags — confirm the instance has the tag
- Check the instance actually has an external IP if connecting from outside GCP
- Set auto_create_subnetworks = false and define subnets explicitly to avoid the default "legacy" network conflicting
Troubleshooting Steps
- Run `gcloud compute firewall-rules list` to confirm the rule is active
- Check the target instance's network tags match target_tags exactly
- Verify the subnet's region matches the instance's region
- Use `gcloud compute networks subnets list` to confirm CIDR ranges don't overlap
Version compatibility: Terraform >= 1.0, Google Provider >= 5.0