Skip to content

RDS DB clusters should be configured for multiple Availability Zones

A single-AZ database deployment creates an availability bottleneck. If that AZ experiences an outage, your database can become unreachable until failover or recovery actions complete.

Multi-AZ architectures maintain capacity in more than one AZ and support automatic failover. Failover timing varies by engine, topology, and workload, and is typically measured in tens of seconds to a few minutes. The cost difference is real, typically doubling compute spend for equivalent instance counts, but that's usually still less than the business impact of an unplanned outage on a production database.

Retrofit consideration

Enabling Multi-AZ on an existing single-AZ Aurora cluster means adding cluster instances in new AZs, which triggers provisioning time and increased cost. For non-Aurora Multi-AZ DB clusters, converting may require downtime or a blue-green deployment.

Implementation

Choose the approach that matches how you manage Terraform.

Use the compliance.tf module to enforce this control by default. See get started with compliance.tf.

module "rds_aurora" {
  source  = "nistcsf.compliance.tf/terraform-aws-modules/rds-aurora/aws"
  version = ">=10.0.0,<11.0.0"

  create_db_subnet_group = true
  create_security_group  = true
  engine                 = "aurora-mysql"
  engine_version         = "8.0.mysql_aurora.3.08.0"
  instances = {
    one = {
      instance_class = "db.t4g.medium"
    }
  }
  manage_master_user_password = true
  master_username             = "root"
  name                        = "abc123"
  skip_final_snapshot         = true
  subnets                     = ["subnet-12345678", "subnet-12345678"]
  vpc_id                      = "vpc-12345678"
}

module "rds_aurora" {
  source  = "nistcsfv11.compliance.tf/terraform-aws-modules/rds-aurora/aws"
  version = ">=10.0.0,<11.0.0"

  create_db_subnet_group = true
  create_security_group  = true
  engine                 = "aurora-mysql"
  engine_version         = "8.0.mysql_aurora.3.08.0"
  instances = {
    one = {
      instance_class = "db.t4g.medium"
    }
  }
  manage_master_user_password = true
  master_username             = "root"
  name                        = "abc123"
  skip_final_snapshot         = true
  subnets                     = ["subnet-12345678", "subnet-12345678"]
  vpc_id                      = "vpc-12345678"
}

If you use terraform-aws-modules/rds-aurora/aws, set the right module inputs for this control. You can later migrate to the compliance.tf module with minimal changes because it is compatible by design.

module "rds_aurora" {
  source  = "terraform-aws-modules/rds-aurora/aws"
  version = ">=10.0.0,<11.0.0"

  create_db_subnet_group = true
  create_security_group  = true
  engine                 = "aurora-mysql"
  engine_version         = "8.0.mysql_aurora.3.08.0"
  instances = {
    one = {
      instance_class = "db.t4g.medium"
    }
  }
  manage_master_user_password = true
  master_username             = "root"
  name                        = "abc123"
  skip_final_snapshot         = true
  subnets                     = ["subnet-12345678", "subnet-12345678"]
  vpc_id                      = "vpc-12345678"
}

Use AWS provider resources directly. See docs for the resources involved: aws_rds_cluster.

resource "aws_rds_cluster" "this" {
  backtrack_window                = 72
  cluster_identifier              = "pofix-abc123"
  db_subnet_group_name            = "example-db-subnet-group"
  enabled_cloudwatch_logs_exports = ["audit"]
  engine                          = "aurora-mysql"
  engine_version                  = "8.0.mysql_aurora.3.08.0"
  manage_master_user_password     = true
  master_username                 = "dbadmin"
  skip_final_snapshot             = true
  vpc_security_group_ids          = ["sg-12345678"]
}

What this control checks

For Aurora clusters, the control validates that aws_rds_cluster has availability_zones set to at least two distinct AZs, and that corresponding aws_rds_cluster_instance resources exist in those AZs. Each instance can specify availability_zone to pin it to a particular AZ. A cluster with only one instance, or with all instances in the same AZ, fails. For non-Aurora Multi-AZ DB clusters (engine mysql or postgres), aws_rds_cluster should include at least two entries in availability_zones, and the DB cluster MultiAZ field should be true.

Common pitfalls

  • Aurora availability_zones is informational at creation

    Setting availability_zones on aws_rds_cluster tells AWS which AZs are eligible for instances, but it doesn't guarantee instances actually run there. Deploy at least two aws_rds_cluster_instance resources in separate AZs using the availability_zone argument on each instance to achieve true Multi-AZ.

  • Serverless v1 clusters may not support Multi-AZ the same way

    Aurora Serverless v1 (engine_mode = "serverless") doesn't use discrete cluster instances. Its Multi-AZ behavior is managed internally by AWS and may not surface as MultiAZ: true in the API response. Serverless v2 uses cluster instances with class db.serverless, but HA evidence and behavior should still be validated with Aurora-specific signals rather than assuming identical semantics to provisioned Multi-AZ DB clusters.

  • Terraform plan shows no drift when AZ removed externally

    Delete a cluster instance in a second AZ through the console or CLI, and Terraform will catch the missing aws_rds_cluster_instance on the next plan. But if that instance was never managed by Terraform in the first place, there's no drift to detect, and the cluster silently becomes single-AZ.

  • Cost doubles with Multi-AZ

    Each additional cluster instance in a new AZ incurs full compute charges. For Aurora, a db.r6g.xlarge writer plus one reader roughly doubles the hourly cost from approximately $0.58/hr to $1.16/hr in us-east-1. Budget for this before enabling Multi-AZ across all clusters.

Audit evidence

An auditor expects rds-cluster-multi-az-enabled Config rule results showing compliant status for all in-scope clusters. Supporting evidence includes aws rds describe-db-clusters output filtered to show AvailabilityZones with at least two entries, plus engine-appropriate HA indicators: MultiAZ: true for Multi-AZ DB clusters, and instance placement confirming failover-capable topology for Aurora. Console screenshots of the RDS cluster detail page work as supplementary proof when they show the relevant HA fields for that engine.

For continuous assurance, a compliance dashboard pulling from AWS Config or a CSPM tool should show historical evaluation results confirming Multi-AZ remained enabled throughout the audit period, with no gaps in compliance status.

Framework-specific interpretation

NIST Cybersecurity Framework v2.0: PR.IR (Infrastructure Resilience) and RC.RP (Recovery Planning) are both in scope. PR.IR expects components to remain available during failures, which multi-AZ deployment addresses directly. RC.RP is satisfied by automatic failover: recovery happens without a manual restore operation, which is what that planning requirement is really asking for.

Tool mappings

Use these identifiers to cross-reference this control across tools, reports, and evidence.

  • Compliance.tf Control: rds_db_cluster_multiple_az_enabled

  • AWS Config Managed Rule: RDS_CLUSTER_MULTI_AZ_ENABLED

  • Checkov Check: CKV_AWS_157

  • Powerpipe Control: aws_compliance.control.rds_db_cluster_multiple_az_enabled

  • Prowler Check: rds_cluster_multi_az

  • AWS Security Hub Control: RDS.15

Last reviewed: 2026-03-09