Skip to content

Commit 1b53806

Browse files
authored
[feature] Add aws-redis-replication-group (#267)
### Summary This module is similar to [aws-redis-node](https://github.com/chanzuckerberg/cztack/tree/main/aws-redis-node), however it creates a Elasticache Redis cluster using a replication group. The motivation to create this module is because encryption options (in-transit and at rest) are only available when you create your cluster using a replication group. ### Test Plan - Manual tests (already applied to IDseq) - Unit tests ### References (Optional) Additional links to provide more context.
1 parent 68ab717 commit 1b53806

File tree

7 files changed

+288
-0
lines changed

7 files changed

+288
-0
lines changed

.github/workflows/ci.yml

+1
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ jobs:
111111
- aws-params-secrets-setup
112112
- aws-params-writer
113113
- aws-redis-node
114+
- aws-redis-replication-group
114115
- aws-s3-private-bucket
115116
- aws-s3-public-bucket
116117
- aws-single-page-static-site

aws-redis-replication-group/README.md

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# Redis Replication Group
2+
3+
This module creates a Elasticache Redis cluster using
4+
a replication group with the given parameters.
5+
6+
<!-- START -->
7+
## Requirements
8+
9+
| Name | Version |
10+
|------|---------|
11+
| aws | < 3.0.0 |
12+
13+
## Providers
14+
15+
| Name | Version |
16+
|------|---------|
17+
| aws | < 3.0.0 |
18+
19+
## Inputs
20+
21+
| Name | Description | Type | Default | Required |
22+
|------|-------------|------|---------|:--------:|
23+
| at\_rest\_encryption\_enabled | Whether to enable encryption at rest. | `bool` | `false` | no |
24+
| apply\_immediately | Whether changes should be applied immediately or during the next maintenance window. | `bool` | `true` | no |
25+
| availability\_zone | Availability zone in which this instance should run. | `string` | `null` | no |
26+
| engine\_version | The version of Redis to run. See [supported versions](https://docs.aws.amazon.com/AmazonElastiCache/latest/red-ug/supported-engine-versions.html) | `string` | `"5.0.5"` | no |
27+
| env | Env for tagging and naming. See [doc](../README.md#consistent-tagging). | `string` | n/a | yes |
28+
| ingress\_security\_group\_ids | Source security groups which should be able to contact this instance. | `list(string)` | n/a | yes |
29+
| instance\_type | The type of instance to run. See [supported node types](https://docs.aws.amazon.com/AmazonElastiCache/latest/red-ug/CacheNodes.SupportedTypes.html) | `string` | `"cache.m5.large"` | no |
30+
| number_cache_clusters | Number of cache clusters. | `number` | 1 | no |
31+
| owner | Owner for tagging and naming. See [doc](../README.md#consistent-tagging). | `string` | n/a | yes |
32+
| parameter\_group\_name | Parameter group to use for this Redis cache. | `string` | `"default.redis5.0"` | no |
33+
| port | Port to host Redis on. | `number` | `6379` | no |
34+
| project | Project for tagging and naming. See [doc](../README.md#consistent-tagging) | `string` | n/a | yes |
35+
| replication\_group\_description | A user-created description for the replication group. | `string` | n/a | yes |
36+
| resource\_name | If not set, name will be [var.project]-[var.env]-[var.name]. | `string` | `""` | no |
37+
| service | Service for tagging and naming. See [doc](../README.md#consistent-tagging) | `string` | `"redis"` | no |
38+
| subnets | List of subnets to which this EC instance should be attached. They should probably be private. | `list(string)` | n/a | yes |
39+
| transit\_encryption\_enabled | Whether to enable encryption in transit. | `bool` | `false` | no |
40+
| vpc\_id | VPC where the cache will be deployed. | `string` | n/a | yes |
41+
42+
## Outputs
43+
44+
| Name | Description |
45+
|------|-------------|
46+
| primary_endpoint_address | The endpoint of the primary node in this node group (shard). |
47+
| configuration_endpoint_address | The configuration endpoint address to allow host discovery. |
48+
| port | Redis TCP port. |
49+
50+
<!-- END -->

aws-redis-replication-group/main.tf

+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
locals {
2+
name = "${var.project}-${var.env}-${var.service}"
3+
4+
tags = {
5+
managedBy = "terraform"
6+
Name = "${var.project}-${var.env}-${var.service}"
7+
project = var.project
8+
env = var.env
9+
service = var.service
10+
owner = var.owner
11+
}
12+
}
13+
14+
module "sg" {
15+
source = "terraform-aws-modules/security-group/aws"
16+
version = "3.12.0"
17+
name = local.name
18+
description = "Allow traffic to Redis."
19+
vpc_id = var.vpc_id
20+
tags = local.tags
21+
22+
ingress_with_source_security_group_id = [
23+
for sg in var.ingress_security_group_ids : {
24+
from_port = var.port
25+
to_port = var.port
26+
protocol = "tcp"
27+
description = "Redis port"
28+
source_security_group_id = sg
29+
}
30+
]
31+
32+
egress_rules = ["all-all"]
33+
}
34+
35+
resource "aws_elasticache_subnet_group" "default" {
36+
name = var.resource_name != "" ? var.resource_name : local.name
37+
subnet_ids = var.subnets
38+
}
39+
40+
resource "aws_elasticache_replication_group" "default" {
41+
replication_group_id = var.resource_name != "" ? var.resource_name : local.name
42+
replication_group_description = var.replication_group_description
43+
engine = "redis"
44+
engine_version = var.engine_version
45+
node_type = var.instance_type
46+
port = var.port
47+
number_cache_clusters = var.number_cache_clusters
48+
parameter_group_name = var.parameter_group_name
49+
subnet_group_name = aws_elasticache_subnet_group.default.name
50+
security_group_ids = [module.sg.this_security_group_id]
51+
apply_immediately = var.apply_immediately
52+
at_rest_encryption_enabled = var.at_rest_encryption_enabled
53+
transit_encryption_enabled = var.transit_encryption_enabled
54+
availability_zones = var.availability_zones
55+
tags = local.tags
56+
}
+60
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package test
2+
3+
import (
4+
"fmt"
5+
"log"
6+
"os"
7+
"testing"
8+
9+
"github.com/chanzuckerberg/go-misc/tftest"
10+
"github.com/gruntwork-io/terratest/modules/terraform"
11+
)
12+
13+
func TestAWSRedisReplicationGroup(t *testing.T) {
14+
test := tftest.Test{
15+
16+
Setup: func(t *testing.T) *terraform.Options {
17+
privateSubnets := tftest.ListEnvVar("PRIVATE_SUBNETS")
18+
log.Printf("subnets %#v\n", privateSubnets)
19+
log.Printf("subnets %#v\n", os.Getenv("PRIVATE_SUBNETS"))
20+
vpc := tftest.EnvVar(tftest.EnvVPCID)
21+
22+
sg := tftest.CreateSecurityGroup(t, tftest.DefaultRegion, vpc)
23+
24+
project := tftest.UniqueID()
25+
env := tftest.UniqueID()
26+
service := tftest.UniqueID()
27+
owner := tftest.UniqueID()
28+
replication_group_description := tftest.UniqueID()
29+
transit_encryption_enabled := true
30+
at_rest_encryption_enabled := true
31+
32+
az := fmt.Sprintf("%sa", tftest.DefaultRegion)
33+
34+
return tftest.Options(tftest.DefaultRegion,
35+
map[string]interface{}{
36+
"project": project,
37+
"env": env,
38+
"service": service,
39+
"owner": owner,
40+
41+
"availability_zones": []string{az},
42+
"subnets": privateSubnets,
43+
"ingress_security_group_ids": []string{sg},
44+
"vpc_id": vpc,
45+
46+
"replication_group_description": replication_group_description,
47+
"transit_encryption_enabled": transit_encryption_enabled,
48+
"at_rest_encryption_enabled": at_rest_encryption_enabled,
49+
},
50+
)
51+
},
52+
Validate: func(t *testing.T, options *terraform.Options) {},
53+
54+
Cleanup: func(t *testing.T, options *terraform.Options) {
55+
tftest.DeleteSecurityGroup(t, tftest.DefaultRegion, options.Vars["ingress_security_group_ids"].([]interface{})[0].(string))
56+
},
57+
}
58+
59+
test.Run(t)
60+
}
+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
output "primary_endpoint_address" {
2+
description = "The endpoint of the primary node in this node group (shard)."
3+
value = aws_elasticache_replication_group.default.primary_endpoint_address
4+
}
5+
6+
output "configuration_endpoint_address" {
7+
description = "The configuration endpoint address to allow host discovery."
8+
value = aws_elasticache_replication_group.default.configuration_endpoint_address
9+
}
10+
11+
output "port" {
12+
description = "Redis TCP port."
13+
value = var.port
14+
}
+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
terraform {
2+
required_providers {
3+
aws = "< 3.0.0"
4+
}
5+
}
+102
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
variable "project" {
2+
type = string
3+
description = "Project for tagging and naming. See [doc](../README.md#consistent-tagging)"
4+
}
5+
6+
variable "env" {
7+
type = string
8+
description = "Env for tagging and naming. See [doc](../README.md#consistent-tagging)."
9+
}
10+
11+
variable "service" {
12+
type = string
13+
description = "Service for tagging and naming. See [doc](../README.md#consistent-tagging)"
14+
default = "redis"
15+
}
16+
17+
variable "owner" {
18+
type = string
19+
description = "Owner for tagging and naming. See [doc](../README.md#consistent-tagging)."
20+
}
21+
22+
variable "subnets" {
23+
type = list(string)
24+
description = "List of subnets to which this EC instance should be attached. They should probably be private."
25+
}
26+
27+
variable "availability_zones" {
28+
type = list(string)
29+
description = "Availability zone in which this instance should run."
30+
default = null
31+
}
32+
33+
variable "ingress_security_group_ids" {
34+
type = list(string)
35+
description = "Source security groups which should be able to contact this instance."
36+
}
37+
38+
variable "port" {
39+
type = number
40+
description = "Port to host Redis on."
41+
default = 6379
42+
}
43+
44+
variable "instance_type" {
45+
type = string
46+
description = "The type of instance to run. See [supported node types](https://docs.aws.amazon.com/AmazonElastiCache/latest/red-ug/CacheNodes.SupportedTypes.html)"
47+
default = "cache.m5.large"
48+
}
49+
50+
variable "parameter_group_name" {
51+
type = string
52+
description = "Parameter group to use for this Redis cache."
53+
default = "default.redis5.0"
54+
}
55+
56+
variable "engine_version" {
57+
type = string
58+
description = "The version of Redis to run. See [supported versions](https://docs.aws.amazon.com/AmazonElastiCache/latest/red-ug/supported-engine-versions.html)"
59+
default = "5.0.5"
60+
}
61+
62+
variable "apply_immediately" {
63+
type = bool
64+
description = "Whether changes should be applied immediately or during the next maintenance window."
65+
default = true
66+
}
67+
68+
# This is to get a around a limitation where the elasticache cluster id can be
69+
# only 20 characters long. Use it only if you get that error.
70+
variable "resource_name" {
71+
description = "If not set, name will be [var.project]-[var.env]-[var.name]."
72+
type = string
73+
default = ""
74+
}
75+
76+
variable "vpc_id" {
77+
type = string
78+
description = "VPC where the cache will be deployed."
79+
}
80+
81+
variable "number_cache_clusters" {
82+
type = number
83+
description = "Number of cache clusters. Default 1."
84+
default = 1
85+
}
86+
87+
variable "at_rest_encryption_enabled" {
88+
type = bool
89+
description = "Whether to enable encryption at rest. Default: false."
90+
default = false
91+
}
92+
93+
variable "transit_encryption_enabled" {
94+
type = bool
95+
description = "Whether to enable encryption in transit. Default: false."
96+
default = false
97+
}
98+
99+
variable "replication_group_description" {
100+
type = string
101+
description = "A user-created description for the replication group."
102+
}

0 commit comments

Comments
 (0)