mirror of
https://github.com/ghndrx/terraform-foundation.git
synced 2026-02-10 06:45:06 +00:00
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:
305
terraform/02-network/main.tf
Normal file
305
terraform/02-network/main.tf
Normal file
@@ -0,0 +1,305 @@
|
||||
################################################################################
|
||||
# Layer 02: Network
|
||||
#
|
||||
# Creates shared VPC:
|
||||
# - Single VPC (cost optimized)
|
||||
# - Public/Private subnets
|
||||
# - Single NAT Gateway
|
||||
# - AWS RAM sharing (multi-account only)
|
||||
#
|
||||
# Depends on: 00-bootstrap (single) or 01-organization (multi)
|
||||
################################################################################
|
||||
|
||||
terraform {
|
||||
required_version = ">= 1.5"
|
||||
|
||||
required_providers {
|
||||
aws = {
|
||||
source = "hashicorp/aws"
|
||||
version = ">= 5.0"
|
||||
}
|
||||
}
|
||||
|
||||
backend "s3" {
|
||||
key = "02-network/terraform.tfstate"
|
||||
}
|
||||
}
|
||||
|
||||
################################################################################
|
||||
# Variables
|
||||
################################################################################
|
||||
|
||||
variable "region" {
|
||||
type = string
|
||||
default = "us-east-1"
|
||||
}
|
||||
|
||||
variable "state_bucket" {
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "deployment_mode" {
|
||||
type = string
|
||||
default = "single-account"
|
||||
|
||||
validation {
|
||||
condition = contains(["single-account", "multi-account"], var.deployment_mode)
|
||||
error_message = "Must be single-account or multi-account"
|
||||
}
|
||||
}
|
||||
|
||||
variable "vpc_cidr" {
|
||||
type = string
|
||||
default = "10.0.0.0/16"
|
||||
}
|
||||
|
||||
variable "azs" {
|
||||
type = list(string)
|
||||
default = ["us-east-1a", "us-east-1b"]
|
||||
}
|
||||
|
||||
variable "enable_nat" {
|
||||
type = bool
|
||||
default = true
|
||||
}
|
||||
|
||||
################################################################################
|
||||
# Data Sources
|
||||
################################################################################
|
||||
|
||||
data "terraform_remote_state" "org" {
|
||||
count = var.deployment_mode == "multi-account" ? 1 : 0
|
||||
backend = "s3"
|
||||
config = {
|
||||
bucket = var.state_bucket
|
||||
key = "01-organization/terraform.tfstate"
|
||||
region = var.region
|
||||
}
|
||||
}
|
||||
|
||||
################################################################################
|
||||
# Provider
|
||||
################################################################################
|
||||
|
||||
provider "aws" {
|
||||
region = var.region
|
||||
|
||||
default_tags {
|
||||
tags = {
|
||||
Layer = "02-network"
|
||||
ManagedBy = "terraform"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
################################################################################
|
||||
# VPC
|
||||
################################################################################
|
||||
|
||||
resource "aws_vpc" "main" {
|
||||
cidr_block = var.vpc_cidr
|
||||
enable_dns_hostnames = true
|
||||
enable_dns_support = true
|
||||
|
||||
tags = { Name = "shared-vpc" }
|
||||
}
|
||||
|
||||
resource "aws_internet_gateway" "main" {
|
||||
vpc_id = aws_vpc.main.id
|
||||
tags = { Name = "shared-igw" }
|
||||
}
|
||||
|
||||
################################################################################
|
||||
# Subnets
|
||||
################################################################################
|
||||
|
||||
resource "aws_subnet" "public" {
|
||||
count = length(var.azs)
|
||||
vpc_id = aws_vpc.main.id
|
||||
cidr_block = cidrsubnet(var.vpc_cidr, 4, count.index)
|
||||
availability_zone = var.azs[count.index]
|
||||
map_public_ip_on_launch = true
|
||||
|
||||
tags = { Name = "public-${var.azs[count.index]}", Type = "public" }
|
||||
}
|
||||
|
||||
resource "aws_subnet" "private" {
|
||||
count = length(var.azs)
|
||||
vpc_id = aws_vpc.main.id
|
||||
cidr_block = cidrsubnet(var.vpc_cidr, 4, count.index + 4)
|
||||
availability_zone = var.azs[count.index]
|
||||
|
||||
tags = { Name = "private-${var.azs[count.index]}", Type = "private" }
|
||||
}
|
||||
|
||||
################################################################################
|
||||
# NAT Gateway
|
||||
################################################################################
|
||||
|
||||
resource "aws_eip" "nat" {
|
||||
count = var.enable_nat ? 1 : 0
|
||||
domain = "vpc"
|
||||
tags = { Name = "nat-eip" }
|
||||
}
|
||||
|
||||
resource "aws_nat_gateway" "main" {
|
||||
count = var.enable_nat ? 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.main]
|
||||
}
|
||||
|
||||
################################################################################
|
||||
# Route Tables
|
||||
################################################################################
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
tags = { Name = "public-rt" }
|
||||
}
|
||||
|
||||
resource "aws_route_table" "private" {
|
||||
vpc_id = aws_vpc.main.id
|
||||
|
||||
dynamic "route" {
|
||||
for_each = var.enable_nat ? [1] : []
|
||||
content {
|
||||
cidr_block = "0.0.0.0/0"
|
||||
nat_gateway_id = aws_nat_gateway.main[0].id
|
||||
}
|
||||
}
|
||||
|
||||
tags = { Name = "private-rt" }
|
||||
}
|
||||
|
||||
resource "aws_route_table_association" "public" {
|
||||
count = length(var.azs)
|
||||
subnet_id = aws_subnet.public[count.index].id
|
||||
route_table_id = aws_route_table.public.id
|
||||
}
|
||||
|
||||
resource "aws_route_table_association" "private" {
|
||||
count = length(var.azs)
|
||||
subnet_id = aws_subnet.private[count.index].id
|
||||
route_table_id = aws_route_table.private.id
|
||||
}
|
||||
|
||||
################################################################################
|
||||
# Default SG - Deny All
|
||||
################################################################################
|
||||
|
||||
resource "aws_default_security_group" "default" {
|
||||
vpc_id = aws_vpc.main.id
|
||||
tags = { Name = "default-deny-all" }
|
||||
}
|
||||
|
||||
################################################################################
|
||||
# VPC Flow Logs (Audit Trail)
|
||||
################################################################################
|
||||
|
||||
resource "aws_flow_log" "main" {
|
||||
vpc_id = aws_vpc.main.id
|
||||
traffic_type = "ALL"
|
||||
log_destination_type = "cloud-watch-logs"
|
||||
log_destination = aws_cloudwatch_log_group.flow_logs.arn
|
||||
iam_role_arn = aws_iam_role.flow_logs.arn
|
||||
max_aggregation_interval = 60 # 1 minute for better visibility
|
||||
|
||||
tags = { Name = "vpc-flow-logs" }
|
||||
}
|
||||
|
||||
resource "aws_cloudwatch_log_group" "flow_logs" {
|
||||
name = "/aws/vpc/flow-logs"
|
||||
retention_in_days = 90
|
||||
|
||||
tags = { Name = "vpc-flow-logs" }
|
||||
}
|
||||
|
||||
resource "aws_iam_role" "flow_logs" {
|
||||
name = "vpc-flow-logs"
|
||||
|
||||
assume_role_policy = jsonencode({
|
||||
Version = "2012-10-17"
|
||||
Statement = [{
|
||||
Effect = "Allow"
|
||||
Action = "sts:AssumeRole"
|
||||
Principal = { Service = "vpc-flow-logs.amazonaws.com" }
|
||||
}]
|
||||
})
|
||||
|
||||
tags = { Name = "vpc-flow-logs" }
|
||||
}
|
||||
|
||||
resource "aws_iam_role_policy" "flow_logs" {
|
||||
name = "vpc-flow-logs"
|
||||
role = aws_iam_role.flow_logs.id
|
||||
|
||||
policy = jsonencode({
|
||||
Version = "2012-10-17"
|
||||
Statement = [{
|
||||
Effect = "Allow"
|
||||
Action = [
|
||||
"logs:CreateLogGroup",
|
||||
"logs:CreateLogStream",
|
||||
"logs:PutLogEvents",
|
||||
"logs:DescribeLogGroups",
|
||||
"logs:DescribeLogStreams"
|
||||
]
|
||||
Resource = "*"
|
||||
}]
|
||||
})
|
||||
}
|
||||
|
||||
################################################################################
|
||||
# RAM Sharing (multi-account only)
|
||||
################################################################################
|
||||
|
||||
resource "aws_ram_resource_share" "subnets" {
|
||||
count = var.deployment_mode == "multi-account" ? 1 : 0
|
||||
name = "shared-subnets"
|
||||
allow_external_principals = false
|
||||
}
|
||||
|
||||
resource "aws_ram_resource_association" "private" {
|
||||
count = var.deployment_mode == "multi-account" ? length(var.azs) : 0
|
||||
resource_arn = aws_subnet.private[count.index].arn
|
||||
resource_share_arn = aws_ram_resource_share.subnets[0].arn
|
||||
}
|
||||
|
||||
resource "aws_ram_principal_association" "workloads" {
|
||||
count = var.deployment_mode == "multi-account" ? 1 : 0
|
||||
principal = data.terraform_remote_state.org[0].outputs.ou_ids.workloads
|
||||
resource_share_arn = aws_ram_resource_share.subnets[0].arn
|
||||
}
|
||||
|
||||
################################################################################
|
||||
# Outputs
|
||||
################################################################################
|
||||
|
||||
output "vpc_id" {
|
||||
value = aws_vpc.main.id
|
||||
}
|
||||
|
||||
output "vpc_cidr" {
|
||||
value = aws_vpc.main.cidr_block
|
||||
}
|
||||
|
||||
output "public_subnet_ids" {
|
||||
value = aws_subnet.public[*].id
|
||||
}
|
||||
|
||||
output "private_subnet_ids" {
|
||||
value = aws_subnet.private[*].id
|
||||
}
|
||||
|
||||
output "nat_ip" {
|
||||
value = var.enable_nat ? aws_eip.nat[0].public_ip : null
|
||||
}
|
||||
Reference in New Issue
Block a user