Search code examples
amazon-web-servicesterraformamazon-rdsterraform-provider-awshigh-availability

How to make a single-AZ (non-HA) RDS instance with terraform?


In this AWS Database Blog, they assert that

You can set up Amazon RDS in a Single-AZ database (DB) instance or a Multi-AZ DB instance for high availability requirements

and that you can

...modify existing Single-AZ instances to become Multi-AZ deployments.

Furthermore,

...you can create a Multi-AZ read replica, synchronize it with your Single-AZ DB instance, and then promote it as your primary DB instance to minimize latencies during conversion.

Additionally, in v1.32 of the official AWS VPC Module there are multiple references to the usage of single_nat_gateway, particularly

If single_nat_gateway = true, then all private subnets will route their Internet traffic through this single NAT gateway.

and in the official RDS module, multi_az is shown as defaulting to false (link).

Despite this, I am getting the following error

╷
│ Error: DBSubnetGroupDoesNotCoverEnoughAZs: The DB subnet group doesn't meet Availability Zone (AZ) coverage requirement. Current AZ coverage: us-west-2a. Add subnets to cover at least 2 AZs.
│   status code: 400, request id: *****
│ 
│   with module.rds.module.db_subnet_group.aws_db_subnet_group.this[0],
│   on .terraform/modules/rds/modules/db_subnet_group/main.tf line 8, in resource "aws_db_subnet_group" "this":
│    8: resource "aws_db_subnet_group" "this" {

when attempting to terraform apply this main.tf configuration:

module "rds" {
  source                                = "terraform-aws-modules/rds/aws"
  version                               = "~> 3.4.0"
  identifier                            = "${var.env}-${var.user}-${local.db_name}"
  engine                                = var.postgres.engine
  engine_version                        = var.postgres.engine_version
  family                                = var.postgres.family
  major_engine_version                  = var.postgres.major_engine_version
  instance_class                        = var.postgres.instance_class
  allocated_storage                     = var.postgres.allocated_storage
  max_allocated_storage                 = var.postgres.max_allocated_storage
  storage_encrypted                     = var.postgres.storage_encrypted
  password                              = random_password.password.result
  port                                  = var.postgres.port
  multi_az                              = false
  subnet_ids                            = [data.aws_subnet.priv1.id]
  vpc_security_group_ids                = [module.db_security_group.security_group_id]
  maintenance_window                    = var.postgres.maintenance_window
  backup_window                         = var.postgres.backup_window
  enabled_cloudwatch_logs_exports       = var.postgres.enabled_cloudwatch_logs_exports
  backup_retention_period               = var.postgres.backup_retention_period
  skip_final_snapshot                   = var.postgres.skip_final_snapshot
  deletion_protection                   = var.postgres.deletion_protection
  performance_insights_enabled          = var.postgres.performance_insights_enabled
  performance_insights_retention_period = var.postgres.performance_insights_retention_period
  create_monitoring_role                = var.postgres.create_monitoring_role
  monitoring_role_name                  = "${var.env}-${var.user}-${var.postgres.monitoring_role_name}"
  monitoring_interval                   = var.postgres.monitoring_interval
  snapshot_identifier                   = var.postgres.snapshot_identifier
  iam_database_authentication_enabled   = var.postgres.iam_auth
  apply_immediately                     = true
  tags = {
    Name        = "${var.env}-${var.user}-rds"
    Terraform   = "true"
    Environment = var.env
    Created     = timestamp()
  }
}

with this postgres variable defined in my terraform.tfvars:

postgres = {
  db_name = "postgres-db"
  # All available versions: https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/CHAP_PostgreSQL.html#PostgreSQL.Concepts
  engine                                = "postgres"
  engine_version                        = "11.12"
  family                                = "postgres11" # DB parameter group
  major_engine_version                  = "11"         # DB option group
  instance_class                        = "db.t2.micro"
  allocated_storage                     = 100
  max_allocated_storage                 = 200
  storage_encrypted                     = false
  port                                  = 5432
  multi_az                              = false
  maintenance_window                    = "Mon:00:00-Mon:03:00"
  backup_window                         = "03:00-06:00"
  enabled_cloudwatch_logs_exports       = ["postgresql", "upgrade"]
  backup_retention_period               = 0
  skip_final_snapshot                   = true
  deletion_protection                   = false
  performance_insights_enabled          = false
  performance_insights_retention_period = 7
  create_monitoring_role                = true
  monitoring_role_name                  = "monitoring_role"
  monitoring_interval                   = 60
  snapshot_identifier                   = "arn:aws:rds:us-west-2:999999999999:snapshot:rds-ss"
  iam_auth                              = true
}

Similar questions on SO seem to all have answers with the theme that you must provide multiple availability zones, which implies at least two subnets, which - if you are creating them manually as private subnets - would then each need their own NAT gateway. That seems unnecessarily expensive and constraining, especially for development and test environments.

How can I deploy a single-az RDS Postgres instance with these components?


Solution

  • A DB subnet group has to have multiple subnets. That's a requirement of RDS that you can't bypass. Even if you are only deploying a single instance, if that entire availability zone were to go down, Amazon RDS would automatically spin up a new instance in one of the other availability zones you have specified. That's one of the managed database services that you get automatically with Amazon RDS.

    So even if you are deploying a single-az instance, you have to specify multiple availability zones in the DB subnet group.