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
This commit is contained in:
2026-02-01 20:06:28 +00:00
commit 6136cde9bb
145 changed files with 30832 additions and 0 deletions

View File

@@ -0,0 +1,34 @@
# security-groups
Terraform module for AWS landing zone pattern.
Create common security group patterns for multi-tier architectures.
## Planned Features
- [ ] Web tier (HTTP/HTTPS from ALB)
- [ ] App tier (from web tier only)
- [ ] Database tier (from app tier only)
- [ ] Bastion host (SSH from allowed CIDRs)
- [ ] VPC endpoints (HTTPS from VPC)
- [ ] EKS patterns (cluster, nodes, pods)
## Planned Usage
```hcl
module "security_groups" {
source = "../modules/security-groups"
vpc_id = module.vpc.vpc_id
name_prefix = "myapp"
create_web_tier = true
create_app_tier = true
create_db_tier = true
create_bastion = true
allowed_ssh_cidrs = ["10.0.0.0/8"]
tags = local.tags
}
```

View File

@@ -0,0 +1,395 @@
################################################################################
# Security Groups Module
#
# Creates common security group patterns for multi-tier architectures:
# - Web tier (HTTP/HTTPS from ALB or internet)
# - App tier (from web tier only)
# - Database tier (from app tier only)
# - Bastion host (SSH from allowed CIDRs)
# - VPC endpoints (HTTPS from VPC)
# - EKS patterns (cluster, nodes)
################################################################################
terraform {
required_version = ">= 1.5.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 5.0"
}
}
}
data "aws_vpc" "selected" {
id = var.vpc_id
}
locals {
vpc_cidr = data.aws_vpc.selected.cidr_block
}
################################################################################
# Web Tier Security Group
################################################################################
resource "aws_security_group" "web" {
count = var.create_web_tier ? 1 : 0
name_prefix = "${var.name_prefix}-web-"
description = "Web tier - HTTP/HTTPS access"
vpc_id = var.vpc_id
tags = merge(var.tags, {
Name = "${var.name_prefix}-web"
Tier = "web"
})
lifecycle {
create_before_destroy = true
}
}
resource "aws_vpc_security_group_ingress_rule" "web_http" {
count = var.create_web_tier ? 1 : 0
security_group_id = aws_security_group.web[0].id
description = "HTTP from allowed sources"
from_port = 80
to_port = 80
ip_protocol = "tcp"
cidr_ipv4 = var.web_ingress_cidr
tags = { Name = "http-ingress" }
}
resource "aws_vpc_security_group_ingress_rule" "web_https" {
count = var.create_web_tier ? 1 : 0
security_group_id = aws_security_group.web[0].id
description = "HTTPS from allowed sources"
from_port = 443
to_port = 443
ip_protocol = "tcp"
cidr_ipv4 = var.web_ingress_cidr
tags = { Name = "https-ingress" }
}
resource "aws_vpc_security_group_egress_rule" "web_all" {
count = var.create_web_tier ? 1 : 0
security_group_id = aws_security_group.web[0].id
description = "Allow all outbound"
ip_protocol = "-1"
cidr_ipv4 = "0.0.0.0/0"
tags = { Name = "all-egress" }
}
################################################################################
# App Tier Security Group
################################################################################
resource "aws_security_group" "app" {
count = var.create_app_tier ? 1 : 0
name_prefix = "${var.name_prefix}-app-"
description = "App tier - access from web tier"
vpc_id = var.vpc_id
tags = merge(var.tags, {
Name = "${var.name_prefix}-app"
Tier = "app"
})
lifecycle {
create_before_destroy = true
}
}
resource "aws_vpc_security_group_ingress_rule" "app_from_web" {
count = var.create_app_tier && var.create_web_tier ? 1 : 0
security_group_id = aws_security_group.app[0].id
description = "App port from web tier"
from_port = var.app_port
to_port = var.app_port
ip_protocol = "tcp"
referenced_security_group_id = aws_security_group.web[0].id
tags = { Name = "from-web-tier" }
}
resource "aws_vpc_security_group_ingress_rule" "app_from_cidr" {
count = var.create_app_tier && !var.create_web_tier ? 1 : 0
security_group_id = aws_security_group.app[0].id
description = "App port from VPC"
from_port = var.app_port
to_port = var.app_port
ip_protocol = "tcp"
cidr_ipv4 = local.vpc_cidr
tags = { Name = "from-vpc" }
}
resource "aws_vpc_security_group_egress_rule" "app_all" {
count = var.create_app_tier ? 1 : 0
security_group_id = aws_security_group.app[0].id
description = "Allow all outbound"
ip_protocol = "-1"
cidr_ipv4 = "0.0.0.0/0"
tags = { Name = "all-egress" }
}
################################################################################
# Database Tier Security Group
################################################################################
resource "aws_security_group" "db" {
count = var.create_db_tier ? 1 : 0
name_prefix = "${var.name_prefix}-db-"
description = "Database tier - access from app tier"
vpc_id = var.vpc_id
tags = merge(var.tags, {
Name = "${var.name_prefix}-db"
Tier = "database"
})
lifecycle {
create_before_destroy = true
}
}
resource "aws_vpc_security_group_ingress_rule" "db_from_app" {
count = var.create_db_tier && var.create_app_tier ? 1 : 0
security_group_id = aws_security_group.db[0].id
description = "Database port from app tier"
from_port = var.db_port
to_port = var.db_port
ip_protocol = "tcp"
referenced_security_group_id = aws_security_group.app[0].id
tags = { Name = "from-app-tier" }
}
resource "aws_vpc_security_group_ingress_rule" "db_from_cidr" {
count = var.create_db_tier && !var.create_app_tier ? 1 : 0
security_group_id = aws_security_group.db[0].id
description = "Database port from VPC"
from_port = var.db_port
to_port = var.db_port
ip_protocol = "tcp"
cidr_ipv4 = local.vpc_cidr
tags = { Name = "from-vpc" }
}
resource "aws_vpc_security_group_egress_rule" "db_all" {
count = var.create_db_tier ? 1 : 0
security_group_id = aws_security_group.db[0].id
description = "Allow all outbound"
ip_protocol = "-1"
cidr_ipv4 = "0.0.0.0/0"
tags = { Name = "all-egress" }
}
################################################################################
# Bastion Security Group
################################################################################
resource "aws_security_group" "bastion" {
count = var.create_bastion ? 1 : 0
name_prefix = "${var.name_prefix}-bastion-"
description = "Bastion host - SSH from allowed CIDRs"
vpc_id = var.vpc_id
tags = merge(var.tags, {
Name = "${var.name_prefix}-bastion"
Tier = "bastion"
})
lifecycle {
create_before_destroy = true
}
}
resource "aws_vpc_security_group_ingress_rule" "bastion_ssh" {
for_each = var.create_bastion ? toset(var.allowed_ssh_cidrs) : []
security_group_id = aws_security_group.bastion[0].id
description = "SSH from ${each.value}"
from_port = 22
to_port = 22
ip_protocol = "tcp"
cidr_ipv4 = each.value
tags = { Name = "ssh-from-${replace(each.value, "/", "-")}" }
}
resource "aws_vpc_security_group_egress_rule" "bastion_all" {
count = var.create_bastion ? 1 : 0
security_group_id = aws_security_group.bastion[0].id
description = "Allow all outbound"
ip_protocol = "-1"
cidr_ipv4 = "0.0.0.0/0"
tags = { Name = "all-egress" }
}
################################################################################
# VPC Endpoints Security Group
################################################################################
resource "aws_security_group" "endpoints" {
count = var.create_endpoints ? 1 : 0
name_prefix = "${var.name_prefix}-endpoints-"
description = "VPC Endpoints - HTTPS from VPC"
vpc_id = var.vpc_id
tags = merge(var.tags, {
Name = "${var.name_prefix}-endpoints"
Tier = "endpoints"
})
lifecycle {
create_before_destroy = true
}
}
resource "aws_vpc_security_group_ingress_rule" "endpoints_https" {
count = var.create_endpoints ? 1 : 0
security_group_id = aws_security_group.endpoints[0].id
description = "HTTPS from VPC"
from_port = 443
to_port = 443
ip_protocol = "tcp"
cidr_ipv4 = local.vpc_cidr
tags = { Name = "https-from-vpc" }
}
resource "aws_vpc_security_group_egress_rule" "endpoints_all" {
count = var.create_endpoints ? 1 : 0
security_group_id = aws_security_group.endpoints[0].id
description = "Allow all outbound"
ip_protocol = "-1"
cidr_ipv4 = "0.0.0.0/0"
tags = { Name = "all-egress" }
}
################################################################################
# EKS Cluster Security Group
################################################################################
resource "aws_security_group" "eks_cluster" {
count = var.create_eks ? 1 : 0
name_prefix = "${var.name_prefix}-eks-cluster-"
description = "EKS cluster control plane"
vpc_id = var.vpc_id
tags = merge(var.tags, {
Name = "${var.name_prefix}-eks-cluster"
Tier = "eks"
})
lifecycle {
create_before_destroy = true
}
}
resource "aws_vpc_security_group_ingress_rule" "eks_cluster_https" {
count = var.create_eks ? 1 : 0
security_group_id = aws_security_group.eks_cluster[0].id
description = "HTTPS from VPC (kubectl)"
from_port = 443
to_port = 443
ip_protocol = "tcp"
cidr_ipv4 = local.vpc_cidr
tags = { Name = "https-from-vpc" }
}
resource "aws_vpc_security_group_egress_rule" "eks_cluster_all" {
count = var.create_eks ? 1 : 0
security_group_id = aws_security_group.eks_cluster[0].id
description = "Allow all outbound"
ip_protocol = "-1"
cidr_ipv4 = "0.0.0.0/0"
tags = { Name = "all-egress" }
}
################################################################################
# EKS Nodes Security Group
################################################################################
resource "aws_security_group" "eks_nodes" {
count = var.create_eks ? 1 : 0
name_prefix = "${var.name_prefix}-eks-nodes-"
description = "EKS worker nodes"
vpc_id = var.vpc_id
tags = merge(var.tags, {
Name = "${var.name_prefix}-eks-nodes"
Tier = "eks"
"kubernetes.io/cluster/${var.name_prefix}" = "owned"
})
lifecycle {
create_before_destroy = true
}
}
resource "aws_vpc_security_group_ingress_rule" "eks_nodes_self" {
count = var.create_eks ? 1 : 0
security_group_id = aws_security_group.eks_nodes[0].id
description = "Node to node communication"
ip_protocol = "-1"
referenced_security_group_id = aws_security_group.eks_nodes[0].id
tags = { Name = "node-to-node" }
}
resource "aws_vpc_security_group_ingress_rule" "eks_nodes_cluster" {
count = var.create_eks ? 1 : 0
security_group_id = aws_security_group.eks_nodes[0].id
description = "From cluster control plane"
from_port = 1025
to_port = 65535
ip_protocol = "tcp"
referenced_security_group_id = aws_security_group.eks_cluster[0].id
tags = { Name = "from-cluster" }
}
resource "aws_vpc_security_group_egress_rule" "eks_nodes_all" {
count = var.create_eks ? 1 : 0
security_group_id = aws_security_group.eks_nodes[0].id
description = "Allow all outbound"
ip_protocol = "-1"
cidr_ipv4 = "0.0.0.0/0"
tags = { Name = "all-egress" }
}

