feat(phase-2): workstream 8 — Multi-Region Terraform Deployment
AWS environment: - VPC (3-AZ, public + private subnets, NAT gateways, VPC endpoints for ECR/SM/CW) - ECS Fargate service (sentryagent/agentidp) — secrets from Secrets Manager - RDS PostgreSQL 14 (Multi-AZ, encrypted, VPC-internal, storage autoscaling) - ElastiCache Redis 7 (primary + replica, at-rest + in-transit encryption) - ALB with HTTPS/443, HTTP→HTTPS redirect, ACM certificate - Route 53 alias record GCP environment: - VPC + private services access + Serverless VPC connector - Cloud Run service — secrets from Secret Manager - Cloud SQL PostgreSQL 14 (private IP, no public endpoint) - Cloud Memorystore Redis 7 (VPC-internal, AUTH enabled) Shared: - 4 reusable modules: agentidp (dual AWS/GCP), rds, redis, lb - No hardcoded secrets; all sensitive vars marked sensitive=true - terraform.tfvars.example for both environments - docs/devops/deployment.md — AWS + GCP step-by-step walkthrough, rollback procedures Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
180
terraform/modules/rds/main.tf
Normal file
180
terraform/modules/rds/main.tf
Normal file
@@ -0,0 +1,180 @@
|
||||
################################################################################
|
||||
# Module: rds
|
||||
# Main — AWS RDS PostgreSQL 14
|
||||
#
|
||||
# - Multi-AZ for HA
|
||||
# - Encryption at rest (AWS-managed KMS key)
|
||||
# - No public access — VPC-internal only
|
||||
# - Storage autoscaling up to max_allocated_storage
|
||||
# - Enhanced monitoring and Performance Insights enabled by default
|
||||
# - Access restricted to explicitly allowed security groups (app only)
|
||||
################################################################################
|
||||
|
||||
terraform {
|
||||
required_version = ">= 1.6.0"
|
||||
|
||||
required_providers {
|
||||
aws = {
|
||||
source = "hashicorp/aws"
|
||||
version = ">= 5.40.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
locals {
|
||||
identifier = "${var.project}-${var.environment}"
|
||||
|
||||
common_tags = {
|
||||
environment = var.environment
|
||||
project = var.project
|
||||
managed_by = "terraform"
|
||||
}
|
||||
}
|
||||
|
||||
################################################################################
|
||||
# Security Group — only the app SGs may connect on 5432
|
||||
################################################################################
|
||||
|
||||
resource "aws_security_group" "rds" {
|
||||
name = "${local.identifier}-rds-sg"
|
||||
description = "Controls inbound access to RDS PostgreSQL — allow only app SG on 5432"
|
||||
vpc_id = var.vpc_id
|
||||
|
||||
# No ingress rules defined here — added dynamically below to avoid circular deps.
|
||||
egress {
|
||||
description = "All outbound (RDS initiates no outbound connections; this satisfies AWS requirement)"
|
||||
from_port = 0
|
||||
to_port = 0
|
||||
protocol = "-1"
|
||||
cidr_blocks = ["0.0.0.0/0"]
|
||||
}
|
||||
|
||||
tags = merge(local.common_tags, {
|
||||
Name = "${local.identifier}-rds-sg"
|
||||
})
|
||||
}
|
||||
|
||||
resource "aws_security_group_rule" "rds_ingress_from_app" {
|
||||
for_each = toset(var.allowed_security_group_ids)
|
||||
|
||||
type = "ingress"
|
||||
description = "PostgreSQL from app security group"
|
||||
from_port = 5432
|
||||
to_port = 5432
|
||||
protocol = "tcp"
|
||||
source_security_group_id = each.value
|
||||
security_group_id = aws_security_group.rds.id
|
||||
}
|
||||
|
||||
################################################################################
|
||||
# DB Subnet Group — must cover at least 2 AZs for Multi-AZ
|
||||
################################################################################
|
||||
|
||||
resource "aws_db_subnet_group" "main" {
|
||||
name = "${local.identifier}-db-subnet-group"
|
||||
description = "Private subnets for AgentIdP RDS instance"
|
||||
subnet_ids = var.subnet_ids
|
||||
|
||||
tags = merge(local.common_tags, {
|
||||
Name = "${local.identifier}-db-subnet-group"
|
||||
})
|
||||
}
|
||||
|
||||
################################################################################
|
||||
# DB Parameter Group — enforce SSL connections
|
||||
################################################################################
|
||||
|
||||
resource "aws_db_parameter_group" "main" {
|
||||
name = "${local.identifier}-pg14-params"
|
||||
family = var.parameter_group_family
|
||||
description = "AgentIdP custom parameter group — enforces SSL"
|
||||
|
||||
parameter {
|
||||
name = "rds.force_ssl"
|
||||
value = "1"
|
||||
apply_method = "immediate"
|
||||
}
|
||||
|
||||
parameter {
|
||||
name = "log_connections"
|
||||
value = "1"
|
||||
apply_method = "immediate"
|
||||
}
|
||||
|
||||
parameter {
|
||||
name = "log_disconnections"
|
||||
value = "1"
|
||||
apply_method = "immediate"
|
||||
}
|
||||
|
||||
parameter {
|
||||
name = "log_min_duration_statement"
|
||||
value = "1000"
|
||||
apply_method = "immediate"
|
||||
}
|
||||
|
||||
tags = local.common_tags
|
||||
}
|
||||
|
||||
################################################################################
|
||||
# RDS Instance
|
||||
################################################################################
|
||||
|
||||
resource "aws_db_instance" "main" {
|
||||
identifier = local.identifier
|
||||
|
||||
# Engine
|
||||
engine = "postgres"
|
||||
engine_version = "14"
|
||||
instance_class = var.instance_class
|
||||
|
||||
# Storage
|
||||
storage_type = "gp3"
|
||||
allocated_storage = var.allocated_storage
|
||||
max_allocated_storage = var.max_allocated_storage
|
||||
storage_encrypted = true
|
||||
# kms_key_id is omitted — defaults to the AWS-managed RDS KMS key.
|
||||
# For customer-managed key, set kms_key_id to your CMK ARN.
|
||||
|
||||
# Database
|
||||
db_name = var.db_name
|
||||
username = var.db_username
|
||||
password = var.db_password
|
||||
|
||||
# Network — VPC-internal only, no public endpoint
|
||||
db_subnet_group_name = aws_db_subnet_group.main.name
|
||||
vpc_security_group_ids = [aws_security_group.rds.id]
|
||||
publicly_accessible = false
|
||||
multi_az = var.multi_az
|
||||
port = 5432
|
||||
|
||||
# Parameter group
|
||||
parameter_group_name = aws_db_parameter_group.main.name
|
||||
|
||||
# Backups
|
||||
backup_retention_period = var.backup_retention_days
|
||||
backup_window = var.backup_window
|
||||
delete_automated_backups = false
|
||||
copy_tags_to_snapshot = true
|
||||
skip_final_snapshot = var.skip_final_snapshot
|
||||
final_snapshot_identifier = var.skip_final_snapshot ? null : "${local.identifier}-final-snapshot"
|
||||
|
||||
# Maintenance
|
||||
maintenance_window = var.maintenance_window
|
||||
auto_minor_version_upgrade = true
|
||||
apply_immediately = false
|
||||
|
||||
# Observability
|
||||
enabled_cloudwatch_logs_exports = ["postgresql", "upgrade"]
|
||||
performance_insights_enabled = var.performance_insights_enabled
|
||||
performance_insights_retention_period = var.performance_insights_enabled ? var.performance_insights_retention_period : null
|
||||
monitoring_interval = var.monitoring_interval
|
||||
monitoring_role_arn = var.monitoring_interval > 0 ? var.monitoring_role_arn : null
|
||||
|
||||
# Protection
|
||||
deletion_protection = var.deletion_protection
|
||||
|
||||
tags = merge(local.common_tags, {
|
||||
Name = local.identifier
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user