Files
terraform-foundation/terraform/modules/shared-vpc/main.tf
Greg Hendrickson 6136cde9bb feat: Terraform Foundation - AWS Landing Zone
Enterprise-grade multi-tenant AWS cloud foundation.

Modules:
- GitHub OIDC for keyless CI/CD authentication
- IAM account settings and security baseline
- AWS Config Rules for compliance
- ABAC (Attribute-Based Access Control)
- SCPs (Service Control Policies)

Features:
- Multi-account architecture
- Cost optimization patterns
- Security best practices
- Comprehensive documentation

Tech: Terraform, AWS Organizations, IAM Identity Center
2026-02-02 02:57:23 +00:00

268 lines
7.3 KiB
HCL

################################################################################
# Shared VPC Module
# Single VPC shared across all tenants via AWS RAM
# Isolation via: Security Groups, ABAC (tags), optional subnet segmentation
################################################################################
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 5.0"
}
}
}
################################################################################
# VPC
################################################################################
resource "aws_vpc" "shared" {
cidr_block = var.vpc_cidr
enable_dns_hostnames = true
enable_dns_support = true
tags = {
Name = "shared-vpc"
Environment = "shared"
ManagedBy = "terraform"
}
}
################################################################################
# Internet Gateway
################################################################################
resource "aws_internet_gateway" "shared" {
vpc_id = aws_vpc.shared.id
tags = {
Name = "shared-igw"
}
}
################################################################################
# NAT Gateway (Single for cost savings)
################################################################################
resource "aws_eip" "nat" {
count = var.enable_nat_gateway ? 1 : 0
domain = "vpc"
tags = {
Name = "shared-nat-eip"
}
}
resource "aws_nat_gateway" "shared" {
count = var.enable_nat_gateway ? 1 : 0
allocation_id = aws_eip.nat[0].id
subnet_id = aws_subnet.public[0].id
tags = {
Name = "shared-nat"
}
depends_on = [aws_internet_gateway.shared]
}
################################################################################
# Subnets - Public (shared)
################################################################################
resource "aws_subnet" "public" {
count = length(var.availability_zones)
vpc_id = aws_vpc.shared.id
cidr_block = cidrsubnet(var.vpc_cidr, 4, count.index)
availability_zone = var.availability_zones[count.index]
map_public_ip_on_launch = true
tags = {
Name = "shared-public-${var.availability_zones[count.index]}"
Type = "public"
Environment = "shared"
}
}
################################################################################
# Subnets - Private (shared across tenants)
################################################################################
resource "aws_subnet" "private_shared" {
count = length(var.availability_zones)
vpc_id = aws_vpc.shared.id
cidr_block = cidrsubnet(var.vpc_cidr, 4, count.index + length(var.availability_zones))
availability_zone = var.availability_zones[count.index]
tags = {
Name = "shared-private-${var.availability_zones[count.index]}"
Type = "private"
Environment = "shared"
}
}
################################################################################
# Subnets - Per-Tenant Private (optional, for stricter isolation)
################################################################################
resource "aws_subnet" "private_tenant" {
for_each = var.create_tenant_subnets ? {
for combo in setproduct(var.tenants, range(length(var.availability_zones))) :
"${combo[0]}-${combo[1]}" => {
tenant = combo[0]
az_idx = combo[1]
}
} : {}
vpc_id = aws_vpc.shared.id
cidr_block = cidrsubnet(var.tenant_subnet_cidr, 4, index(var.tenants, each.value.tenant) * length(var.availability_zones) + each.value.az_idx)
availability_zone = var.availability_zones[each.value.az_idx]
tags = {
Name = "tenant-${each.value.tenant}-private-${var.availability_zones[each.value.az_idx]}"
Type = "private"
Tenant = each.value.tenant
Environment = "shared"
}
}
################################################################################
# Route Tables
################################################################################
resource "aws_route_table" "public" {
vpc_id = aws_vpc.shared.id
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.shared.id
}
tags = {
Name = "shared-public-rt"
}
}
resource "aws_route_table" "private" {
vpc_id = aws_vpc.shared.id
dynamic "route" {
for_each = var.enable_nat_gateway ? [1] : []
content {
cidr_block = "0.0.0.0/0"
nat_gateway_id = aws_nat_gateway.shared[0].id
}
}
tags = {
Name = "shared-private-rt"
}
}
resource "aws_route_table_association" "public" {
count = length(var.availability_zones)
subnet_id = aws_subnet.public[count.index].id
route_table_id = aws_route_table.public.id
}
resource "aws_route_table_association" "private_shared" {
count = length(var.availability_zones)
subnet_id = aws_subnet.private_shared[count.index].id
route_table_id = aws_route_table.private.id
}
resource "aws_route_table_association" "private_tenant" {
for_each = aws_subnet.private_tenant
subnet_id = each.value.id
route_table_id = aws_route_table.private.id
}
################################################################################
# AWS RAM - Share VPC Subnets with Organization
################################################################################
resource "aws_ram_resource_share" "vpc_subnets" {
name = "shared-vpc-subnets"
allow_external_principals = false
tags = {
Name = "Shared VPC Subnets"
}
}
# Share private subnets with the organization
resource "aws_ram_resource_association" "private_shared" {
count = length(var.availability_zones)
resource_arn = aws_subnet.private_shared[count.index].arn
resource_share_arn = aws_ram_resource_share.vpc_subnets.arn
}
# Share tenant-specific subnets (if created)
resource "aws_ram_resource_association" "private_tenant" {
for_each = aws_subnet.private_tenant
resource_arn = each.value.arn
resource_share_arn = aws_ram_resource_share.vpc_subnets.arn
}
# Share with specific OUs or entire org
resource "aws_ram_principal_association" "workloads_ou" {
principal = var.workloads_ou_arn
resource_share_arn = aws_ram_resource_share.vpc_subnets.arn
}
################################################################################
# Default Security Group - Deny All (force explicit SGs)
################################################################################
resource "aws_default_security_group" "default" {
vpc_id = aws_vpc.shared.id
# No ingress or egress rules = deny all
tags = {
Name = "default-deny-all"
Description = "Default SG - no access, use tenant-specific SGs"
}
}
################################################################################
# Outputs
################################################################################
output "vpc_id" {
value = aws_vpc.shared.id
}
output "vpc_cidr" {
value = aws_vpc.shared.cidr_block
}
output "public_subnet_ids" {
value = aws_subnet.public[*].id
}
output "private_shared_subnet_ids" {
value = aws_subnet.private_shared[*].id
}
output "private_tenant_subnet_ids" {
value = {
for k, v in aws_subnet.private_tenant : k => v.id
}
}
output "nat_gateway_ip" {
value = var.enable_nat_gateway ? aws_eip.nat[0].public_ip : null
}
output "ram_share_arn" {
value = aws_ram_resource_share.vpc_subnets.arn
}