2 minute read

Context

As AWS environments grow, hard-coding resource IDs becomes fragile and expensive to maintain.

Tagging provides a way to describe intent—environment, ownership, purpose—while Terraform data sources let you query that intent dynamically at plan time.

This post focuses on using Terraform data sources to filter AWS resources by tags and wire them into real infrastructure workflows.


Why Tag-Based Discovery Matters

Static references break when:

  • resources are recreated
  • environments multiply
  • ownership changes
  • infrastructure evolves independently

Tag-based discovery allows Terraform to:

  • locate existing infrastructure safely
  • adapt to changes without rewrites
  • reduce duplication across modules
  • encode environment awareness declaratively

Tags become an API for your infrastructure.


Terraform Data Sources vs Resources

A quick reminder:

  • Resources create or manage infrastructure
  • Data sources read existing infrastructure

Tag filtering is almost always a data source concern.

Using data sources keeps Terraform:

  • declarative
  • side-effect free
  • safe in shared environments

Example: Filtering a VPC by Tags

A common pattern is locating an existing VPC by environment tags.

data "aws_vpc" "selected" {
  filter {
    name   = "tag:Environment"
    values = ["production"]
  }
}

This allows downstream resources to reference the VPC dynamically:

vpc_id = data.aws_vpc.selected.id

No hard-coded IDs required.


Filtering Subnets by Tags

Subnets are frequently grouped by role (public, private, internal).

data "aws_subnets" "private" {
  filter {
    name   = "vpc-id"
    values = [data.aws_vpc.selected.id]
  }

  filter {
    name   = "tag:Tier"
    values = ["private"]
  }
}

This enables patterns like:

  • passing subnet IDs to modules
  • selecting AZ-aware resources
  • avoiding brittle lists

Working with Multiple Matches

Some data sources return lists, not single objects.

Be explicit about expectations:

  • use aws_subnets when multiple results are valid
  • use aws_subnet only when exactly one match is expected

Example:

subnet_ids = data.aws_subnets.private.ids

Assuming uniqueness when it isn’t guaranteed leads to surprises.


Tagging Conventions Matter

Terraform can only filter on what exists.

Effective tag-based discovery depends on:

  • consistent keys (Environment, Name, Tier)
  • predictable values
  • shared conventions across teams

Without discipline, tag filtering becomes unreliable.


Common Failure Modes

No Matches Found

Usually caused by:

  • mismatched tag keys
  • inconsistent casing
  • environment drift

Terraform errors here are a feature—they prevent unsafe assumptions.


Too Many Matches

Often indicates:

  • overly broad filters
  • missing constraints
  • unclear tagging intent

Refine filters instead of picking an arbitrary result.


Using Tag Filtering in Modules

Modules benefit greatly from tag-based inputs.

Example:

variable "environment" {
  type = string
}

data "aws_vpc" "env" {
  filter {
    name   = "tag:Environment"
    values = [var.environment]
  }
}

This makes modules:

  • reusable across environments
  • decoupled from concrete IDs
  • safer to evolve over time

When Not to Use Tag Filtering

Avoid tag-based discovery when:

  • the resource is created in the same module
  • strong ordering guarantees are required
  • ambiguity would be dangerous

Not everything needs to be dynamic.


Takeaways

  • Terraform data sources enable safe infrastructure discovery
  • Tags act as a contract between teams and tools
  • Filtering avoids brittle hard-coded references
  • Consistent tagging is non-negotiable
  • Dynamic discovery improves reuse and longevity

Used thoughtfully, tag filtering lets Terraform adapt to reality instead of fighting it.