From ea175ae0cd3e3a8635155104214c782fc4eb805a Mon Sep 17 00:00:00 2001 From: Nathan Yergler Date: Fri, 6 Feb 2026 11:06:21 -0800 Subject: [PATCH] Update ECS module to use AWS provider v6 This commit upgrades the ECS module to be compatible with AWS provider version 6. As part of this we fork the elasticache-redis module, since it has not been updated to support AWS provider v6. --- .claude/settings.json | 5 +- terraform/modules/ecs/README.md | 30 +- terraform/modules/ecs/buckets.tf | 2 +- terraform/modules/ecs/database.tf | 2 +- terraform/modules/ecs/ecs-cluster.tf | 2 +- terraform/modules/ecs/efs.tf | 2 +- terraform/modules/ecs/logs.tf | 4 +- terraform/modules/ecs/redis.tf | 2 +- terraform/modules/ecs/security-groups.tf | 8 +- terraform/modules/ecs/stats.tf | 2 +- terraform/modules/ecs/versions.tf | 4 +- terraform/modules/ecs/vpc.tf | 2 +- terraform/modules/elasticache-redis/README.md | 99 +++++++ terraform/modules/elasticache-redis/main.tf | 173 ++++++++++++ .../modules/elasticache-redis/outputs.tf | 80 ++++++ .../modules/elasticache-redis/variables.tf | 264 ++++++++++++++++++ .../modules/elasticache-redis/versions.tf | 15 + 17 files changed, 665 insertions(+), 31 deletions(-) create mode 100644 terraform/modules/elasticache-redis/README.md create mode 100644 terraform/modules/elasticache-redis/main.tf create mode 100644 terraform/modules/elasticache-redis/outputs.tf create mode 100644 terraform/modules/elasticache-redis/variables.tf create mode 100644 terraform/modules/elasticache-redis/versions.tf diff --git a/.claude/settings.json b/.claude/settings.json index 5aa6bd5..df4e0bb 100644 --- a/.claude/settings.json +++ b/.claude/settings.json @@ -2,7 +2,10 @@ "permissions": { "allow": [ "Bash(helm dependency list:*)", - "Bash(terraform-docs markdown table:*)" + "Bash(terraform-docs markdown table:*)", + "WebFetch(domain:registry.terraform.io)", + "Bash(gh issue view:*)", + "Bash(terraform validate:*)" ] } } diff --git a/terraform/modules/ecs/README.md b/terraform/modules/ecs/README.md index f9db54c..1005ca8 100644 --- a/terraform/modules/ecs/README.md +++ b/terraform/modules/ecs/README.md @@ -184,8 +184,8 @@ module "polytomic-ecs" { | Name | Version | |------|---------| -| [terraform](#requirement\_terraform) | >= 1.0 | -| [aws](#requirement\_aws) | >= 4.0, < 6.0.0 | +| [terraform](#requirement\_terraform) | >= 1.11 | +| [aws](#requirement\_aws) | >= 6.0, < 7.0 | | [null](#requirement\_null) | >= 3.0 | | [random](#requirement\_random) | >= 3.0 | @@ -248,22 +248,22 @@ module "polytomic-ecs" { | Name | Source | Version | |------|--------|---------| -| [database](#module\_database) | terraform-aws-modules/rds/aws | 5.9.0 | -| [database\_sg](#module\_database\_sg) | terraform-aws-modules/security-group/aws | ~> 4.0 | -| [ecs](#module\_ecs) | terraform-aws-modules/ecs/aws | <5.0.0 | +| [database](#module\_database) | terraform-aws-modules/rds/aws | ~> 7.0 | +| [database\_sg](#module\_database\_sg) | terraform-aws-modules/security-group/aws | ~> 5.0 | +| [ecs](#module\_ecs) | terraform-aws-modules/ecs/aws | ~> 6.0 | | [ecs-alerts-worker](#module\_ecs-alerts-worker) | ../monitoring/ecs-alerts | n/a | -| [ecs\_log\_groups](#module\_ecs\_log\_groups) | terraform-aws-modules/cloudwatch/aws//modules/log-group | ~> 3.0 | -| [efs](#module\_efs) | cloudposse/efs/aws | ~> 0.35.0 | -| [efs\_sg](#module\_efs\_sg) | terraform-aws-modules/security-group/aws | ~> 4.0 | +| [ecs\_log\_groups](#module\_ecs\_log\_groups) | terraform-aws-modules/cloudwatch/aws//modules/log-group | ~> 5.0 | +| [efs](#module\_efs) | cloudposse/efs/aws | ~> 1.1 | +| [efs\_sg](#module\_efs\_sg) | terraform-aws-modules/security-group/aws | ~> 5.0 | | [elasticache-alerts](#module\_elasticache-alerts) | ../monitoring/elasticache-alerts | n/a | -| [fargate\_sg](#module\_fargate\_sg) | terraform-aws-modules/security-group/aws | ~> 4.0 | -| [lb\_sg](#module\_lb\_sg) | terraform-aws-modules/security-group/aws | ~> 4.0 | -| [log\_group](#module\_log\_group) | terraform-aws-modules/cloudwatch/aws//modules/log-group | ~> 3.0 | +| [fargate\_sg](#module\_fargate\_sg) | terraform-aws-modules/security-group/aws | ~> 5.0 | +| [lb\_sg](#module\_lb\_sg) | terraform-aws-modules/security-group/aws | ~> 5.0 | +| [log\_group](#module\_log\_group) | terraform-aws-modules/cloudwatch/aws//modules/log-group | ~> 5.0 | | [rds-alerts](#module\_rds-alerts) | ../monitoring/rds-alerts | n/a | -| [redis](#module\_redis) | umotif-public/elasticache-redis/aws | n/a | -| [s3\_bucket](#module\_s3\_bucket) | terraform-aws-modules/s3-bucket/aws | 4.11.0 | -| [scheduled\_task](#module\_scheduled\_task) | cn-terraform/ecs-fargate-scheduled-task/aws | 1.0.22 | -| [vpc](#module\_vpc) | terraform-aws-modules/vpc/aws | <5.0.0 | +| [redis](#module\_redis) | ../elasticache-redis | n/a | +| [s3\_bucket](#module\_s3\_bucket) | terraform-aws-modules/s3-bucket/aws | ~> 5.0 | +| [scheduled\_task](#module\_scheduled\_task) | cn-terraform/ecs-fargate-scheduled-task/aws | ~> 1.0.27 | +| [vpc](#module\_vpc) | terraform-aws-modules/vpc/aws | ~> 6.0 | ## Inputs diff --git a/terraform/modules/ecs/buckets.tf b/terraform/modules/ecs/buckets.tf index ff2380e..8221f33 100644 --- a/terraform/modules/ecs/buckets.tf +++ b/terraform/modules/ecs/buckets.tf @@ -1,6 +1,6 @@ module "s3_bucket" { source = "terraform-aws-modules/s3-bucket/aws" - version = "4.11.0" + version = "~> 5.0" for_each = { exports = local.polytomic_export_bucket, diff --git a/terraform/modules/ecs/database.tf b/terraform/modules/ecs/database.tf index 1c22dfe..0b16ae8 100644 --- a/terraform/modules/ecs/database.tf +++ b/terraform/modules/ecs/database.tf @@ -1,6 +1,6 @@ module "database" { source = "terraform-aws-modules/rds/aws" - version = "5.9.0" + version = "~> 7.0" count = var.database_endpoint == "" ? 1 : 0 diff --git a/terraform/modules/ecs/ecs-cluster.tf b/terraform/modules/ecs/ecs-cluster.tf index f022948..2ba6ab2 100644 --- a/terraform/modules/ecs/ecs-cluster.tf +++ b/terraform/modules/ecs/ecs-cluster.tf @@ -1,6 +1,6 @@ module "ecs" { source = "terraform-aws-modules/ecs/aws" - version = "<5.0.0" + version = "~> 6.0" count = var.ecs_cluster_name == "" ? 1 : 0 diff --git a/terraform/modules/ecs/efs.tf b/terraform/modules/ecs/efs.tf index 9e9b905..de2e320 100644 --- a/terraform/modules/ecs/efs.tf +++ b/terraform/modules/ecs/efs.tf @@ -1,6 +1,6 @@ module "efs" { source = "cloudposse/efs/aws" - version = "~> 0.35.0" + version = "~> 1.1" name = "${var.prefix}-efs" subnets = var.vpc_id == "" ? module.vpc[0].private_subnets : var.private_subnet_ids diff --git a/terraform/modules/ecs/logs.tf b/terraform/modules/ecs/logs.tf index e3a082d..160b004 100644 --- a/terraform/modules/ecs/logs.tf +++ b/terraform/modules/ecs/logs.tf @@ -1,6 +1,6 @@ module "log_group" { source = "terraform-aws-modules/cloudwatch/aws//modules/log-group" - version = "~> 3.0" + version = "~> 5.0" name = "${var.prefix}-polytomic-logs" retention_in_days = var.log_retention_days @@ -16,7 +16,7 @@ module "log_group" { module "ecs_log_groups" { source = "terraform-aws-modules/cloudwatch/aws//modules/log-group" - version = "~> 3.0" + version = "~> 5.0" for_each = toset(["sync", "scheduler", "schemacache", "stats-reporter", "web", "worker"]) diff --git a/terraform/modules/ecs/redis.tf b/terraform/modules/ecs/redis.tf index 977332d..16e64d0 100644 --- a/terraform/modules/ecs/redis.tf +++ b/terraform/modules/ecs/redis.tf @@ -1,5 +1,5 @@ module "redis" { - source = "umotif-public/elasticache-redis/aws" + source = "../elasticache-redis" count = var.redis_endpoint == "" ? 1 : 0 diff --git a/terraform/modules/ecs/security-groups.tf b/terraform/modules/ecs/security-groups.tf index 3b5c47c..795dc15 100644 --- a/terraform/modules/ecs/security-groups.tf +++ b/terraform/modules/ecs/security-groups.tf @@ -1,6 +1,6 @@ module "database_sg" { source = "terraform-aws-modules/security-group/aws" - version = "~> 4.0" + version = "~> 5.0" count = var.database_endpoint == "" ? 1 : 0 @@ -29,7 +29,7 @@ module "database_sg" { module "fargate_sg" { source = "terraform-aws-modules/security-group/aws" - version = "~> 4.0" + version = "~> 5.0" name = "${var.prefix}-fargate_task" vpc_id = var.vpc_id == "" ? module.vpc[0].vpc_id : var.vpc_id @@ -65,7 +65,7 @@ module "fargate_sg" { module "efs_sg" { source = "terraform-aws-modules/security-group/aws" - version = "~> 4.0" + version = "~> 5.0" name = "${var.prefix}-efs" vpc_id = var.vpc_id == "" ? module.vpc[0].vpc_id : var.vpc_id @@ -92,7 +92,7 @@ module "efs_sg" { module "lb_sg" { source = "terraform-aws-modules/security-group/aws" - version = "~> 4.0" + version = "~> 5.0" count = length(var.load_balancer_security_groups) == 0 ? 1 : 0 diff --git a/terraform/modules/ecs/stats.tf b/terraform/modules/ecs/stats.tf index b369213..665c8ac 100644 --- a/terraform/modules/ecs/stats.tf +++ b/terraform/modules/ecs/stats.tf @@ -2,7 +2,7 @@ module "scheduled_task" { count = var.enable_stats ? 1 : 0 source = "cn-terraform/ecs-fargate-scheduled-task/aws" - version = "1.0.22" + version = "~> 1.0.27" name_prefix = var.prefix ecs_cluster_arn = var.ecs_cluster_name == "" ? module.ecs[0].cluster_arn : data.aws_ecs_cluster.cluster[0].arn event_rule_name = "polytomic-stats-reporter" diff --git a/terraform/modules/ecs/versions.tf b/terraform/modules/ecs/versions.tf index 3c2c737..acbb4f1 100644 --- a/terraform/modules/ecs/versions.tf +++ b/terraform/modules/ecs/versions.tf @@ -1,10 +1,10 @@ terraform { - required_version = ">= 1.0" + required_version = ">= 1.11" required_providers { aws = { source = "hashicorp/aws" - version = ">= 4.0, < 6.0.0" + version = ">= 6.0, < 7.0" } null = { diff --git a/terraform/modules/ecs/vpc.tf b/terraform/modules/ecs/vpc.tf index 2ece20e..3a45a4c 100644 --- a/terraform/modules/ecs/vpc.tf +++ b/terraform/modules/ecs/vpc.tf @@ -1,6 +1,6 @@ module "vpc" { source = "terraform-aws-modules/vpc/aws" - version = "<5.0.0" + version = "~> 6.0" count = var.vpc_id == "" ? 1 : 0 diff --git a/terraform/modules/elasticache-redis/README.md b/terraform/modules/elasticache-redis/README.md new file mode 100644 index 0000000..6e8d80f --- /dev/null +++ b/terraform/modules/elasticache-redis/README.md @@ -0,0 +1,99 @@ +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >= 1.0 | +| [aws](#requirement\_aws) | >= 4.0 | +| [random](#requirement\_random) | >= 3.0 | + +## Providers + +| Name | Version | +|------|---------| +| [aws](#provider\_aws) | >= 4.0 | +| [random](#provider\_random) | >= 3.0 | + +## Modules + +No modules. + +## Resources + +| Name | Type | +|------|------| +| [aws_elasticache_parameter_group.redis](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/elasticache_parameter_group) | resource | +| [aws_elasticache_replication_group.redis](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/elasticache_replication_group) | resource | +| [aws_elasticache_subnet_group.redis](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/elasticache_subnet_group) | resource | +| [aws_security_group.redis](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group) | resource | +| [aws_security_group_rule.other_sg_ingress](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource | +| [aws_security_group_rule.redis_egress](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource | +| [aws_security_group_rule.redis_ingress_cidr_blocks](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource | +| [aws_security_group_rule.redis_ingress_self](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource | +| [random_id.redis_pg](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/id) | resource | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [allowed\_security\_groups](#input\_allowed\_security\_groups) | List of existing security groups that will be allowed ingress via the elaticache security group rules | `list(string)` | `[]` | no | +| [apply\_immediately](#input\_apply\_immediately) | Specifies whether any modifications are applied immediately, or during the next maintenance window. | `bool` | `false` | no | +| [at\_rest\_encryption\_enabled](#input\_at\_rest\_encryption\_enabled) | Whether to enable encryption at rest. | `bool` | `true` | no | +| [auth\_token](#input\_auth\_token) | The password used to access a password protected server. Can be specified only if `transit_encryption_enabled = true`. | `string` | `""` | no | +| [auth\_token\_update\_strategy](#input\_auth\_token\_update\_strategy) | Strategy to use when updating the auth\_token. Valid values are SET, ROTATE, and DELETE. Defaults to ROTATE. | `string` | `"ROTATE"` | no | +| [auto\_minor\_version\_upgrade](#input\_auto\_minor\_version\_upgrade) | n/a | `string` | `true` | no | +| [automatic\_failover\_enabled](#input\_automatic\_failover\_enabled) | Specifies whether a read-only replica will be automatically promoted to read/write primary if the existing primary fails. If enabled, number\_cache\_clusters must be greater than 1. Must be enabled for Redis (cluster mode enabled) replication groups. | `bool` | `true` | no | +| [cluster\_mode\_enabled](#input\_cluster\_mode\_enabled) | Enable creation of a native redis cluster. | `bool` | `false` | no | +| [data\_tiering\_enabled](#input\_data\_tiering\_enabled) | Enables data tiering. Data tiering is only supported for replication groups using the r6gd node type. This parameter must be set to true when using r6gd nodes. | `bool` | `false` | no | +| [description](#input\_description) | The description of the all resources. | `string` | `"Managed by Terraform"` | no | +| [egress\_cidr\_blocks](#input\_egress\_cidr\_blocks) | List of Egress CIDR blocks. | `list(string)` |
[
"0.0.0.0/0"
]
| no | +| [engine\_version](#input\_engine\_version) | The version number of the cache engine to be used for the cache clusters in this replication group. | `string` | `"6.x"` | no | +| [family](#input\_family) | The family of the ElastiCache parameter group. | `string` | `"redis6.x"` | no | +| [final\_snapshot\_identifier](#input\_final\_snapshot\_identifier) | The name of your final node group (shard) snapshot. ElastiCache creates the snapshot from the primary node in the cluster. If omitted, no final snapshot will be made. | `string` | `null` | no | +| [global\_replication\_group\_id](#input\_global\_replication\_group\_id) | The ID of the global replication group to which this replication group should belong. | `string` | `null` | no | +| [ingress\_cidr\_blocks](#input\_ingress\_cidr\_blocks) | List of Ingress CIDR blocks. | `list(string)` | `[]` | no | +| [ingress\_self](#input\_ingress\_self) | Specify whether the security group itself will be added as a source to the ingress rule. | `bool` | `false` | no | +| [kms\_key\_id](#input\_kms\_key\_id) | The ARN of the key that you wish to use if encrypting at rest. If not supplied, uses service managed encryption. Can be specified only if `at_rest_encryption_enabled = true` | `string` | `""` | no | +| [log\_delivery\_configuration](#input\_log\_delivery\_configuration) | Log Delivery configuration for the cluster. |
list(object({
destination_type = string
destination = string
log_format = string
log_type = string
}))
| `[]` | no | +| [maintenance\_window](#input\_maintenance\_window) | Specifies the weekly time range for when maintenance on the cache cluster is performed. | `string` | `""` | no | +| [multi\_az\_enabled](#input\_multi\_az\_enabled) | Specifies whether to enable Multi-AZ Support for the replication group. If true, `automatic_failover_enabled` must also be enabled. Defaults to false. | `bool` | `false` | no | +| [name\_prefix](#input\_name\_prefix) | The replication group identifier. This parameter is stored as a lowercase string. | `string` | n/a | yes | +| [node\_type](#input\_node\_type) | The compute and memory capacity of the nodes in the node group. | `string` | n/a | yes | +| [notification\_topic\_arn](#input\_notification\_topic\_arn) | An Amazon Resource Name (ARN) of an SNS topic to send ElastiCache notifications to. Example: `arn:aws:sns:us-east-1:012345678999:my_sns_topic` | `string` | `""` | no | +| [num\_cache\_clusters](#input\_num\_cache\_clusters) | The number of cache clusters (primary and replicas) this replication group will have. If Multi-AZ is enabled, the value of this parameter must be at least 2. Updates will occur before other modifications. Conflicts with num\_node\_groups. | `number` | `1` | no | +| [num\_node\_groups](#input\_num\_node\_groups) | Specify the number of node groups (shards) for this Redis replication group. Changing this number will trigger an online resizing operation before other settings modifications. | `number` | `0` | no | +| [parameter](#input\_parameter) | A list of Redis parameters to apply. Note that parameters may differ from one Redis family to another |
list(object({
name = string
value = string
}))
| `[]` | no | +| [parameter\_group\_description](#input\_parameter\_group\_description) | The description of the ElastiCache parameter group | `string` | `null` | no | +| [port](#input\_port) | The port number on which each of the cache nodes will accept connections. | `number` | `6379` | no | +| [preferred\_cache\_cluster\_azs](#input\_preferred\_cache\_cluster\_azs) | A list of EC2 availability zones in which the replication group's cache clusters will be created. The order of the availability zones in the list is not important. | `list(string)` | `null` | no | +| [replicas\_per\_node\_group](#input\_replicas\_per\_node\_group) | Specify the number of replica nodes in each node group. Valid values are 0 to 5. Changing this number will trigger an online resizing operation before other settings modifications. | `number` | `0` | no | +| [security\_group\_ids](#input\_security\_group\_ids) | List of Security Groups. | `list(string)` | `[]` | no | +| [snapshot\_name](#input\_snapshot\_name) | The name of a snapshot from which to restore data into the new node group. Changing the snapshot\_name forces a new resource. | `string` | `null` | no | +| [snapshot\_retention\_limit](#input\_snapshot\_retention\_limit) | The number of days for which ElastiCache will retain automatic cache cluster snapshots before deleting them. | `number` | `30` | no | +| [snapshot\_window](#input\_snapshot\_window) | The daily time range (in UTC) during which ElastiCache will begin taking a daily snapshot of your cache cluster. | `string` | `""` | no | +| [subnet\_group\_name](#input\_subnet\_group\_name) | The name of the subnet group. If it is not specified, the module will create one for you | `string` | `null` | no | +| [subnet\_ids](#input\_subnet\_ids) | List of VPC Subnet IDs for the cache subnet group. | `list(string)` | `[]` | no | +| [tags](#input\_tags) | A mapping of tags to assign to all resources. | `map(string)` | `{}` | no | +| [transit\_encryption\_enabled](#input\_transit\_encryption\_enabled) | Whether to enable encryption in transit. | `bool` | `true` | no | +| [user\_group\_ids](#input\_user\_group\_ids) | User Group ID to associate with the replication group | `list(string)` | `null` | no | +| [vpc\_id](#input\_vpc\_id) | VPC Id to associate with Redis ElastiCache. | `string` | n/a | yes | + +## Outputs + +| Name | Description | +|------|-------------| +| [elasticache\_auth\_token](#output\_elasticache\_auth\_token) | The Redis Auth Token. | +| [elasticache\_parameter\_group\_id](#output\_elasticache\_parameter\_group\_id) | The ElastiCache parameter group name. | +| [elasticache\_port](#output\_elasticache\_port) | The Redis port. | +| [elasticache\_replication\_group\_arn](#output\_elasticache\_replication\_group\_arn) | The Amazon Resource Name (ARN) of the created ElastiCache Replication Group. | +| [elasticache\_replication\_group\_id](#output\_elasticache\_replication\_group\_id) | The ID of the ElastiCache Replication Group. | +| [elasticache\_replication\_group\_member\_clusters](#output\_elasticache\_replication\_group\_member\_clusters) | The identifiers of all the nodes that are part of this replication group. | +| [elasticache\_replication\_group\_primary\_endpoint\_address](#output\_elasticache\_replication\_group\_primary\_endpoint\_address) | The address of the endpoint for the primary node in the replication group. | +| [elasticache\_replication\_group\_reader\_endpoint\_address](#output\_elasticache\_replication\_group\_reader\_endpoint\_address) | The address of the endpoint for the reader node in the replication group. | +| [security\_group\_arn](#output\_security\_group\_arn) | The ARN of the Redis ElastiCache security group. | +| [security\_group\_description](#output\_security\_group\_description) | The description of the Redis ElastiCache security group. | +| [security\_group\_egress](#output\_security\_group\_egress) | The egress rules of the Redis ElastiCache security group. | +| [security\_group\_id](#output\_security\_group\_id) | The ID of the Redis ElastiCache security group. | +| [security\_group\_ingress](#output\_security\_group\_ingress) | The ingress rules of the Redis ElastiCache security group. | +| [security\_group\_name](#output\_security\_group\_name) | The name of the Redis ElastiCache security group. | +| [security\_group\_owner\_id](#output\_security\_group\_owner\_id) | The owner ID of the Redis ElastiCache security group. | +| [security\_group\_vpc\_id](#output\_security\_group\_vpc\_id) | The VPC ID of the Redis ElastiCache security group. | diff --git a/terraform/modules/elasticache-redis/main.tf b/terraform/modules/elasticache-redis/main.tf new file mode 100644 index 0000000..f2f2208 --- /dev/null +++ b/terraform/modules/elasticache-redis/main.tf @@ -0,0 +1,173 @@ +# Forked from umotif-public/terraform-aws-elasticache-redis v3.5.0 +# https://github.com/umotif-public/terraform-aws-elasticache-redis +# +# Changes from upstream: +# - Added auth_token_update_strategy variable for AWS Provider v6 compatibility +# - Updated provider version constraints + +locals { + subnet_group_name = var.subnet_group_name != null ? var.subnet_group_name : aws_elasticache_subnet_group.redis[0].name +} + +resource "aws_elasticache_replication_group" "redis" { + engine = var.global_replication_group_id == null ? "redis" : null + + parameter_group_name = var.global_replication_group_id == null ? aws_elasticache_parameter_group.redis.name : null + subnet_group_name = local.subnet_group_name + security_group_ids = concat(var.security_group_ids, [aws_security_group.redis.id]) + + preferred_cache_cluster_azs = var.preferred_cache_cluster_azs + replication_group_id = var.global_replication_group_id == null ? "${var.name_prefix}-redis" : "${var.name_prefix}-redis-replica" + num_cache_clusters = var.cluster_mode_enabled ? null : var.num_cache_clusters + node_type = var.global_replication_group_id == null ? var.node_type : null + + engine_version = var.global_replication_group_id == null ? var.engine_version : null + port = var.port + + maintenance_window = var.maintenance_window + snapshot_window = var.snapshot_window + snapshot_retention_limit = var.snapshot_retention_limit + snapshot_name = var.snapshot_name + final_snapshot_identifier = var.final_snapshot_identifier + automatic_failover_enabled = var.automatic_failover_enabled && var.num_cache_clusters >= 2 ? true : false + auto_minor_version_upgrade = var.auto_minor_version_upgrade + multi_az_enabled = var.multi_az_enabled + + at_rest_encryption_enabled = var.global_replication_group_id == null ? var.at_rest_encryption_enabled : null + transit_encryption_enabled = var.global_replication_group_id == null ? var.transit_encryption_enabled : null + auth_token = var.auth_token != "" ? var.auth_token : null + auth_token_update_strategy = var.auth_token != "" ? var.auth_token_update_strategy : null + kms_key_id = var.kms_key_id + global_replication_group_id = var.global_replication_group_id + + apply_immediately = var.apply_immediately + + description = var.description + + data_tiering_enabled = var.data_tiering_enabled + + notification_topic_arn = var.notification_topic_arn + + replicas_per_node_group = var.cluster_mode_enabled ? var.replicas_per_node_group : null + num_node_groups = var.cluster_mode_enabled ? var.num_node_groups : null + + user_group_ids = var.user_group_ids + + dynamic "log_delivery_configuration" { + for_each = var.log_delivery_configuration + + content { + destination_type = log_delivery_configuration.value.destination_type + destination = log_delivery_configuration.value.destination + log_format = log_delivery_configuration.value.log_format + log_type = log_delivery_configuration.value.log_type + } + } + + tags = merge( + { + "Name" = "${var.name_prefix}-redis" + }, + var.tags, + ) +} + +resource "random_id" "redis_pg" { + keepers = { + family = var.family + } + + byte_length = 2 +} + +resource "aws_elasticache_parameter_group" "redis" { + name = "${var.name_prefix}-redis-${random_id.redis_pg.hex}" + family = var.family + description = var.parameter_group_description != null ? var.parameter_group_description : "Elasticache parameter group managed by Terraform" + + dynamic "parameter" { + for_each = var.num_node_groups > 0 ? concat([{ name = "cluster-enabled", value = "yes" }], var.parameter) : var.parameter + content { + name = parameter.value.name + value = parameter.value.value + } + } + + # Ignore changes to the description since it will try to recreate the resource + lifecycle { + ignore_changes = [ + description, + ] + } + + tags = var.tags +} + +resource "aws_elasticache_subnet_group" "redis" { + count = var.subnet_group_name == null && length(var.subnet_ids) > 0 ? 1 : 0 + + name = var.global_replication_group_id == null ? "${var.name_prefix}-redis-sg" : "${var.name_prefix}-redis-sg-replica" + subnet_ids = var.subnet_ids + description = "Elasticache subnet group for ${var.description}" + + tags = var.tags +} + +resource "aws_security_group" "redis" { + name_prefix = "${var.name_prefix}-redis-" + vpc_id = var.vpc_id + + tags = merge( + { + "Name" = "${var.name_prefix}-redis" + }, + var.tags + ) + + lifecycle { + create_before_destroy = true + } +} + +resource "aws_security_group_rule" "redis_ingress_self" { + count = var.ingress_self ? 1 : 0 + + type = "ingress" + from_port = var.port + to_port = var.port + protocol = "tcp" + self = true + security_group_id = aws_security_group.redis.id +} + +resource "aws_security_group_rule" "redis_ingress_cidr_blocks" { + count = length(var.ingress_cidr_blocks) != 0 ? 1 : 0 + + type = "ingress" + from_port = var.port + to_port = var.port + protocol = "tcp" + cidr_blocks = var.ingress_cidr_blocks + security_group_id = aws_security_group.redis.id +} + +resource "aws_security_group_rule" "redis_egress" { + count = length(var.egress_cidr_blocks) != 0 ? 1 : 0 + + type = "egress" + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = var.egress_cidr_blocks + security_group_id = aws_security_group.redis.id +} + +resource "aws_security_group_rule" "other_sg_ingress" { + count = length(var.allowed_security_groups) + type = "ingress" + from_port = var.port + to_port = var.port + protocol = "tcp" + source_security_group_id = element(var.allowed_security_groups, count.index) + security_group_id = aws_security_group.redis.id +} diff --git a/terraform/modules/elasticache-redis/outputs.tf b/terraform/modules/elasticache-redis/outputs.tf new file mode 100644 index 0000000..95e854e --- /dev/null +++ b/terraform/modules/elasticache-redis/outputs.tf @@ -0,0 +1,80 @@ +output "elasticache_replication_group_arn" { + value = aws_elasticache_replication_group.redis.arn + description = "The Amazon Resource Name (ARN) of the created ElastiCache Replication Group." +} + +output "elasticache_replication_group_id" { + value = aws_elasticache_replication_group.redis.id + description = "The ID of the ElastiCache Replication Group." +} + +output "elasticache_replication_group_primary_endpoint_address" { + value = var.num_node_groups > 0 ? aws_elasticache_replication_group.redis.configuration_endpoint_address : aws_elasticache_replication_group.redis.primary_endpoint_address + description = "The address of the endpoint for the primary node in the replication group." +} + +output "elasticache_replication_group_reader_endpoint_address" { + value = var.num_node_groups > 0 ? aws_elasticache_replication_group.redis.configuration_endpoint_address : aws_elasticache_replication_group.redis.reader_endpoint_address + description = "The address of the endpoint for the reader node in the replication group." +} + +output "elasticache_replication_group_member_clusters" { + value = aws_elasticache_replication_group.redis.member_clusters + description = "The identifiers of all the nodes that are part of this replication group." +} + +output "elasticache_parameter_group_id" { + value = aws_elasticache_parameter_group.redis.id + description = "The ElastiCache parameter group name." +} + +output "security_group_id" { + value = aws_security_group.redis.id + description = "The ID of the Redis ElastiCache security group." +} + +output "security_group_arn" { + value = aws_security_group.redis.arn + description = "The ARN of the Redis ElastiCache security group." +} + +output "security_group_vpc_id" { + value = aws_security_group.redis.vpc_id + description = "The VPC ID of the Redis ElastiCache security group." +} + +output "security_group_owner_id" { + value = aws_security_group.redis.owner_id + description = "The owner ID of the Redis ElastiCache security group." +} + +output "security_group_name" { + value = aws_security_group.redis.name + description = "The name of the Redis ElastiCache security group." +} + +output "security_group_description" { + value = aws_security_group.redis.description + description = "The description of the Redis ElastiCache security group." +} + +output "security_group_ingress" { + value = aws_security_group.redis.ingress + description = "The ingress rules of the Redis ElastiCache security group." +} + +output "security_group_egress" { + value = aws_security_group.redis.egress + description = "The egress rules of the Redis ElastiCache security group." +} + +output "elasticache_auth_token" { + description = "The Redis Auth Token." + value = aws_elasticache_replication_group.redis.auth_token + sensitive = true +} + +output "elasticache_port" { + description = "The Redis port." + value = aws_elasticache_replication_group.redis.port +} diff --git a/terraform/modules/elasticache-redis/variables.tf b/terraform/modules/elasticache-redis/variables.tf new file mode 100644 index 0000000..5690fa0 --- /dev/null +++ b/terraform/modules/elasticache-redis/variables.tf @@ -0,0 +1,264 @@ +variable "name_prefix" { + type = string + description = "The replication group identifier. This parameter is stored as a lowercase string." +} + +variable "num_cache_clusters" { + type = number + default = 1 + description = "The number of cache clusters (primary and replicas) this replication group will have. If Multi-AZ is enabled, the value of this parameter must be at least 2. Updates will occur before other modifications. Conflicts with num_node_groups." +} + +variable "cluster_mode_enabled" { + type = bool + description = "Enable creation of a native redis cluster." + default = false +} + +variable "node_type" { + type = string + description = "The compute and memory capacity of the nodes in the node group." +} + +variable "subnet_ids" { + type = list(string) + description = "List of VPC Subnet IDs for the cache subnet group." + default = [] +} + +variable "subnet_group_name" { + type = string + description = "The name of the subnet group. If it is not specified, the module will create one for you" + default = null +} + +variable "vpc_id" { + type = string + description = "VPC Id to associate with Redis ElastiCache." +} + +variable "ingress_cidr_blocks" { + type = list(string) + description = "List of Ingress CIDR blocks." + default = [] +} + +variable "egress_cidr_blocks" { + type = list(string) + description = "List of Egress CIDR blocks." + default = ["0.0.0.0/0"] +} + +variable "ingress_self" { + type = bool + description = "Specify whether the security group itself will be added as a source to the ingress rule." + default = false +} + +variable "security_group_ids" { + type = list(string) + description = "List of Security Groups." + default = [] +} + +variable "engine_version" { + default = "6.x" + type = string + description = "The version number of the cache engine to be used for the cache clusters in this replication group." +} + +variable "port" { + default = 6379 + type = number + description = "The port number on which each of the cache nodes will accept connections." +} + +variable "maintenance_window" { + default = "" + type = string + description = "Specifies the weekly time range for when maintenance on the cache cluster is performed." +} + +variable "snapshot_name" { + type = string + description = "The name of a snapshot from which to restore data into the new node group. Changing the snapshot_name forces a new resource." + default = null +} + +variable "snapshot_window" { + default = "" + type = string + description = "The daily time range (in UTC) during which ElastiCache will begin taking a daily snapshot of your cache cluster." +} + +variable "snapshot_retention_limit" { + default = 30 + type = number + description = "The number of days for which ElastiCache will retain automatic cache cluster snapshots before deleting them." +} + +variable "auto_minor_version_upgrade" { + default = true + type = string +} + +variable "automatic_failover_enabled" { + default = true + type = bool + description = "Specifies whether a read-only replica will be automatically promoted to read/write primary if the existing primary fails. If enabled, number_cache_clusters must be greater than 1. Must be enabled for Redis (cluster mode enabled) replication groups." +} + +variable "at_rest_encryption_enabled" { + default = true + type = bool + description = "Whether to enable encryption at rest." +} + +variable "transit_encryption_enabled" { + default = true + type = bool + description = "Whether to enable encryption in transit." +} + +variable "apply_immediately" { + default = false + type = bool + description = "Specifies whether any modifications are applied immediately, or during the next maintenance window." +} + +variable "family" { + default = "redis6.x" + type = string + description = "The family of the ElastiCache parameter group." +} + +variable "description" { + default = "Managed by Terraform" + type = string + description = "The description of the all resources." +} + +variable "tags" { + default = {} + type = map(string) + description = "A mapping of tags to assign to all resources." +} + +variable "auth_token" { + type = string + description = "The password used to access a password protected server. Can be specified only if `transit_encryption_enabled = true`." + default = "" +} + +variable "auth_token_update_strategy" { + type = string + description = "Strategy to use when updating the auth_token. Valid values are SET, ROTATE, and DELETE. Defaults to ROTATE." + default = "ROTATE" + + validation { + condition = contains(["SET", "ROTATE", "DELETE"], var.auth_token_update_strategy) + error_message = "auth_token_update_strategy must be one of: SET, ROTATE, DELETE" + } +} + +variable "kms_key_id" { + type = string + description = "The ARN of the key that you wish to use if encrypting at rest. If not supplied, uses service managed encryption. Can be specified only if `at_rest_encryption_enabled = true`" + default = "" +} + +variable "parameter" { + type = list(object({ + name = string + value = string + })) + default = [] + description = "A list of Redis parameters to apply. Note that parameters may differ from one Redis family to another" +} + +variable "notification_topic_arn" { + type = string + default = "" + description = "An Amazon Resource Name (ARN) of an SNS topic to send ElastiCache notifications to. Example: `arn:aws:sns:us-east-1:012345678999:my_sns_topic`" +} + +variable "replicas_per_node_group" { + type = number + default = 0 + description = "Specify the number of replica nodes in each node group. Valid values are 0 to 5. Changing this number will trigger an online resizing operation before other settings modifications." + + validation { + condition = var.replicas_per_node_group <= 5 + error_message = "The replicas_per_node_group value must be between 0 and 5." + } +} + +variable "num_node_groups" { + type = number + default = 0 + description = "Specify the number of node groups (shards) for this Redis replication group. Changing this number will trigger an online resizing operation before other settings modifications." +} + +variable "preferred_cache_cluster_azs" { + type = list(string) + description = "A list of EC2 availability zones in which the replication group's cache clusters will be created. The order of the availability zones in the list is not important." + default = null +} + +variable "multi_az_enabled" { + type = bool + description = "Specifies whether to enable Multi-AZ Support for the replication group. If true, `automatic_failover_enabled` must also be enabled. Defaults to false." + default = false +} + +variable "final_snapshot_identifier" { + type = string + description = "The name of your final node group (shard) snapshot. ElastiCache creates the snapshot from the primary node in the cluster. If omitted, no final snapshot will be made." + default = null +} + +variable "global_replication_group_id" { + description = "The ID of the global replication group to which this replication group should belong." + type = string + default = null +} + +variable "log_delivery_configuration" { + type = list(object({ + destination_type = string + destination = string + log_format = string + log_type = string + })) + description = "Log Delivery configuration for the cluster." + default = [] + + validation { + condition = length(var.log_delivery_configuration) <= 2 + error_message = "You can set 2 targets at most for log delivery options." + } +} + +variable "allowed_security_groups" { + type = list(string) + description = "List of existing security groups that will be allowed ingress via the elaticache security group rules" + default = [] +} + +variable "data_tiering_enabled" { + type = bool + default = false + description = "Enables data tiering. Data tiering is only supported for replication groups using the r6gd node type. This parameter must be set to true when using r6gd nodes." +} + +variable "user_group_ids" { + type = list(string) + default = null + description = "User Group ID to associate with the replication group" +} + +variable "parameter_group_description" { + type = string + description = "The description of the ElastiCache parameter group" + default = null +} diff --git a/terraform/modules/elasticache-redis/versions.tf b/terraform/modules/elasticache-redis/versions.tf new file mode 100644 index 0000000..24b2343 --- /dev/null +++ b/terraform/modules/elasticache-redis/versions.tf @@ -0,0 +1,15 @@ +terraform { + required_version = ">= 1.0" + + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 4.0" + } + + random = { + source = "hashicorp/random" + version = ">= 3.0" + } + } +}