RDS database instances should use a custom administrator username
Default administrator usernames are documented in AWS guides, vendor manuals, and attack toolkits. Using them gives attackers half of the credential pair for free, reducing a brute-force attack to password guessing alone. Automated scanners specifically probe for "admin" and "postgres" against publicly or VPC-exposed endpoints.
A custom username adds obscurity that, combined with strong passwords and secrets rotation, meaningfully raises the cost of unauthorized access. It is a low-effort change at provisioning time but painful to retrofit on production databases that already have application connection strings baked in.
Retrofit consideration
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.
This control is enforced automatically with Compliance.tf modules. Start free trial
If you use terraform-aws-modules/rds/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" {
source = "terraform-aws-modules/rds/aws"
version = ">=7.0.0"
allocated_storage = 20
db_name = "myapp"
db_subnet_group_name = "example-db-subnet-group"
engine = "mysql"
engine_version = "8.0.41"
family = "mysql8.0"
identifier = "abc123"
instance_class = "db.t3.micro"
major_engine_version = "8.0"
password_wo = "change-me-in-production"
skip_final_snapshot = true
username = "dbadmin"
vpc_security_group_ids = ["sg-12345678"]
}Use AWS provider resources directly. See docs for the resources involved: aws_db_instance.
resource "aws_db_instance" "this" {
allocated_storage = 20
enabled_cloudwatch_logs_exports = ["general", "slowquery"]
engine = "mysql"
identifier = "pofix-abc123"
instance_class = "db.t3.micro"
monitoring_interval = 60
monitoring_role_arn = "arn:aws:iam::123456789012:role/example-role"
password = "ChangeMe123!"
skip_final_snapshot = true
username = "dbadmin"
}What this control checks
For aws_db_instance resources, username must not be a known engine default. Failing values include "admin" (MySQL, MariaDB, Oracle, and some SQL Server configurations) and "postgres" (PostgreSQL). The list of disallowed values is engine-specific, but any non-default value passes. For Aurora clusters (aws_rds_cluster), the same logic applies to master_username. Set username or master_username to a custom string such as "app_dba", or source the value from a variable or Secrets Manager reference. There is no boolean flag to toggle; the control evaluates the literal value of the username attribute against known defaults.
Common pitfalls
Engine-specific default lists vary
The set of disallowed usernames differs by engine. "postgres" fails only for PostgreSQL instances, while "admin" fails for MySQL and MariaDB. If you standardize on a single custom username across engines, verify it does not collide with any engine's reserved words by checking engine documentation.
Terraform replace on username change
username on aws_db_instance is a force-new attribute. Changing it on an existing resource triggers a destroy-and-recreate cycle, which means production data loss if you are not careful. Set lifecycle { prevent_destroy = true } on production instances and plan a blue-green migration rather than an in-place change.
Aurora clusters need master_username too
With aws_rds_cluster and aws_rds_cluster_instance, the master username is set on the cluster resource via master_username, not on the instance. Applying a custom username only at the instance level has no effect because Aurora instances inherit credentials from the cluster.
Hardcoded usernames in Terraform state
Even custom usernames are stored in plaintext in Terraform state. The username itself can be generated with random_string or sourced from an external process, then passed in via a variable marked sensitive = true to limit exposure in logs and plan output. Secrets Manager can store the final value, but username generation must be handled separately before that reference is possible.
Audit evidence
Compliance evaluation output from a custom Config rule or equivalent policy-as-code control, showing all RDS instances as COMPLIANT, is the primary artifact. Supporting evidence includes AWS CLI output from aws rds describe-db-instances showing the MasterUsername field for each instance, confirming none use default values like "admin" or "postgres".
For continuous compliance, an exported report across all accounts and regions is strongest. Screenshots of the RDS console showing the master username column are acceptable for point-in-time audits.
Framework-specific interpretation
NIST Cybersecurity Framework v1.1: PR.AC-1 covers identity and credential management for authorized users and devices. A default master username is a publicly known credential, not a managed one, which undermines the subcategory's intent. Replacing it with a custom value is one of the lowest-effort ways to satisfy the credential management expectation here.
PCI DSS v3.2.1: Requirement 2.1 is unambiguous: change all vendor-supplied defaults before putting a system on the network. An RDS instance provisioned with "admin" or "postgres" as the master username fails this requirement on first inspection.
Tool mappings
Use these identifiers to cross-reference this control across tools, reports, and evidence.
- Compliance.tf Control:
rds_db_instance_no_default_admin_name - AWS Config Managed Rule:
RDS_INSTANCE_DEFAULT_ADMIN_CHECK - Powerpipe Control:
aws_compliance.control.rds_db_instance_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