Database subnet groups are a core networking primitive in AWS for placing managed database instances inside a Virtual Private Cloud. Terraform codifies this primitive through the aws_db_subnet_group resource for RDS and analogous resources for DocumentDB, Neptune, ElastiCache and other services. Correct subnet group definition determines placement across Availability Zones, controls Multi-AZ high availability, and gates connectivity through security groups and private routing.
This article covers the resource definition, arguments and attributes, data source usage with Jsonnet tooling, integration with RDS instances, and patterns for other AWS database services. All specifics are drawn from the reference implementations.
What a Database Subnet Group Represents
A database subnet group is a collection of subnets, typically private, that you designate for your database instances. When you create a database instance, AWS uses the subnet group to determine which subnets and availability zones the instance can be launched in. For Multi-AZ deployments, the primary and standby instances are placed in different availability zones within the subnet group.
Database subnet groups define which subnets your database instances can be placed in within your VPC. They are a fundamental networking requirement for many AWS managed database services that run inside a VPC. RDS DB subnet groups generally require subnets in at least two availability zones, and subnet groups that span multiple availability zones enable high availability features like Multi-AZ deployments.
Each AWS database service has its own subnet group resource type.
Resource Definition for RDS
aws_db_subnet_group provides an RDS DB subnet group resource.
Example Usage
resource "aws_db_subnet_group" "default" {
name = "main"
subnet_ids = ["${aws_subnet.frontend.id}", "${aws_subnet.backend.id}"]
tags {
Name = "My DB subnet group"
}
}
Argument Reference
The following arguments are supported:
name- Optional, Forces new resource. The name of the DB subnet group. If omitted, Terraform will assign a random, unique name.name_prefix- Optional, Forces new resource. Creates a unique name beginning with the specified prefix. Conflicts withname.description- Optional. The description of the DB subnet group. Defaults to "Managed by Terraform".subnet_ids- Required. A list of VPC subnet IDs.tags- Optional. A mapping of tags to assign to the resource.
Attributes Reference
In addition to all arguments above, the following attributes are exported.
Import
DB Subnet groups can be imported using the name, e.g.
$ terraform import aws_db_subnet_group.default production-subnet-group
Data Source Representation with Jsonnet
The db_subnet_group data source represents the aws_db_subnet_group Terraform data source.
This package contains functions and utilities for setting up the data source using Jsonnet code.
new()
aws.data.db_subnet_group.new
injects a new data_aws_db_subnet_group Terraform data source block into the root module document. Additionally, this inserts a private function into the _ref attribute that generates references to attributes of the resource. For example, if you added a new instance to the root using:
```
arguments omitted for brevity
aws.data.dbsubnetgroup.new('some_id')
```
You can get the reference to the id field of the created aws.data.db_subnet_group using the reference:
$._ref.data_aws_db_subnet_group.some_id.get('id')
This is the same as directly entering "${ data_aws_db_subnet_group.some_id.id }" as the value.
NOTE: if you are chaining multiple resources together in a merge operation, you may not be able to use super, self, or $ to refer to the root object.
VPC and Subnet Foundation for RDS
The tutorial configuration begins with a VPC module.
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "6.6.0"
name = "education"
cidr = "10.0.0.0/16"
azs = data.aws_availability_zones.available.names
public_subnets = ["10.0.4.0/24", "10.0.5.0/24", "10.0.6.0/24"]
enable_dns_hostnames = true
enable_dns_support = true
}
The next resource is an aws_db_subnet_group, which designates a collection of subnets that your RDS instance can be provisioned in. This subnet group uses the subnets created by the VPC module.
resource "aws_db_subnet_group" "education" {
name = "education"
subnet_ids = module.vpc.public_subnets
tags = {
Name = "Education"
}
}
This subnet group resource is an optional parameter in your aws_db_instance block below. Without it, Terraform creates your RDS instances in the default VPC.
Linking Subnet Group to an RDS Instance
Review the aws_db_instance configuration.
resource "aws_db_instance" "education" {
identifier = "education"
instance_class = "db.t3.micro"
allocated_storage = 5
engine = "postgres"
engine_version = "18.3"
username = "edu"
password = var.db_password
db_subnet_group_name = aws_db_subnet_group.education.name
vpc_security_group_ids = [aws_security_group.rds.id]
parameter_group_name = aws_db_parameter_group.education.name
publicly_accessible = true
skip_final_snapshot = true
}
Note the following arguments.
username and password: The credentials for the root user.
publicly_accessible: Set to true for this tutorial's configuration.
If you are new to Terraform, complete the Get Started tutorials first.
For this tutorial, you will need:
- Clone the sample repository for this tutorial, which contains Terraform configuration for an RDS instance, parameter group, security group, and subnet group.
$ git clone https://github.com/hashicorp-education/learn-terraform-rds - Change into the repository directory.
$ cd learn-terraform-rds - Open the
main.tffile in your editor to review the sample configuration.
The first resources defined are the VPC and subnets, using the terraform-aws-vpc module.
Cross Service Subnet Group Patterns
The same conceptual model repeats across AWS database services with service-specific resource names.
Creating a DocumentDB Subnet Group
```
Subnet group for DocumentDB
resource "awsdocdbsubnetgroup" "docdb" {
name = "docdb-subnet-group"
description = "Subnet group for DocumentDB clusters"
subnetids = local.privatesubnetids
tags = {
Name = "docdb-subnet-group"
Environment = "production"
ManagedBy = "terraform"
}
}
```
```
Use it in a DocumentDB cluster
resource "awsdocdbcluster" "docdb" {
clusteridentifier = "app-docdb"
engine = "docdb"
masterusername = "docdbadmin"
masterpassword = var.dbpassword
dbsubnetgroupname = awsdocdbsubnetgroup.docdb.name
vpcsecuritygroupids = [awssecuritygroup.docdbsg.id]
storageencrypted = true
skipfinal_snapshot = true
tags = {
Environment = "production"
}
}
```
Creating a Neptune Subnet Group
```
Subnet group for Neptune
resource "awsneptunesubnetgroup" "neptune" {
name = "neptune-subnet-group"
description = "Subnet group for Neptune graph database"
subnetids = local.privatesubnetids
tags = {
Name = "neptune-subnet-group"
Environment = "production"
ManagedBy = "terraform"
}
}
```
```
Use it in a Neptune cluster
resource "awsneptunecluster" "neptune" {
clusteridentifier = "app-neptune"
engine = "neptune"
neptunesubnetgroupname = awsneptunesubnetgroup.neptune.name
vpcsecuritygroupids = [awssecuritygroup.neptunesg.id]
storageencrypted = true
skipfinalsnapshot = true
tags =
```
Creating an ElastiCache Subnet Group
ElastiCache uses its own subnet group resource:
```
Subnet group for ElastiCache
resource "awselasticachesubnetgroup" "redis" {
name = "redis-subnet-group"
description = "Subnet group for ElastiCache Redis"
subnetids = local.privatesubnetids
tags = {
Name = "redis-subnet-group"
Environment = "production"
ManagedBy = "terraform"
}
}
```
```
Use it in a Redis replication group
resource "awselasticachereplicationgroup" "redis" {
replicationgroupid = "app-redis"
description = "Application Redis cluster"
nodetype = "cache.r6g.large"
numcacheclusters = 3
automaticfailoverenabled = true
subnetgroupname = awselasticachesubnetgroup.redis.name
securitygroupids = [awssecuritygroup.redissg.id]
engine = "redis"
engineversion = "7.0"
atrestencryptionenabled = true
tags = {
Environment = "production"
}
}
```
Aurora cluster example reuses the same RDS subnet group.
resource "aws_rds_cluster" "aurora" {
cluster_identifier = "app-aurora"
engine = "aurora-postgresql"
engine_version = "15.4"
database_name = "appdb"
master_username = "dbadmin"
master_password = var.db_password
db_subnet_group_name = aws_db_subnet_group.rds.name
vpc_security_group_ids = [aws_security_group.db_sg.id]
storage_encrypted = true
skip_final_snapshot = true
tags = {
Environment = "production"
}
}
Variable definition for sensitive password:
variable "db_password" {
type = string
sensitive = true
}
Specification Comparison
The following table summarizes the core arguments for the RDS DB subnet group resource.
| Argument | Required | Forces New Resource | Default / Notes |
|---|---|---|---|
| name | Optional | Yes | Random unique name if omitted |
| name_prefix | Optional | Yes | Conflicts with name |
| description | Optional | No | "Managed by Terraform" |
| subnet_ids | Required | No | List of VPC subnet IDs |
| tags | Optional | No | Mapping of tags |
Attributes exported by the resource include the identifiers and computed values alongside all input arguments.
Service-specific subnet group resources mirror this structure with names such as aws_docdb_subnet_group, aws_neptune_subnet_group, and aws_elasticache_subnet_group. Each service binds the subnet group via a dedicated attribute like db_subnet_group_name, neptune_subnet_group_name, or subnet_group_name.
Practical Notes
- Subnet selection matters for high availability. RDS DB subnet groups generally require subnets in at least two availability zones. Spanning multiple availability zones enables Multi-AZ deployments where primary and standby instances are placed in different AZs within the subnet group.
- Public subnets can be referenced in the tutorial example, but production designs typically use private subnets with NAT gateway egress.
- The
descriptionargument defaults to "Managed by Terraform" for RDS DB subnet groups. - Import is name-based:
terraform import aws_db_subnet_group.default production-subnet-group. - When using Jsonnet tooling, the
aws.data.db_subnet_group.newhelper injects a data source block and provides_refaccessors for attribute interpolation without manual string templating.
Conclusion
The aws_db_subnet_group resource is the control plane for database placement inside a VPC. By declaring name, subnet_ids, and optional description and tags, Terraform creates a named collection of subnets that RDS and Aurora instances can reference via db_subnet_group_name. The same pattern repeats across DocumentDB, Neptune, and ElastiCache with service-specific resource types.
In practice, design the subnet group to span at least two availability zones with private subnets, attach appropriate security groups, and reference the group in the database instance or cluster definition. For IaC automation beyond plain Terraform HCL, the Jsonnet data source helper provides programmatic injection of data_aws_db_subnet_group blocks with reference generation through the _ref attribute.
Consistent naming, tagging, and description conventions across services simplify cost allocation, compliance auditing, and operational troubleshooting. Importing existing subnet groups by name allows gradual migration of legacy resources under Terraform management without recreation.