Files
sentryagent-idp/terraform/modules/redis/main.tf
SentryAgent.ai Developer 6913d62648 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>
2026-03-29 06:25:14 +00:00

177 lines
5.8 KiB
HCL

################################################################################
# Module: redis
# Main — AWS ElastiCache Redis 7
#
# - Single shard (cluster mode disabled): one primary + one replica
# - Encryption at rest and in transit (TLS)
# - AUTH token required when transit encryption is enabled
# - VPC-internal only — no public access
# - Access restricted to explicitly allowed security groups (app only)
# - Slow log + engine log delivery to CloudWatch
################################################################################
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"
}
}
################################################################################
# CloudWatch Log Group for Redis logs
################################################################################
resource "aws_cloudwatch_log_group" "redis" {
count = var.log_delivery_enabled ? 1 : 0
name = var.log_group_name
retention_in_days = 30
tags = local.common_tags
}
################################################################################
# Security Group — only the app SGs may connect on 6379
################################################################################
resource "aws_security_group" "redis" {
name = "${local.identifier}-redis-sg"
description = "Controls inbound access to ElastiCache Redis — allow only app SG on 6379"
vpc_id = var.vpc_id
egress {
description = "All outbound"
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = merge(local.common_tags, {
Name = "${local.identifier}-redis-sg"
})
}
resource "aws_security_group_rule" "redis_ingress_from_app" {
for_each = toset(var.allowed_security_group_ids)
type = "ingress"
description = "Redis from app security group"
from_port = 6379
to_port = 6379
protocol = "tcp"
source_security_group_id = each.value
security_group_id = aws_security_group.redis.id
}
################################################################################
# ElastiCache Subnet Group
################################################################################
resource "aws_elasticache_subnet_group" "main" {
name = "${local.identifier}-redis-subnet-group"
description = "Private subnets for AgentIdP ElastiCache Redis"
subnet_ids = var.subnet_ids
tags = local.common_tags
}
################################################################################
# ElastiCache Parameter Group — Redis 7.x defaults are fine; custom group
# allows future tuning without recreating the replication group.
################################################################################
resource "aws_elasticache_parameter_group" "main" {
name = "${local.identifier}-redis7-params"
family = "redis7"
description = "AgentIdP Redis 7 parameter group"
# Disable dangerous commands that could truncate data in production
parameter {
name = "lazyfree-lazy-eviction"
value = "yes"
}
parameter {
name = "lazyfree-lazy-expire"
value = "yes"
}
tags = local.common_tags
}
################################################################################
# ElastiCache Replication Group (cluster mode disabled)
#
# cluster_mode = 0 (disabled) gives a single-shard setup:
# - 1 primary node
# - num_cache_clusters - 1 replica nodes
# This matches the application usage: token revocation (SET/GET/DEL),
# rate limiting (INCR/EXPIRE), and monthly counters (INCR) — no sharding needed.
################################################################################
resource "aws_elasticache_replication_group" "main" {
replication_group_id = local.identifier
description = "AgentIdP Redis 7 — token revocation, rate limiting, counters"
# Engine
engine = "redis"
engine_version = var.engine_version
node_type = var.node_type
parameter_group_name = aws_elasticache_parameter_group.main.name
port = 6379
# Topology — single shard, primary + replica
num_cache_clusters = var.num_cache_clusters
automatic_failover_enabled = var.automatic_failover_enabled
multi_az_enabled = var.multi_az_enabled
# Network — VPC-internal, no public endpoints
subnet_group_name = aws_elasticache_subnet_group.main.name
security_group_ids = [aws_security_group.redis.id]
# Security
at_rest_encryption_enabled = var.at_rest_encryption_enabled
transit_encryption_enabled = var.transit_encryption_enabled
auth_token = var.transit_encryption_enabled && var.auth_token != "" ? var.auth_token : null
# Maintenance and snapshots
maintenance_window = var.maintenance_window
snapshot_retention_limit = var.snapshot_retention_limit
snapshot_window = var.snapshot_window
apply_immediately = var.apply_immediately
# Log delivery to CloudWatch
dynamic "log_delivery_configuration" {
for_each = var.log_delivery_enabled ? [
{ log_type = "slow-log", log_format = "json" },
{ log_type = "engine-log", log_format = "json" }
] : []
content {
destination = var.log_delivery_enabled ? aws_cloudwatch_log_group.redis[0].name : ""
destination_type = "cloudwatch-logs"
log_format = log_delivery_configuration.value.log_format
log_type = log_delivery_configuration.value.log_type
}
}
tags = merge(local.common_tags, {
Name = local.identifier
})
}