View File

@@ -0,0 +1,51 @@
################################################################################
# Security Groups - Outputs
################################################################################
output "web_tier_sg_id" {
value = try(aws_security_group.web[0].id, null)
description = "Web tier security group ID"
}
output "app_tier_sg_id" {
value = try(aws_security_group.app[0].id, null)
description = "App tier security group ID"
}
output "db_tier_sg_id" {
value = try(aws_security_group.db[0].id, null)
description = "Database tier security group ID"
}
output "bastion_sg_id" {
value = try(aws_security_group.bastion[0].id, null)
description = "Bastion security group ID"
}
output "endpoints_sg_id" {
value = try(aws_security_group.endpoints[0].id, null)
description = "VPC endpoints security group ID"
}
output "eks_cluster_sg_id" {
value = try(aws_security_group.eks_cluster[0].id, null)
description = "EKS cluster security group ID"
}
output "eks_nodes_sg_id" {
value = try(aws_security_group.eks_nodes[0].id, null)
description = "EKS nodes security group ID"
}
output "all_sg_ids" {
value = {
web = try(aws_security_group.web[0].id, null)
app = try(aws_security_group.app[0].id, null)
db = try(aws_security_group.db[0].id, null)
bastion = try(aws_security_group.bastion[0].id, null)
endpoints = try(aws_security_group.endpoints[0].id, null)
eks_cluster = try(aws_security_group.eks_cluster[0].id, null)
eks_nodes = try(aws_security_group.eks_nodes[0].id, null)
}
description = "Map of all security group IDs"
}

