Terraform Data Sources for AWS Subnets in Practice

Working with AWS subnets in Terraform is a recurring pattern that tends to surface early in any AWS configuration. The initial approach many teams adopt is to input a list of Subnet IDs and CIDRs as variables. While this works, it becomes clunky and messy when the network is complicated. The core problem is manual maintenance. When subnets already exist in the environment, pasting their identifiers into code defeats the purpose of Terraform doing this stuff dynamically.

The more sustainable philosophy is to treat subnets as discoverable resources. Terraform provides built-in Data Sources which can be used to look up VPC and subnet data and then filter using Tags. The essential prerequisite is that subnets are suitably tagged. With tags in place, configurations can query existing infrastructure instead of hard coding identifiers.

Data Sources and Tag-Based Discovery

The foundation of working with subnet information is using Terraform Data Sources, AWS Tags and some basic filtering. The idea is that subnets already exist as resources in the environment and you should not have to waste time looking up their IDs or CIDRs manually just to paste them into code.

A common pattern is to use the aws_subnets data source to find existing subnets. The data source can be filtered by VPC ID or other attributes. Once discovered, the identifiers can be used for further configuration.

The code used in these examples can be found in Github here.

Tagging Subnets Created Outside Terraform

A specific operational need is to add tags to AWS subnets that were created and are managed outside of the current Terraform configuration. This guide demonstrates how to use Terraform to add tags to those subnets.

The procedure uses the aws_subnets data source to find existing subnets. You can filter by VPC ID or other attributes.

hcl data "aws_subnets" "example" { filter { name = "vpc-id" values = ["vpc-12345678"] } }

After discovery, a tag can be applied to each subnet found by the data source using aws_ec2_tag with for_each.

hcl resource "aws_ec2_tag" "example" { for_each = toset(data.aws_subnets.example.ids) resource_id = each.value key = "example_key" value = "example_value" }

Additional information notes that if you use default_tags in the AWS provider configuration, it may conflict with this aws_ec2_tag resource when managing the same tags. Ensure your tagging strategy is consistent to avoid potential conflicts.

Remote State and Cross-Configuration Subnet Consumption

Data sources let you dynamically fetch data from APIs or other Terraform state backends. Examples of data sources include machine image IDs from a cloud provider or Terraform outputs from other configurations. Data sources make your configuration more flexible and dynamic and let you reference values from other configurations, helping you scope your configuration while still referencing any dependent resource attributes. In HCP Terraform, data sources let you share data between workspaces.

In the tutorial workflow, you will use data sources to make your configuration more dynamic. First, you will use Terraform to create an AWS VPC and security groups. Next, you will use the aws_availability_zones data source to make your configuration deployable across any region. You will then deploy application infrastructure defined by a separate Terraform configuration, and use the terraform_remote_state data source to query information about your VPC. Finally, you will use the aws_ami data source to configure the correct AMI for the current region.

You can complete this tutorial using the same workflow with either Terraform Community Edition or HCP Terraform. HCP Terraform is a platform that can be used to manage and execute your Terraform projects. It includes features like remote state and execution, structured plan output, workspace resource summaries, and more.

This tutorial assumes you are familiar with the Terraform and HCP Terraform workflows.

Provisioning Multiple Instances Per Subnet

The configuration in main.tf only uses a single EC2 instance. Update the configuration to use the instances_per_subnet variable to provision multiple EC2 instances per subnet.

hcl resource "aws_instance" "app" { count = var.instances_per_subnet * length(data.terraform_remote_state.vpc.outputs.private_subnet_ids) ami = "ami-04d29b6f966df1537" }

Now when you apply this configuration, Terraform will provision var.instances_per_subnet instances for each private subnet configured in your VPC workspace.

The AWS instance configuration also uses a hard-coded AMI ID, which is only valid for the us-east-1 region. Use an aws_ami data source to load the correct AMI ID for the current region.

Region Discovery and Outputs

Add a data source to main.tf to access region information.

hcl data "aws_region" "current" { }

Add an output for the region to outputs.tf.

hcl output "aws_region" { description = "AWS region" value = data.aws_region.current.name }

Apply this configuration, setting the value of aws_region to us-west-1.

bash $ terraform apply -var aws_region=us-west-1

Plan: 34 to add, 0 to change, 0 to destroy.

Do you want to perform these actions in workspace "learn-terraform-data-sources-vpc"?
Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.
Enter a value: yes

