################################################################################ # 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 }) }