View File

@@ -0,0 +1,79 @@
################################################################################
# Security Groups - Input Variables
################################################################################
variable "vpc_id" {
type = string
description = "VPC ID to create security groups in"
}
variable "name_prefix" {
type = string
description = "Prefix for security group names"
}
variable "create_web_tier" {
type = bool
default = false
description = "Create web tier security group"
}
variable "create_app_tier" {
type = bool
default = false
description = "Create application tier security group"
}
variable "create_db_tier" {
type = bool
default = false
description = "Create database tier security group"
}
variable "create_bastion" {
type = bool
default = false
description = "Create bastion host security group"
}
variable "create_endpoints" {
type = bool
default = false
description = "Create VPC endpoints security group"
}
variable "create_eks" {
type = bool
default = false
description = "Create EKS cluster and node security groups"
}
variable "web_ingress_cidr" {
type = string
default = "0.0.0.0/0"
description = "CIDR for web tier ingress (use ALB SG for production)"
}
variable "app_port" {
type = number
default = 8080
description = "Application port for app tier"
}
variable "db_port" {
type = number
default = 5432
description = "Database port (5432=PostgreSQL, 3306=MySQL)"
}
variable "allowed_ssh_cidrs" {
type = list(string)
default = []
description = "CIDRs allowed SSH access to bastion"
}
variable "tags" {
type = map(string)
default = {}
description = "Tags to apply to security groups"
}