diff --git a/README.md b/README.md
index 46717e4..f21c87e 100644
--- a/README.md
+++ b/README.md
@@ -80,6 +80,10 @@ This project creates and manages resources within an AWS account for infrastruct
| [aws_efs_file_system.infrastructure_ecs_cluster](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/efs_file_system) | resource |
| [aws_efs_mount_target.infrastructure_ecs_cluster](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/efs_mount_target) | resource |
| [aws_eip.infrastructure_nat](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/eip) | resource |
+| [aws_elasticache_parameter_group.infrastructure_elasticache_cluster](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/elasticache_parameter_group) | resource |
+| [aws_elasticache_replication_group.infrastructure_elasticache_cluster](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/elasticache_replication_group) | resource |
+| [aws_elasticache_serverless_cache.infrastructure_elasticache](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/elasticache_serverless_cache) | resource |
+| [aws_elasticache_subnet_group.infrastructure_elasticache_cluster_subnet_group](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/elasticache_subnet_group) | resource |
| [aws_flow_log.infrastructure_vpc_flow_logs_cloudwatch](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/flow_log) | resource |
| [aws_flow_log.infrastructure_vpc_flow_logs_s3](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/flow_log) | resource |
| [aws_globalaccelerator_accelerator.infrastructure_ecs_cluster_service_alb](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/globalaccelerator_accelerator) | resource |
@@ -245,6 +249,7 @@ This project creates and manages resources within an AWS account for infrastruct
| [aws_security_group.infrastructure_ecs_cluster_container_instances](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group) | resource |
| [aws_security_group.infrastructure_ecs_cluster_efs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group) | resource |
| [aws_security_group.infrastructure_ecs_cluster_service_alb](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group) | resource |
+| [aws_security_group.infrastructure_elasticache](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group) | resource |
| [aws_security_group.infrastructure_rds](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group) | resource |
| [aws_security_group_rule.infrastructure_ecs_cluster_container_instances_egress_dns_tcp](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource |
| [aws_security_group_rule.infrastructure_ecs_cluster_container_instances_egress_dns_udp](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource |
@@ -258,6 +263,7 @@ This project creates and manages resources within an AWS account for infrastruct
| [aws_security_group_rule.infrastructure_ecs_cluster_service_alb_container_instance_egress_udp](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource |
| [aws_security_group_rule.infrastructure_ecs_cluster_service_alb_http](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource |
| [aws_security_group_rule.infrastructure_ecs_cluster_service_alb_https](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource |
+| [aws_security_group_rule.infrastructure_elasticache_ingress_tcp](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource |
| [aws_security_group_rule.infrastructure_rds_ingress_tcp](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource |
| [aws_sns_topic.infrastructure_ecs_cluster_autoscaling_lifecycle_termination](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sns_topic) | resource |
| [aws_sns_topic_subscription.ecs_cluster_infrastructure_draining_autoscaling_lifecycle_termination](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sns_topic_subscription) | resource |
@@ -317,6 +323,8 @@ This project creates and manages resources within an AWS account for infrastruct
| [infrastructure\_ecs\_cluster\_services\_alb\_ip\_allow\_list](#input\_infrastructure\_ecs\_cluster\_services\_alb\_ip\_allow\_list) | IP allow list for ingress traffic to the infrastructure ECS cluster services ALB | `list(string)` | n/a | yes |
| [infrastructure\_ecs\_cluster\_services\_alb\_logs\_retention](#input\_infrastructure\_ecs\_cluster\_services\_alb\_logs\_retention) | Retention in days for the infrasrtucture ecs cluster ALB logs | `number` | n/a | yes |
| [infrastructure\_ecs\_cluster\_termination\_timeout](#input\_infrastructure\_ecs\_cluster\_termination\_timeout) | The timeout for the terminiation lifecycle hook | `number` | n/a | yes |
+| [infrastructure\_elasticache](#input\_infrastructure\_elasticache) | Map of Elasticaches (The key will be the elasticache name). Values in here will override `infrastructure_elasticache_defaults` values if set."
{
elasticache-name = {
type: Choose either `cluster` or `serverless`
engine: ElastiCache engine (Only `redis` is currently supported)
engine\_version: ElastiCache Engine version (For serverless, Specify the major version only)
parameters: Map of Parameters for the ElastiCache parameter group ({ parameter-name = parameter-value, ... })
cluster\_node\_type: ElastiCache Cluster node type
cluster\_node\_count: ElastiCache Cluster node count
serverless\_max\_storage: Serverless maximum storage
serverless\_max\_ecpu: Serverless maximum number of ECPUs the cache can consume per second (1000 - 15000000)
snapshot\_retention\_limit: Snapshot retention limit
}
} |
map(object({| n/a | yes | +| [infrastructure\_elasticache\_defaults](#input\_infrastructure\_elasticache\_defaults) | Default values for ElastiCaches |
type = optional(string, null)
engine = optional(string, null)
engine_version = optional(string, null)
parameters = optional(map(string), null)
cluster_node_type = optional(string, null)
cluster_node_count = optional(number, null)
serverless_max_storage = optional(string, null)
serverless_max_ecpu = optional(number, null)
snapshot_retention_limit = optional(number, null)
}))
object({| n/a | yes | | [infrastructure\_kms\_encryption](#input\_infrastructure\_kms\_encryption) | Enable infrastructure KMS encryption. This will create a single KMS key to be used across all resources that support KMS encryption. | `bool` | n/a | yes | | [infrastructure\_logging\_bucket\_retention](#input\_infrastructure\_logging\_bucket\_retention) | Retention in days for the infrasrtucture S3 logs. This is for the default S3 logs bucket, where all AWS service logs will be delivered | `number` | n/a | yes | | [infrastructure\_name](#input\_infrastructure\_name) | The infrastructure name to be used as part of the resource prefix | `string` | n/a | yes | diff --git a/elasticache-infrastructure-cluster.tf b/elasticache-infrastructure-cluster.tf new file mode 100644 index 0000000..b953db9 --- /dev/null +++ b/elasticache-infrastructure-cluster.tf @@ -0,0 +1,66 @@ +resource "aws_elasticache_parameter_group" "infrastructure_elasticache_cluster" { + for_each = local.infrastructure_vpc_network_enable_public || local.infrastructure_vpc_network_enable_private ? { + for k, v in local.infrastructure_elasticache : k => merge(v, { + parameter_group_version = replace(v["engine_version"], "6.", "") != v["engine_version"] ? "6.x" : ( + replace(v["engine_version"], "7.", "") != v["engine_version"] ? "7" : replace(v["engine_version"], "/\\.[\\d]+$/", "") + ) + }) if v["engine"] == "redis" && v["type"] == "cluster" + } : {} + + name = "pg-${local.resource_prefix_hash}-${each.key}-${replace(each.value["parameter_group_version"], ".", "-")}" + + description = "Parameter Group for ${local.resource_prefix_hash} ${each.key} ${each.value["parameter_group_version"]}" + + family = "${each.value["engine"]}${each.value["parameter_group_version"]}" + + dynamic "parameter" { + for_each = each.value["parameters"] != null ? each.value["parameters"] : {} + content { + name = parameter.value.name + value = parameter.value.value + } + } + + lifecycle { + create_before_destroy = true + } +} + +resource "aws_elasticache_subnet_group" "infrastructure_elasticache_cluster_subnet_group" { + for_each = local.infrastructure_vpc_network_enable_public || local.infrastructure_vpc_network_enable_private ? { + for k, v in local.infrastructure_elasticache : k => v if v["engine"] == "redis" && v["type"] == "cluster" + } : {} + + name = "id-${local.resource_prefix_hash}-${substr(sha512(each.key), 0, 6)}" + subnet_ids = local.infrastructure_vpc_network_enable_private ? [for subnet in aws_subnet.infrastructure_private : subnet.id] : local.infrastructure_vpc_network_enable_public ? [for subnet in aws_subnet.infrastructure_public : subnet.id] : null +} + +resource "aws_elasticache_replication_group" "infrastructure_elasticache_cluster" { + for_each = local.infrastructure_vpc_network_enable_public || local.infrastructure_vpc_network_enable_private ? { + for k, v in local.infrastructure_elasticache : k => v if v["engine"] == "redis" && v["type"] == "cluster" + } : {} + + replication_group_id = "id-${local.resource_prefix_hash}-${substr(sha512(each.key), 0, 6)}" + description = "ElastiCache replication group for ${local.resource_prefix} ${each.key}" + + node_type = each.value["cluster_node_type"] + num_cache_clusters = each.value["cluster_node_count"] + engine_version = each.value["engine_version"] + port = local.elasticache_ports[each.value["engine"]] + at_rest_encryption_enabled = true + + parameter_group_name = aws_elasticache_parameter_group.infrastructure_elasticache_cluster[each.key].id + subnet_group_name = aws_elasticache_subnet_group.infrastructure_elasticache_cluster_subnet_group[each.key].id + security_group_ids = [aws_security_group.infrastructure_elasticache[each.key].id] + + maintenance_window = "Mon:19:00-Mon:22:00" + snapshot_window = "22:00-23:59" + snapshot_retention_limit = each.value["snapshot_retention_limit"] != null ? each.value["snapshot_retention_limit"] : 0 + + automatic_failover_enabled = false + apply_immediately = true + + tags = { + Name = "${local.resource_prefix}-${each.key}" + } +} diff --git a/elasticache-infrastructure-security-group.tf b/elasticache-infrastructure-security-group.tf new file mode 100644 index 0000000..50ad680 --- /dev/null +++ b/elasticache-infrastructure-security-group.tf @@ -0,0 +1,20 @@ +resource "aws_security_group" "infrastructure_elasticache" { + for_each = local.infrastructure_vpc_network_enable_public || local.infrastructure_vpc_network_enable_private ? local.infrastructure_elasticache : {} + + name = "${local.resource_prefix}-infrastructure-elasticache-${each.key}" + description = "Infrastructure ElastiCache" + vpc_id = aws_vpc.infrastructure[0].id +} + +resource "aws_security_group_rule" "infrastructure_elasticache_ingress_tcp" { + for_each = local.infrastructure_vpc_network_enable_public || local.infrastructure_vpc_network_enable_private ? local.infrastructure_elasticache : {} + + description = "Allow ElastiCache port tcp ingress from ECS instances if launched, otherwise from the subnet" + type = "ingress" + from_port = local.elasticache_ports[each.value["engine"]] + to_port = local.elasticache_ports[each.value["engine"]] + protocol = "tcp" + cidr_blocks = local.enable_infrastructure_ecs_cluster ? null : local.infrastructure_vpc_network_enable_private ? [for subnet in aws_subnet.infrastructure_private : subnet.cidr_block] : local.infrastructure_vpc_network_enable_public ? [for subnet in aws_subnet.infrastructure_public : subnet.cidr_block] : null + source_security_group_id = local.enable_infrastructure_ecs_cluster ? aws_security_group.infrastructure_ecs_cluster_container_instances[0].id : null + security_group_id = aws_security_group.infrastructure_elasticache[each.key].id +} diff --git a/elasticache-infrastructure-serverless.tf b/elasticache-infrastructure-serverless.tf new file mode 100644 index 0000000..b630d83 --- /dev/null +++ b/elasticache-infrastructure-serverless.tf @@ -0,0 +1,27 @@ +resource "aws_elasticache_serverless_cache" "infrastructure_elasticache" { + for_each = local.infrastructure_vpc_network_enable_public || local.infrastructure_vpc_network_enable_private ? { + for k, v in local.infrastructure_elasticache : k => v if v["engine"] == "redis" && v["type"] == "serverless" + } : {} + + engine = each.value["engine"] + name = "id-${local.resource_prefix_hash}-${substr(sha512(each.key), 0, 6)}" + cache_usage_limits { + data_storage { + maximum = each.value["serverless_max_storage"] + unit = "GB" + } + ecpu_per_second { + maximum = each.value["serverless_max_ecpu"] + } + } + description = "${local.resource_prefix} ${each.key}" + kms_key_id = local.infrastructure_kms_encryption ? aws_kms_key.infrastructure[0].arn : null + major_engine_version = each.value["engine_version"] + snapshot_retention_limit = each.value["snapshot_retention_limit"] != null ? each.value["snapshot_retention_limit"] : 0 + security_group_ids = [aws_security_group.infrastructure_elasticache[each.key].id] + subnet_ids = local.infrastructure_vpc_network_enable_private ? [for subnet in aws_subnet.infrastructure_private : subnet.id] : local.infrastructure_vpc_network_enable_public ? [for subnet in aws_subnet.infrastructure_public : subnet.id] : null + + tags = { + Name = "${local.resource_prefix}-${each.key}" + } +} diff --git a/locals.tf b/locals.tf index 1ed52d2..12a8567 100644 --- a/locals.tf +++ b/locals.tf @@ -179,6 +179,17 @@ locals { "postgres" = 5432 } + infrastructure_elasticache_defaults = var.infrastructure_elasticache_defaults + infrastructure_elasticache_keys = length(var.infrastructure_elasticache) > 0 ? keys(values(var.infrastructure_elasticache)[0]) : [] + infrastructure_elasticache = { + for k, v in var.infrastructure_elasticache : k => merge({ + for elasticache_key in local.infrastructure_elasticache_keys : elasticache_key => try(coalesce(v[elasticache_key], local.infrastructure_elasticache_defaults[elasticache_key]), null) + }) + } + elasticache_ports = { + "redis" = 6379 + } + custom_route53_hosted_zones = var.custom_route53_hosted_zones custom_s3_buckets = var.custom_s3_buckets diff --git a/variables.tf b/variables.tf index 4ed06db..3e7e6ed 100644 --- a/variables.tf +++ b/variables.tf @@ -458,6 +458,51 @@ variable "infrastructure_rds" { })) } +variable "infrastructure_elasticache_defaults" { + description = "Default values for ElastiCaches" + type = object({ + type = optional(string, null) + engine = optional(string, null) + engine_version = optional(string, null) + parameters = optional(map(string), null) + cluster_node_type = optional(string, null) + cluster_node_count = optional(number, null) + serverless_max_storage = optional(number, null) + serverless_max_ecpu = optional(number, null) + snapshot_retention_limit = optional(number, null) + }) +} + +variable "infrastructure_elasticache" { + description = <
type = optional(string, null)
engine = optional(string, null)
engine_version = optional(string, null)
parameters = optional(map(string), null)
cluster_node_type = optional(string, null)
cluster_node_count = optional(number, null)
serverless_max_storage = optional(number, null)
serverless_max_ecpu = optional(number, null)
snapshot_retention_limit = optional(number, null)
})