Apply complete! Resources: 34 added, 0 changed, 0 destroyed.
Outputs:
appsecuritygroupids = [
"sg-00fc397fb1066b140",
]
aws
region = "us-west-1"
lbsecuritygroupids = [
"sg-0ab0e3a1416bac068",
]
private
subnetids = [
"subnet-0e9855907f0bab6f4",
"subnet-074a96820b50023bc",
]
public
subnet_ids = [
"subnet-0303938fcbcdb0d16",
"subnet-012e5c8724dfa5a0e",
]

Now that you deployed your network resources, go to the learn-terraform-data-sources-app directory.

bash $ cd ../learn-terraform-data-sources-app

This directory contains the Terraform configuration for your application.

Initialize your configuration.

In the workspace's Settings > General page, find the Remote state sharing section.

  • Select the Share with specific workspaces option and choose the learn-terraform-data-sources-app workspace.
  • Click the Save settings button.

Now, update your aws provider configuration in main.tf to use the same region as the VPC configuration instead of a hardcoded region.

hcl provider "aws" { region = data.terraform_remote_state.vpc.outputs.aws_region }

The VPC configuration also included outputs for subnet and security group IDs. Configure the load balancer security group and subnet arguments for the elb module with those values.

hcl module "elb_http" { security_groups = data.terraform_remote_state.vpc.outputs.lb_security_group_ids subnets = data.terraform_remote_state.vpc.outputs.public_subnet_ids }

You can use values from data sources just like any other Terraform values, including by passing them to functions. The configuration in main.tf only uses a single EC2 instance.

Availability Zones and Dynamic VPC Configuration

Add the following to main.tf.

hcl data "aws_availability_zones" "available" { state = "available" filter { name = "zone-type" values = ["availability-zone"] } }

The aws_availability_zones data source is part of the AWS provider and retrieves a list of availability zones based on the arguments supplied. In this case, the state argument limits the availability zones to only those that are currently available.

You can reference data source attributes with the pattern data.<NAME>.<ATTRIBUTE>. Update the VPC configuration to use this data source to set the list of availability zones.

hcl module "vpc" { source = "terraform-aws-modules/vpc/aws" version = "3.14.0" cidr = var.vpc_cidr_block azs = data.aws_availability_zones.available.names private_subnets = slice(var.private_subnet_cidr_blocks, 0, 2) public_subnets = slice(var.public_subnet_cidr_blocks, 0, 2) }

Configure the VPC workspace to output the region, which the application workspace will require as an input.

Data Source Reference Table

The following table summarizes the data sources used in the referenced workflows.

Data Source Purpose Key Attribute
aws_subnets Discover existing subnets in a VPC ids
awsec2tag Apply tags to discovered subnets resource_id
terraformremotestate Query outputs from another workspace outputs
aws_region Retrieve current region name name
aws_ami Load correct AMI ID for current region id
awsavailabilityzones List available AZs for deployment names

Practical Patterns for Subnet Handling

Using data sources for subnets removes the need to maintain static lists of IDs. The workflow becomes:

  • Ensure subnets are tagged consistently in AWS.
  • Use aws_subnets with filters such as vpc-id to discover them.
  • Reference data.aws_subnets.example.ids for downstream resources.
  • When tagging is required for resources outside Terraform, use aws_ec2_tag with for_each = toset(data.aws_subnets.example.ids).

When configurations are split across workspaces, terraform_remote_state provides a clean interface. The VPC workspace outputs aws_region, private_subnet_ids, public_subnet_ids, app_security_group_ids, and lb_security_group_ids. The application workspace consumes those outputs for provider region, ELB module arguments, and instance count calculations.

Dynamic AMI selection avoids hard-coded IDs like ami-04d29b6f966df1537 which is only valid for us-east-1. The aws_ami data source loads the correct image per region.

Availability zone selection is made dynamic via aws_availability_zones with state = "available" and a filter for zone-type = "availability-zone". The resulting data.aws_availability_zones.available.names feeds the VPC module azs argument.

Conclusion

Working with AWS subnets in Terraform is most effective when discovery and tagging replace manual variable lists. Data sources provide the mechanism to query existing infrastructure, share state between workspaces, and adapt configurations to the current region and availability zones. The aws_subnets data source combined with tag-based filtering allows configurations to find subnets created outside the current Terraform state. The aws_ec2_tag resource with for_each enables retroactive tagging without importing resources. Remote state sharing via terraform_remote_state lets application configurations reference VPC outputs for subnet IDs, security group IDs, and region, keeping network and application concerns separate. Using aws_availability_zones and aws_ami data sources ensures deployments remain portable across regions without hard-coded values. Together these patterns produce configurations that are dynamic, reusable, and resilient to environment changes.

Sources

  1. Terraform Tricks - Working With AWS Subnets
  2. Tagging AWS subnets created outside of Terraform
  3. Terraform data sources

Related Posts