A secure-by-default Amazon MemoryDB cluster β a durable, in-memory database for Redis/Valkey workloads β always encrypted at rest, always TLS-required in transit, RBAC-gated through a named ACL, and snapshot-backed, delivering the cluster, its subnet group, parameter group, RBAC users, and ACL from a single composite call. Built for the AWS provider v6.x.
- β‘ Provisions an Amazon MemoryDB cluster β the keystone is
aws_memorydb_cluster.this. Unlike ElastiCache, MemoryDB is a durable primary database: writes are persisted to a Multi-AZ transactional log, so it can be a system of record, not just a cache. - π Owns the subnet group (
<name>-subnets, private subnets, β₯ 2 AZs for a multi-shard / multi-replica topology) so a single call yields a complete, private placement. - π₯ Optionally creates RBAC users (
aws_memorydb_user) as afor_eachmap keyed byuser_id, bundled into a module-created named ACL (aws_memorydb_acl) and bound to the cluster β the secure path that replaces the discouraged built-inopen-accessACL. - ποΈ Optionally creates a dedicated parameter group (
<name>-params) for engine tuning (maxmemory-policy, etc.). - π Secure by default: at-rest encryption is always on (AWS-managed key, or a caller CMK), in-transit TLS is always on and cannot be disabled, access defaults to a named RBAC ACL (never
open-access), one replica per shard for automatic failover, and 7-day snapshot retention. - π§© Consumes networking, security, and KMS by reference β it never creates a VPC, security group, or KMS key itself; secrets (RBAC passwords) are referenced from Secrets Manager rather than inlined.
π‘ Why it matters: Because MemoryDB is durable, it frequently holds the authoritative copy of session state, tokens, and cached PII under privacy-regulation β the blast radius of a misconfigured cluster is larger than a throwaway cache. This module ships locked down (mandatory encryption, mandatory TLS, RBAC ACL) and makes you opt out of the few relaxable defaults explicitly.
If these Terraform modules have been helpful to you or your organization, I'd appreciate your support in any of the following ways:
- β Star this repository to help others discover this Terraform module.
- π€ Connect with me on LinkedIn: linkedin.com/in/microsoftexpert
- β Buy me a coffee: buymeacoffee.com/microsoftexpert
Whether it's a star, a professional connection, or a coffee, every gesture helps keep these modules actively maintained and continually improving. Thank you for being part of the community!
flowchart LR
VPC["terraform-aws-vpc"]
SG["terraform-aws-security-group"]
KMS["terraform-aws-kms"]
SM["terraform-aws-secrets-manager"]
MDB["terraform-aws-memorydb"]
APP["Application tier<br/>(ECS / EKS / EC2)"]
VPC -->|subnet_ids| MDB
SG -->|security_group_ids| MDB
KMS -->|kms_key_arn| MDB
SM -->|RBAC user passwords| MDB
MDB -->|cluster configuration endpoint| APP
style MDB fill:#FF9900,color:#fff
MemoryDB sits at the durable data tier. It is downstream of the networking, security-group, and KMS foundations and upstream of the application tier that connects to its cluster (configuration) endpoint. Secrets Manager supplies RBAC user passwords.
flowchart TD
subgraph caller["Caller-supplied (by reference)"]
SUBNETS["subnet_ids<br/>(terraform-aws-vpc)"]
SGS["security_group_ids<br/>(terraform-aws-security-group)"]
CMK["kms_key_arn<br/>(terraform-aws-kms)"]
SECRET["user passwords<br/>(terraform-aws-secrets-manager)"]
end
subgraph mod["terraform-aws-memorydb"]
SUB["aws_memorydb_subnet_group.this"]
PG["aws_memorydb_parameter_group.this<br/>(optional)"]
U["aws_memorydb_user.this<br/>for_each β RBAC users"]
ACL["aws_memorydb_acl.this<br/>(optional, binds users)"]
CL["aws_memorydb_cluster.this<br/>keystone"]
end
SUBNETS --> SUB
SUB --> CL
PG --> CL
SGS --> CL
CMK --> CL
SECRET --> U
U --> ACL
ACL --> CL
style CL fill:#FF9900,color:#fff
| Resource | Role |
|---|---|
aws_memorydb_cluster.this |
Keystone β the durable in-memory cluster. Encryption, topology, backups, ACL binding |
aws_memorydb_subnet_group.this |
Placement across caller-supplied private subnets (<name>-subnets, β₯ 2 AZs) |
aws_memorydb_parameter_group.this |
Engine parameters (created only when parameter_group is set; <name>-params) |
aws_memorydb_user.this |
RBAC users, for_each over var.users keyed by user_id |
aws_memorydb_acl.this |
Named ACL binding the RBAC users to the cluster (created only when acl is set; <name>-acl) |
| Requirement | Version |
|---|---|
| Terraform | >= 1.12.0 |
hashicorp/aws |
>= 6.0, < 7.0 |
No provider {} block is declared inside the module β it inherits the caller's configured provider (and credential chain / Region). See the AWS Prerequisites Region note below.
The Terraform identity needs the following (least-privilege). RBAC, ACL, parameter-group, and KMS actions are needed only when their respective features are configured.
| Action | Required for | Notes |
|---|---|---|
memorydb:CreateCluster, memorydb:UpdateCluster, memorydb:DeleteCluster, memorydb:DescribeClusters |
Cluster lifecycle | Keystone |
memorydb:CreateSubnetGroup, memorydb:UpdateSubnetGroup, memorydb:DeleteSubnetGroup, memorydb:DescribeSubnetGroups |
Subnet group | Module-owned |
memorydb:CreateParameterGroup, memorydb:UpdateParameterGroup, memorydb:DeleteParameterGroup, memorydb:DescribeParameterGroups |
Parameter group | Only when parameter_group is set |
memorydb:CreateUser, memorydb:UpdateUser, memorydb:DeleteUser, memorydb:DescribeUsers |
RBAC users | Only when users is set |
memorydb:CreateACL, memorydb:UpdateACL, memorydb:DeleteACL, memorydb:DescribeACLs |
Named ACL | Only when acl is set |
memorydb:TagResource, memorydb:UntagResource, memorydb:ListTags |
Tagging | All taggable resources |
memorydb:CreateSnapshot, memorydb:DescribeSnapshots |
Final / automatic snapshots | final_snapshot_name on destroy; snapshot_retention_limit |
kms:DescribeKey, kms:CreateGrant, kms:RetireGrant |
CMK at-rest encryption | Only when kms_key_arn (CMK) is supplied |
iam:CreateServiceLinkedRole |
First-time AWSServiceRoleForMemoryDB creation |
One-time per account; harmless if it already exists |
ec2:CreateNetworkInterface, ec2:DescribeSubnets, ec2:DescribeSecurityGroups, ec2:DescribeVpcs |
VPC placement / ENI provisioning | Performed by the service via the SLR; describe calls validate the subnet group |
β οΈ Scope the resource ARNs in your policy toarn:aws:memorydb:<region>:<account>:cluster/*/:subnetgroup/*/:parametergroup/*/:user/*/:acl/*patterns rather than"*"where your governance allows.
- Service-linked role:
AWSServiceRoleForMemoryDBis auto-created on first MemoryDB use (iam:CreateServiceLinkedRole). It lets the service manage ENIs and resources inside your VPC. - Networking: the supplied
subnet_idsmust be private (MemoryDB has no public endpoint β PII/privacy-regulation baseline) and span at least two Availability Zones for any multi-shard or multi-replica cluster (the default is one replica per shard). The security groups must allow the cluster port (6379 by default) from the application security group only β never0.0.0.0/0. - Mandatory at-rest encryption: MemoryDB is always encrypted at rest β there is no toggle. Supplying
kms_key_arnselects a customer-managed key; omitting it uses the AWS-managed key. A CMK's key policy must allow the MemoryDB service principal (memorydb.amazonaws.com) the standardkms:Encrypt/Decrypt/GenerateDataKey*/CreateGrantset. Changingkms_key_arnis FORCE-NEW. - Mandatory in-transit encryption (TLS): unlike ElastiCache, MemoryDB cannot disable TLS β it is always on. RBAC therefore always operates over an encrypted channel.
- ACL required: every cluster must reference an ACL. The built-in
open-accessACL disables RBAC and is strongly discouraged for PII/privacy-regulation workloads β define RBAC users viausersand let the module create a named ACL (acl). Provide exactly one ofacloracl_name. - Engine / node availability: the chosen
engine(redis/valkey),engine_version, andnode_typemust be offered in the target Region. MemoryDB uses thedb.*node family (e.g.db.t4g.small,db.r7g.large), not the ElastiCachecache.*family.data_tiering = truerequires an r6gd node type. - Region model: the module relies on provider inheritance β there is no
regionvariable. The caller's provider block (or alias) sets the Region. MemoryDB is a regional service β no us-east-1 global-service constraint applies. - Service quotas: default soft limits on nodes/clusters per Region, all raisable via Service Quotas. A
ClusterQuotaForCustomerExceededFaultindicates the per-account cluster quota was hit.
terraform-aws-memorydb/
βββ providers.tf # terraform{} + required_providers (aws >= 6.0, < 7.0); no provider{} block
βββ variables.tf # typed inputs: identity β required β optional β tags β timeouts
βββ main.tf # subnet group, parameter group, RBAC users, ACL, cluster (this)
βββ outputs.tf # id + arn, endpoints, RBAC/ACL maps, subnet/parameter group, tags_all
βββ SCOPE.md # boundary, IAM, prerequisites, gotchas, secure defaults
βββ README.md # this file
The smallest working call β networking, security, and RBAC wired from upstream modules:
module "memorydb" {
source = "git::https://github.com/microsoftexpert/terraform-aws-memorydb?ref=v1.0.0"
name = "casey-core-mdb"
node_type = "db.r7g.large"
subnet_ids = module.vpc.private_subnet_ids
security_group_ids = [module.mdb_sg.id]
users = {
app-rw = {
user_name = "app-rw"
access_string = "on ~* &* +@all"
authentication_mode = {
type = "password"
passwords = [data.aws_secretsmanager_secret_version.app_rw.secret_string] # sensitive
}
}
}
acl = {} # module creates `<name>-acl` and binds the users above
tags = {
Environment = "prod"
DataClass = "PII"
CostCenter = "platform-data"
}
}Everything not shown inherits the secure baseline: at-rest encryption on (always), TLS on (always), one replica per shard for automatic failover, and 7-day snapshot retention.
| Input | Type | Source module |
|---|---|---|
subnet_ids |
list(string) |
terraform-aws-vpc (private subnets, β₯ 2 AZs) |
security_group_ids |
list(string) |
terraform-aws-security-group |
kms_key_arn |
string (KMS key ARN, optional) |
terraform-aws-kms |
user passwords |
string (sensitive, optional) |
terraform-aws-secrets-manager |
sns_topic_arn |
string (SNS ARN, optional) |
app-integration module |
| Output | Description | Consumed by |
|---|---|---|
id |
Cluster id (same as name) |
references |
arn |
Cluster ARN β cross-resource reference type | IAM policies, monitoring, AWS Backup |
name |
Cluster name | CLI / console / app config |
engine |
redis / valkey |
conditionals |
engine_patch_version |
Running engine patch version | drift / audit |
cluster_endpoint_address |
Cluster (configuration) endpoint host β the client connection target | application config |
cluster_endpoint_port |
Cluster port | application config |
shards |
Shard topology (name, num_nodes, slots, per-node endpoints) | monitoring |
acl_name |
ACL bound to the cluster (module-created or external) | references |
acl_arn |
ARN of the module-created ACL (null when external) |
references |
user_arns |
Map of user_id β ARN for module-created RBAC users |
audit |
subnet_group_name / subnet_group_arn |
Module-created subnet group | references |
parameter_group_name / parameter_group_arn |
Parameter group (module-created, external, or engine default) | references |
tags_all |
All tags incl. provider default_tags |
governance / audit |
1 Β· Minimal (secure defaults, RBAC ACL)
module "memorydb" {
source = "git::https://github.com/microsoftexpert/terraform-aws-memorydb?ref=v1.0.0"
name = "casey-mdb-min"
node_type = "db.t4g.small"
subnet_ids = module.vpc.private_subnet_ids
security_group_ids = [module.mdb_sg.id]
users = {
app = {
user_name = "app"
access_string = "on ~* &* +@all"
authentication_mode = {
type = "password"
passwords = [data.aws_secretsmanager_secret_version.app.secret_string]
}
}
}
acl = {} # creates `casey-mdb-min-acl`
}At-rest encryption, TLS, one replica per shard, and 7-day snapshots are all on by default.
2 Β· Customer-managed KMS key (CMK) for at-rest encryption
module "memorydb" {
source = "git::https://github.com/microsoftexpert/terraform-aws-memorydb?ref=v1.0.0"
name = "casey-mdb-cmk"
node_type = "db.r7g.large"
subnet_ids = module.vpc.private_subnet_ids
security_group_ids = [module.mdb_sg.id]
kms_key_arn = module.kms.arn # auditable, independently-revocable CMK
acl_name = "open-access" # (shown for brevity; prefer a named ACL β see example 1)
}MemoryDB is always encrypted at rest. Supplying
kms_key_arnupgrades the AWS-managed key to your CMK. Changing it later is FORCE-NEW.
3 Β· Tags (merge with provider default_tags)
# Provider-level default_tags is the CALLER's concern (root module / pipeline):
provider "aws" {
default_tags { tags = { ManagedBy = "terraform", Org = "" } }
}
module "memorydb" {
source = "git::https://github.com/microsoftexpert/terraform-aws-memorydb?ref=v1.0.0"
name = "casey-mdb-tagged"
node_type = "db.r7g.large"
subnet_ids = module.vpc.private_subnet_ids
security_group_ids = [module.mdb_sg.id]
acl_name = "open-access"
tags = {
Environment = "prod"
DataClass = "PII"
Org = "-Data" # resource tag wins over default_tags on key conflict
}
}
# module.memorydb.tags_all => { ManagedBy, Org=-Data, Environment, DataClass }4 Β· Sharded cluster (cluster mode)
module "memorydb" {
source = "git::https://github.com/microsoftexpert/terraform-aws-memorydb?ref=v1.0.0"
name = "casey-mdb-sharded"
node_type = "db.r7g.large"
subnet_ids = module.vpc.private_subnet_ids # 3 AZs
security_group_ids = [module.mdb_sg.id]
num_shards = 3
num_replicas_per_shard = 2 # 3 shards Γ (1 primary + 2 replicas) = 9 nodes
acl = {}
users = {
app = {
user_name = "app"
access_string = "on ~* &* +@all"
authentication_mode = { type = "password", passwords = [data.aws_secretsmanager_secret_version.app.secret_string] }
}
}
}Clients connect to the cluster configuration endpoint for shard auto-discovery.
5 Β· Multiple RBAC users (read-write + read-only) in a named ACL
module "memorydb" {
source = "git::https://github.com/microsoftexpert/terraform-aws-memorydb?ref=v1.0.0"
name = "casey-mdb-rbac"
node_type = "db.r7g.large"
subnet_ids = module.vpc.private_subnet_ids
security_group_ids = [module.mdb_sg.id]
users = {
app-rw = {
user_name = "app-rw"
access_string = "on ~* &* +@all"
authentication_mode = { type = "password", passwords = [data.aws_secretsmanager_secret_version.app_rw.secret_string] }
}
app-ro = {
user_name = "app-ro"
access_string = "on ~* &* +@read"
authentication_mode = { type = "password", passwords = [data.aws_secretsmanager_secret_version.app_ro.secret_string] }
}
}
acl = {
name = "casey-mdb-rbac-acl"
user_names = ["audit-readonly"] # externally-managed user, added alongside module users
}
}Every key in
usersis added to the ACL automatically;acl.user_namesappends externally-managed user names.
6 Β· IAM-authenticated RBAC user
module "memorydb" {
source = "git::https://github.com/microsoftexpert/terraform-aws-memorydb?ref=v1.0.0"
name = "casey-mdb-iamauth"
node_type = "db.r7g.large"
subnet_ids = module.vpc.private_subnet_ids
security_group_ids = [module.mdb_sg.id]
users = {
app = {
user_name = "app"
access_string = "on ~* &* +@all"
authentication_mode = { type = "iam" } # no passwords β auth via IAM
}
}
acl = {}
}
type = "iam"lets clients authenticate with IAM identities β no password to store. Prefer this where the client runtime already has an IAM principal.
7 Β· Custom parameter group (module-created)
module "memorydb" {
source = "git::https://github.com/microsoftexpert/terraform-aws-memorydb?ref=v1.0.0"
name = "casey-mdb-params"
node_type = "db.r7g.large"
subnet_ids = module.vpc.private_subnet_ids
security_group_ids = [module.mdb_sg.id]
acl = {}
parameter_group = {
family = "memorydb_redis7"
description = "MemoryDB Redis 7 tuned parameters"
parameters = {
"maxmemory-policy" = "allkeys-lru"
}
}
}The module creates <name>-params and associates it (takes precedence over parameter_group_name).
8 Β· Valkey engine
module "memorydb" {
source = "git::https://github.com/microsoftexpert/terraform-aws-memorydb?ref=v1.0.0"
name = "casey-mdb-valkey"
node_type = "db.r7g.large"
subnet_ids = module.vpc.private_subnet_ids
security_group_ids = [module.mdb_sg.id]
engine = "valkey"
engine_version = "7.3"
acl = {}
parameter_group = { family = "memorydb_valkey7" }
}Valkey is the Redis-compatible, lower-cost engine β RBAC, encryption, and durability apply identically. Match the parameter-group family to the engine.
9 Β· Custom snapshot + maintenance windows, 35-day retention, final snapshot
module "memorydb" {
source = "git::https://github.com/microsoftexpert/terraform-aws-memorydb?ref=v1.0.0"
name = "casey-mdb-backups"
node_type = "db.r7g.large"
subnet_ids = module.vpc.private_subnet_ids
security_group_ids = [module.mdb_sg.id]
acl = {}
snapshot_retention_limit = 35
snapshot_window = "04:00-05:00" # UTC
maintenance_window = "sun:05:30-sun:06:30" # UTC
final_snapshot_name = "casey-mdb-backups-final"
}10 Β· Seed a new cluster from an existing snapshot
module "memorydb" {
source = "git::https://github.com/microsoftexpert/terraform-aws-memorydb?ref=v1.0.0"
name = "casey-mdb-restored"
node_type = "db.r7g.large"
subnet_ids = module.vpc.private_subnet_ids
security_group_ids = [module.mdb_sg.id]
acl = {}
snapshot_name = "casey-core-mdb-2026-06-18" # FORCE-NEW; or seed from S3 via snapshot_arns
}11 Β· SNS notifications + auto minor-version upgrades
module "memorydb" {
source = "git::https://github.com/microsoftexpert/terraform-aws-memorydb?ref=v1.0.0"
name = "casey-mdb-notify"
node_type = "db.r7g.large"
subnet_ids = module.vpc.private_subnet_ids
security_group_ids = [module.mdb_sg.id]
acl = {}
sns_topic_arn = module.mdb_events_topic.arn # app-integration module
auto_minor_version_upgrade = true # default
}12 Β· Reference an existing, externally-managed ACL
module "memorydb" {
source = "git::https://github.com/microsoftexpert/terraform-aws-memorydb?ref=v1.0.0"
name = "casey-mdb-extacl"
node_type = "db.r7g.large"
subnet_ids = module.vpc.private_subnet_ids
security_group_ids = [module.mdb_sg.id]
# No `acl` / `users` here β bind to an ACL managed elsewhere:
acl_name = "platform-shared-acl"
}Provide exactly one of
acl(module-created) oracl_name(external).
13 Β· Disposable dev cluster (resilience defaults relaxed)
module "memorydb" {
source = "git::https://github.com/microsoftexpert/terraform-aws-memorydb?ref=v1.0.0"
name = "casey-mdb-dev"
node_type = "db.t4g.small"
subnet_ids = module.vpc.private_subnet_ids
security_group_ids = [module.mdb_sg.id]
acl = {}
# OPT-OUTS β dev only, never in prod / PII:
num_replicas_per_shard = 0 # single node per shard, no automatic failover
snapshot_retention_limit = 0 # disables automatic snapshots
tags = { Environment = "dev", DataClass = "synthetic" }
}At-rest encryption and TLS remain on β they cannot be disabled on MemoryDB, even for a disposable cluster.
14 Β· π End-to-end composition (VPC β SG β KMS β Secrets β MemoryDB)
module "vpc" {
source = "git::https://github.com/microsoftexpert/terraform-aws-vpc?ref=v1.0.0"
name = "casey-data"
vpc_cidr = "10.40.0.0/16"
#... produces private_subnet_ids across 3 AZs
}
module "kms" {
source = "git::https://github.com/microsoftexpert/terraform-aws-kms?ref=v1.0.0"
description = "CMK for MemoryDB at-rest encryption"
alias = "alias/casey-mdb"
}
module "mdb_sg" {
source = "git::https://github.com/microsoftexpert/terraform-aws-security-group?ref=v1.0.0"
name = "casey-mdb-sg"
vpc_id = module.vpc.id
ingress_rules = {
memorydb = {
from_port = 6379
to_port = 6379
ip_protocol = "tcp"
referenced_security_group_id = module.app_sg.id # app tier only β never 0.0.0.0/0
description = "MemoryDB from application tier"
}
}
}
data "aws_secretsmanager_secret_version" "mdb_app" {
secret_id = "casey/memorydb/app-rw"
}
module "memorydb" {
source = "git::https://github.com/microsoftexpert/terraform-aws-memorydb?ref=v1.0.0"
name = "casey-core-mdb"
node_type = "db.r7g.large"
engine = "redis"
engine_version = "7.1"
subnet_ids = module.vpc.private_subnet_ids
security_group_ids = [module.mdb_sg.id]
kms_key_arn = module.kms.arn
num_shards = 2
num_replicas_per_shard = 1
users = {
app-rw = {
user_name = "app-rw"
access_string = "on ~* &* +@all"
authentication_mode = { type = "password", passwords = [data.aws_secretsmanager_secret_version.mdb_app.secret_string] }
}
}
acl = { name = "casey-core-mdb-acl" }
snapshot_retention_limit = 14
final_snapshot_name = "casey-core-mdb-final"
tags = {
Environment = "prod"
DataClass = "PII"
Compliance = "privacy-regulation"
}
}
output "mdb_endpoint" { value = module.memorydb.cluster_endpoint_address }
output "mdb_arn" { value = module.memorydb.arn }| Name | Type | Default | Description |
|---|---|---|---|
name |
string |
β (required) | Cluster name; base name for subnet/parameter group + ACL. FORCE-NEW. 1β40 chars, lowercase |
node_type |
string |
β (required) | Node instance class β db.* family (e.g. db.r7g.large). r6gd required for data tiering |
subnet_ids |
list(string) |
β (required) | Subnet group members (private, β₯ 2 AZs for multi-shard/replica) |
security_group_ids |
list(string) |
[] |
Security groups attached to the cluster |
acl |
object |
null |
Module-creates a named ACL (<name>-acl) binding users + acl.user_names. Exactly one of acl / acl_name |
acl_name |
string |
null |
Existing external ACL to associate. open-access disables RBAC (discouraged) |
users |
map(object) |
{} |
RBAC users keyed by user_id; each has user_name, access_string, authentication_mode |
engine |
string |
null (β redis) |
redis / valkey |
engine_version |
string |
null |
Engine version; null = latest at create. Downgrades unsupported |
port |
number |
null (β 6379) |
Cluster port. FORCE-NEW |
num_shards |
number |
1 |
Shards (data partitions) |
num_replicas_per_shard |
number |
1 |
Replicas per shard (0β5); 1 = HA baseline |
data_tiering |
bool |
false |
Data tiering (r6gd only). FORCE-NEW |
kms_key_arn |
string |
null |
CMK for at-rest encryption (else AWS-managed key). FORCE-NEW |
tls_enabled |
bool |
true |
In-transit TLS. FORCE-NEW; MemoryDB does not support disabling it in practice |
parameter_group_name |
string |
null |
Existing parameter group to associate |
parameter_group |
object |
null |
Module-creates a dedicated parameter group <name>-params |
snapshot_retention_limit |
number |
7 |
Snapshot retention days (0β35; 0 disables) |
snapshot_window |
string |
null |
Daily UTC snapshot window |
snapshot_arns / snapshot_name |
list/string |
null |
Seed / restore from snapshot. FORCE-NEW |
final_snapshot_name |
string |
null |
Final snapshot on destroy |
maintenance_window |
string |
null |
Weekly UTC maintenance window |
auto_minor_version_upgrade |
bool |
true |
Auto minor-version upgrades. FORCE-NEW |
sns_topic_arn |
string |
null |
SNS topic for cluster notifications |
ip_discovery / network_type |
string |
null |
IP version / dual-stack (network_type FORCE-NEW) |
multi_region_cluster_name |
string |
null |
Multi-region cluster membership |
description |
string |
null |
Cluster description |
subnet_group_description |
string |
"Managed by Terraform" |
Subnet group description |
tags |
map(string) |
{} |
Tags merged onto all taggable resources |
timeouts |
object |
{} |
create / update / delete timeouts |
βΉοΈ Full type schemas and per-field descriptions live in
variables.tf;
See the Emits table above. Primary outputs are id and arn; connectivity is exposed as cluster_endpoint_address / cluster_endpoint_port; shard topology as shards; RBAC/ACL data as acl_name / acl_arn / user_arns; the module-created groups as subnet_group_name/_arn and parameter_group_name/_arn; tags_all reflects the merged tag set. No secret is emitted β RBAC passwords are referenced from Secrets Manager, never output.
- ARN / ID formats.
- Cluster
arn:arn:aws:memorydb:<region>:<account>:cluster/<name>;id= the cluster name. - Subnet group
arn::subnetgroup/<name>-subnets; parameter grouparn::parametergroup/<name>-params; RBAC userarn::user/<user_name>; ACLarn::acl/<name>-acl. - Durable, not a cache. MemoryDB persists writes to a Multi-AZ transactional log, so it can be a primary database. This is why the secure posture (mandatory encryption + TLS, RBAC ACL, snapshots, β₯1 replica) is enforced more tightly than
terraform-aws-elasticache. - FORCE-NEW (immutable) fields.
name,subnet_group_namemembership,port,kms_key_arn,network_type,data_tiering,auto_minor_version_upgrade,tls_enabled, an engine-version downgrade, andsnapshot_arns/snapshot_nameall destroy-and-recreate the cluster (and its data). The module-created parameter group and ACL usecreate_before_destroyso family/membership changes don't deadlock. - Encryption and TLS are mandatory. Unlike ElastiCache there is no
at_rest_encryption_enabledtoggle and no way to turn TLS off βkms_key_arnonly chooses which key encrypts at rest (CMK vs AWS-managed).tls_enableddefaults totrueand the service requires it for RBAC. - ACL coupling. The cluster's
acl_namemust reference an existing ACL. The module computeseffective_acl_namefrom the module-createdaws_memorydb_acl.this(whenaclis set) or the externalacl_name, and the ACL's membership is the union of everyaws_memorydb_user.thisplusacl.user_names. Create users and the ACL before/alongside the cluster β the dependency graph orders this. tagsβtags_allβdefault_tags. The module sets only resource-leveltags(merged with per-item tags on users/ACL viamerge(var.tags, try(each.value.tags, {}))). The provider'sdefault_tagsis the caller's concern (never set inside a module). On a key collision the resource tag wins.tags_all(output) is the computed union AWS actually applied β use it for drift checks and audit.- Eventual consistency. Cluster and node creation is asynchronous and can take many minutes; the
cluster_endpoint_addressis a stable DNS name that follows failover, so applications should resolve it at connect time rather than caching IPs. - Destroy ordering. Terraform tears down the cluster β ACL β users / parameter group / subnet group. A subnet group, parameter group, or ACL cannot be deleted while the cluster still references it; the dependency graph normally orders this, but a half-failed destroy can leave a group/ACL pinned by a lingering cluster. With
final_snapshot_nameset, a final snapshot is taken before deletion. The service-managed ENIs are cleaned up by the SLR β there are no NAT/ENI destroy hazards to manage directly. - No us-east-1 constraint. MemoryDB is a regional service β none of the us-east-1 global-service rules (CloudFront/WAF/ACM) apply. Rely on provider inheritance for the Region.
Secure by default; every weakening is an explicit, documented opt-out.
| Posture | Default | How to opt out |
|---|---|---|
| At-rest encryption | always on (AWS-managed key) | n/a β cannot be disabled |
| Customer-managed key | available via kms_key_arn |
omit for the AWS-managed key |
| In-transit TLS | tls_enabled = true, always on |
n/a β MemoryDB does not allow disabling TLS |
| Access control | named RBAC ACL via acl + users |
acl_name = "open-access" (strongly discouraged) |
| Public exposure | private subnets only β no public endpoint | n/a |
| Automatic failover / HA | num_replicas_per_shard = 1 (replica in another AZ) |
0 (single node per shard; documented exception) |
| Backups | snapshot_retention_limit = 7 |
0 (disables snapshots; discouraged) |
| Final snapshot on destroy | recommended via final_snapshot_name |
leave null (skips it) |
Other principles: exactly four .tf files; one keystone named this; child collections (users, ACL, parameter group) via for_each over map(object) (never count); deeply-typed object schemas with optional defaults; validation {} on every closed value set (engine, network_type, ip_discovery, authentication_mode.type, snapshot/replica ranges); no credential or region variables; secrets referenced not stored; primary outputs id + arn; tags_all surfaced.
# Validate (no credentials needed)
terraform init -backend=false
terraform validate
terraform fmt -check
# Plan / apply (requires AWS credentials + Region)
# credentials via AWS_PROFILE / SSO / OIDC; Region via the provider block
terraform plan -out tfplan
terraform apply tfplan
plan/applyrequire a valid credential chain (profile / SSO / OIDC web-identity) and a configured Region. The module declares noprovider {}block β supply it (and anyassume_role) at the root. Cluster creation can take several minutes; raisetimeouts.createfor large sharded topologies.
β οΈ Always pin the module source with?ref=v1.0.0β never a branch.
terraform init -backend=false && terraform validateβ schema and reference integrity.terraform fmt -checkβ canonical formatting.terraform planagainst a sandbox account β confirm the secure defaults render (at-rest encryption + TLS on, one replica per shard, 7-day snapshots) and that the ACL is the module-created named ACL rather thanopen-access.- Post-apply smoke test: connect over TLS from an in-VPC host to the
cluster_endpoint_addresson 6379, authenticate with an RBAC user, and runPING/INFO replication.
Apply complete! Resources: 4 added, 0 changed, 0 destroyed.
Outputs:
arn = "arn:aws:memorydb:us-east-2:123456789012:cluster/casey-core-mdb"
id = "casey-core-mdb"
mdb_endpoint = "clustercfg.casey-core-mdb.abc123.memorydb.us-east-2.amazonaws.com"
acl_name = "casey-core-mdb-acl"
| Symptom | Likely cause | Fix |
|---|---|---|
| Tag drift on every plan | default_tags overlaps a key the module also sets |
Drop the duplicate from one side; resource tags win β reconcile in the root module |
AccessDenied on memorydb:CreateCluster |
Identity lacks the memorydb: actions |
Attach the Required IAM Permissions |
InvalidParameterValue:... AWSServiceRoleForMemoryDB |
SLR not yet created and iam:CreateServiceLinkedRole missing |
Grant iam:CreateServiceLinkedRole, or pre-create the SLR |
ACLNotFound / cluster create rejects acl_name |
The ACL doesn't exist yet, or both acl and acl_name were set |
Provide exactly one of acl (module-created) or acl_name (existing) |
| ACL create fails β user not found | A name in acl.user_names isn't a real MemoryDB user |
Create the user first (or via users); only externally-managed names go in user_names |
users[*].authentication_mode.type validation error |
Value other than password / iam |
Use password (with passwords) or iam (no password) |
| Changing the KMS key triggers full replacement | kms_key_arn is FORCE-NEW |
Choose the CMK at creation; migrating keys requires a rebuild/restore |
Parameter-group family mismatch |
family doesn't match engine/engine_version (e.g. memorydb_redis7 vs Valkey) |
Set family to the matching engine family (memorydb_valkey7, etc.) |
| Node type rejected | Used an ElastiCache cache.* type |
MemoryDB uses the db.* family β e.g. db.r7g.large |
| Subnet/parameter group or ACL won't delete | A lingering cluster still references it (half-failed destroy) | Remove the cluster first; Terraform's graph normally orders this |
ClusterQuotaForCustomerExceededFault |
Region/account cluster soft limit hit | Raise the quota via Service Quotas |
| Credential-chain errors on plan/apply | No profile/SSO/OIDC resolved, or wrong Region | Set AWS_PROFILE / assume the role; confirm the provider Region |
- Amazon MemoryDB Developer Guide
- Getting started with MemoryDB
- Accessing your MemoryDB cluster
- Authenticating with RBAC and ACLs (MemoryDB)
- Using service-linked roles for MemoryDB
- Terraform
aws_memorydb_cluster - Terraform
aws_memorydb_acl - Terraform
aws_memorydb_user - Module
SCOPE.mdβ boundary, IAM, prerequisites, gotchas - Upstream:
terraform-aws-vpc,terraform-aws-security-group,terraform-aws-kms,terraform-aws-secrets-manager
π§‘ "Infrastructure as Code should be standardized, consistent, and secure."