RDS instances should be deployed in a VPC
Running an RDS instance inside a VPC gives you control over network isolation through subnets, route tables, network ACLs, and security groups. Without VPC placement, the instance sits on a flat, shared network where you cannot restrict traffic at the network layer and cannot use private IP addressing to keep database endpoints off the public internet.
AWS retired EC2-Classic in August 2022, so most modern accounts default to VPC. However, older accounts migrated from Classic may still contain legacy instances, and auditors flag any instance that lacks an explicit DB subnet group association.
Retrofit consideration
Moving a non-VPC RDS instance into a VPC requires creating a snapshot, restoring it into a DB subnet group within the target VPC, and updating application connection strings. This causes downtime.
Implementation
Choose the approach that matches how you manage Terraform.
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"
db_subnet_group_name = "example-db-subnet-group"
}
What this control checks
In Terraform, the aws_db_instance resource needs db_subnet_group_name set to a valid aws_db_subnet_group resource name. That subnet group must reference subnets in at least two Availability Zones within a VPC. If db_subnet_group_name is omitted entirely, the instance may land in the default VPC's default subnet group (which still passes), but explicit assignment is the reliable pattern. An instance fails this control when it has no VPC association at all, meaning DBSubnetGroup is null in the DescribeDBInstances API response. To pass, define a separate aws_db_subnet_group resource with subnet_ids pointing to private subnets, then reference its name output in the aws_db_instance block.
Common pitfalls
Default VPC deletion breaks implicit placement
Omit
db_subnet_group_nameand you're implicitly relying on the default VPC. If that default VPC is later deleted, new RDS creates will fail with no obvious pointer back to this dependency. Always setdb_subnet_group_nameexplicitly.Single-AZ subnet groups cause deployment failures
An
aws_db_subnet_groupwith subnets in only one AZ will fail Multi-AZ deployments outright. Less obvious: AWS requires at least two AZs even for Single-AZ instances. Build subnet groups with subnets across a minimum of two AZs.Restoring snapshots outside VPC
When restoring an RDS snapshot via
snapshot_identifier,db_subnet_group_nameis still required. Skip it and the restore may land outside the intended VPC, in the default subnet group or worse.Inline security group IDs without VPC context
Setting
vpc_security_group_idswithoutdb_subnet_group_nameproduces confusing validation errors. The security groups must belong to the same VPC as the subnet group, so they're meaningless without one.
Audit evidence
An auditor expects AWS Config evaluation results confirming VPC placement, with all RDS instances showing a "COMPLIANT" status. Supporting evidence includes CLI output from aws rds describe-db-instances showing each instance's DBSubnetGroup.VpcId field populated with a valid VPC ID. Where Security Hub is in use, the finding history for this control shows continuous compliance over the audit period.
Framework-specific interpretation
Related controls
Tool mappings
Use these identifiers to cross-reference this control across tools, reports, and evidence.
Compliance.tf Control:
rds_db_instance_in_vpcPowerpipe Control:
aws_compliance.control.rds_db_instance_in_vpcProwler Check:
rds_instance_inside_vpcAWS Security Hub Control:
RDS.18
Last reviewed: 2026-03-09