Skip to content

Commit

Permalink
Merge pull request #67 from dxw/elasticache-redis
Browse files Browse the repository at this point in the history
ElastiCache Redis
  • Loading branch information
Stretch96 authored Mar 22, 2024
2 parents 24852f4 + 6d19f96 commit 2fb265f
Show file tree
Hide file tree
Showing 6 changed files with 177 additions and 0 deletions.
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 |
Expand Down Expand Up @@ -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 |
Expand All @@ -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 |
Expand Down Expand Up @@ -317,6 +323,8 @@ This project creates and manages resources within an AWS account for infrastruct
| <a name="input_infrastructure_ecs_cluster_services_alb_ip_allow_list"></a> [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 |
| <a name="input_infrastructure_ecs_cluster_services_alb_logs_retention"></a> [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 |
| <a name="input_infrastructure_ecs_cluster_termination_timeout"></a> [infrastructure\_ecs\_cluster\_termination\_timeout](#input\_infrastructure\_ecs\_cluster\_termination\_timeout) | The timeout for the terminiation lifecycle hook | `number` | n/a | yes |
| <a name="input_infrastructure_elasticache"></a> [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."<br> {<br> elasticache-name = {<br> type: Choose either `cluster` or `serverless`<br> engine: ElastiCache engine (Only `redis` is currently supported)<br> engine\_version: ElastiCache Engine version (For serverless, Specify the major version only)<br> parameters: Map of Parameters for the ElastiCache parameter group ({ parameter-name = parameter-value, ... })<br> cluster\_node\_type: ElastiCache Cluster node type<br> cluster\_node\_count: ElastiCache Cluster node count<br> serverless\_max\_storage: Serverless maximum storage<br> serverless\_max\_ecpu: Serverless maximum number of ECPUs the cache can consume per second (1000 - 15000000)<br> snapshot\_retention\_limit: Snapshot retention limit<br> }<br> } | <pre>map(object({<br> type = optional(string, null)<br> engine = optional(string, null)<br> engine_version = optional(string, null)<br> parameters = optional(map(string), null)<br> cluster_node_type = optional(string, null)<br> cluster_node_count = optional(number, null)<br> serverless_max_storage = optional(string, null)<br> serverless_max_ecpu = optional(number, null)<br> snapshot_retention_limit = optional(number, null)<br> }))</pre> | n/a | yes |
| <a name="input_infrastructure_elasticache_defaults"></a> [infrastructure\_elasticache\_defaults](#input\_infrastructure\_elasticache\_defaults) | Default values for ElastiCaches | <pre>object({<br> type = optional(string, null)<br> engine = optional(string, null)<br> engine_version = optional(string, null)<br> parameters = optional(map(string), null)<br> cluster_node_type = optional(string, null)<br> cluster_node_count = optional(number, null)<br> serverless_max_storage = optional(number, null)<br> serverless_max_ecpu = optional(number, null)<br> snapshot_retention_limit = optional(number, null)<br> })</pre> | n/a | yes |
| <a name="input_infrastructure_kms_encryption"></a> [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 |
| <a name="input_infrastructure_logging_bucket_retention"></a> [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 |
| <a name="input_infrastructure_name"></a> [infrastructure\_name](#input\_infrastructure\_name) | The infrastructure name to be used as part of the resource prefix | `string` | n/a | yes |
Expand Down
66 changes: 66 additions & 0 deletions elasticache-infrastructure-cluster.tf
Original file line number Diff line number Diff line change
@@ -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}"
}
}
20 changes: 20 additions & 0 deletions elasticache-infrastructure-security-group.tf
Original file line number Diff line number Diff line change
@@ -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
}
27 changes: 27 additions & 0 deletions elasticache-infrastructure-serverless.tf
Original file line number Diff line number Diff line change
@@ -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}"
}
}
11 changes: 11 additions & 0 deletions locals.tf
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
45 changes: 45 additions & 0 deletions variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -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 = <<EOT
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
}
}
EOT
type = map(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(string, null)
serverless_max_ecpu = optional(number, null)
snapshot_retention_limit = optional(number, null)
}))
}

variable "custom_route53_hosted_zones" {
description = <<EOT
Map of Route53 Hosted Zone configurations to create
Expand Down

0 comments on commit 2fb265f

Please sign in to comment.