Skip to content

RDS database clusters should use a custom administrator username

Default admin usernames are publicly documented and appear in every tutorial and quickstart guide. Attackers doing credential-stuffing or brute-force attacks try these usernames first, cutting the search space in half before they touch the password. A custom username forces an adversary to guess both credentials, which makes unauthorized access materially harder.

Custom usernames also make database audit logs more useful. A generic "admin" principal appearing across every cluster tells you nothing; a provisioning-specific username makes it easier to trace activity back to a specific workflow or team.

Retrofit consideration

Changing the master username on an existing RDS cluster requires recreating the cluster. Terraform will plan a destroy-and-create cycle for aws_rds_cluster if master_username changes, causing downtime and potential data loss unless a snapshot restore strategy is in place.

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  = "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"
}

module "rds_aurora" {
  source  = "pcidssv321.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

This control evaluates the master_username argument on aws_rds_cluster resources. The value must not match known engine defaults: "admin" for Aurora MySQL and MySQL, or "postgres" for Aurora PostgreSQL and PostgreSQL. Any other non-empty string passes. Set master_username to a custom value like "app_dba" or source it from a variable or Secrets Manager reference.

master_username is required in most configurations; snapshot restore and replication paths are the main exceptions. If the resolved value matches a known default via either path, the control fails. Enabling manage_master_user_password for Secrets Manager rotation does not remove the requirement to supply a non-default master_username up front.

Common pitfalls

  • Retrofit forces cluster replacement

    Take a final snapshot with final_snapshot_identifier and schedule a maintenance window before changing master_username. The attribute is ForceNew in Terraform, so any change triggers a destroy-and-create cycle. There is no in-place rename.

  • Engine-specific defaults vary

    Aurora MySQL defaults to "admin", Aurora PostgreSQL defaults to "postgres", and Oracle defaults to "admin", but defaults can shift across engine versions and creation paths. A username that's non-default for one engine may be the default for another. Standardize on an organization-wide custom username pattern so this doesn't become a per-engine judgment call.

  • Restored clusters inherit original username

    When restoring from a snapshot via snapshot_identifier, the master username comes from the snapshot, not from your Terraform config. If the original cluster used a default username, the restore inherits it and fails this control. You cannot change the username during a snapshot restore, so the fix has to happen before the original cluster is snapshotted.

  • Global clusters propagate the primary username

    Secondary clusters in an aws_rds_global_cluster setup inherit their master username from the source cluster or snapshot, not from an independent Terraform argument. If the primary was provisioned with a default username, all secondaries carry it. Fix the primary first; secondaries cannot be changed in-place.

Audit evidence

Config rule evaluation results showing each RDS DB cluster marked COMPLIANT are the primary evidence. Supplement with aws rds describe-db-clusters output showing the MasterUsername field for every in-scope cluster, with no instances of "admin" or "postgres". RDS console screenshots of the cluster configuration tab are acceptable supporting documentation.

For continuous assurance, historical evaluation results from Security Hub or a comparable compliance dashboard covering the full audit period show the control held throughout, not just at snapshot time.

Framework-specific interpretation

NIST Cybersecurity Framework v1.1: PR.AC calls for unique identification and authentication of users, processes, and devices. Defaulting to a well-known username like 'admin' works directly against that goal. A custom username is one part of meeting this expectation for privileged database accounts.

PCI DSS v3.2.1: Requirement 2.1 requires changing all vendor-supplied defaults before deploying any system on the network. Default database admin usernames are vendor defaults, full stop. This control verifies that requirement is met for every RDS cluster.

Tool mappings

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

  • Compliance.tf Control: rds_db_cluster_no_default_admin_name

  • AWS Config Managed Rule: RDS_CLUSTER_DEFAULT_ADMIN_CHECK

  • Powerpipe Control: aws_compliance.control.rds_db_cluster_no_default_admin_name

  • Prowler Checks: rds_cluster_default_admin, rds_instance_default_admin

  • AWS Security Hub Controls: RDS.24, RDS.25

Last reviewed: 2026-03-09