Skip to main content

Overview

The AWS VPC Terraform module provides a hierarchical tagging system that allows you to apply tags at multiple levels:
  1. Global tags - Applied to all resources created by the module
  2. Subnet-specific tags - Additional tags for specific subnet types
  3. Automatic name tags - Generated automatically based on resource type and AZ
This approach enables consistent tagging across your infrastructure while allowing granular control for specific resources.

Tag Variables

Global Tags

tags
map(string)
A map of tags to add to all resources created by the module.Default: {}Defined in: variables.tf:93-96Applied to: VPC, subnets, route tables, NAT gateways, internet gateway, subnet groups

Subnet-Specific Tags

public_subnet_tags
map(string)
Additional tags for public subnets only.Default: {}Defined in: variables.tf:98-101Used in: main.tf:108
private_subnet_tags
map(string)
Additional tags for private subnets only.Default: {}Defined in: variables.tf:103-106Used in: main.tf:59
database_subnet_tags
map(string)
Additional tags for database subnets only.Default: {}Defined in: variables.tf:108-111Used in: main.tf:69
elasticache_subnet_tags
map(string)
Additional tags for ElastiCache subnets only.Default: {}Defined in: variables.tf:113-116Used in: main.tf:89

How Tag Merging Works

Tags are merged hierarchically using Terraform’s merge() function. From main.tf:7, here’s how VPC tags are constructed:
tags = "${merge(var.tags, map(\"Name\", format(\"%s\", var.name)))}"
For subnets, the pattern adds subnet-specific tags (main.tf:59):
tags = "${merge(var.tags, var.private_subnet_tags, map(\"Name\", format(\"%s-subnet-private-%s\", var.name, element(var.azs, count.index))))}"

Tag Merge Order

  1. Base: Global tags from var.tags
  2. Layer: Subnet-specific tags (if applicable)
  3. Override: Automatic Name tag
Later tags in the merge chain override earlier tags with the same key. The automatically generated Name tag will always override any Name tag you provide in tags or subnet-specific tag variables.

Automatic Name Tags

The module automatically generates descriptive Name tags for all resources:

VPC Name

Name = "{var.name}"
Example: "production-vpc"

Subnet Names

# Public subnets (main.tf:108)
Name = "{var.name}-subnet-public-{az}"
Example: "production-vpc-subnet-public-us-east-1a"

# Private subnets (main.tf:59)
Name = "{var.name}-subnet-private-{az}"
Example: "production-vpc-subnet-private-us-east-1a"

# Database subnets (main.tf:69)
Name = "{var.name}-subnet-database-{az}"
Example: "production-vpc-subnet-database-us-east-1a"

# ElastiCache subnets (main.tf:89)
Name = "{var.name}-subnet-elasticache-{az}"
Example: "production-vpc-subnet-elasticache-us-east-1a"

Route Table Names

# Public route table (main.tf:24)
Name = "{var.name}-rt-public"
Example: "production-vpc-rt-public"

# Private route tables (main.tf:49)
Name = "{var.name}-rt-private-{az}"
Example: "production-vpc-rt-private-us-east-1a"

Other Resource Names

# Internet Gateway (main.tf:15)
Name = "{var.name}-igw"

# Database Subnet Group (main.tf:79)
Name = "{var.name}-database-subnet-group"

Tagging Examples

Simple Global Tags

module "vpc" {
  source = "github.com/terraform-community-modules/tf_aws_vpc"

  name = "my-vpc"
  cidr = "10.0.0.0/16"
  
  azs             = ["us-east-1a", "us-east-1b"]
  private_subnets = ["10.0.1.0/24", "10.0.2.0/24"]
  public_subnets  = ["10.0.101.0/24", "10.0.102.0/24"]
  
  # Global tags applied to all resources
  tags = {
    Terraform   = "true"
    Environment = "production"
    Owner       = "platform-team"
  }
}
Result: All resources get these tags:
  • Terraform = "true"
  • Environment = "production"
  • Owner = "platform-team"
  • Name = "my-vpc-{resource-type}-{az}"

Common Tagging Strategies

Environment-Based Tagging

variable "environment" {
  default = "production"
}

module "vpc" {
  source = "github.com/terraform-community-modules/tf_aws_vpc"

  name = "${var.environment}-vpc"
  cidr = "10.0.0.0/16"
  
  # ... subnet configuration ...
  
  tags = {
    Environment = var.environment
    Terraform   = "true"
    ManagedBy   = "DevOps Team"
  }
}

Project-Based Tagging

locals {
  common_tags = {
    Project     = "customer-portal"
    Owner       = "platform-team"
    Terraform   = "true"
    Repository  = "github.com/myorg/infrastructure"
  }
}

module "vpc" {
  source = "github.com/terraform-community-modules/tf_aws_vpc"

  name = "customer-portal-vpc"
  cidr = "10.0.0.0/16"
  
  # ... subnet configuration ...
  
  tags = local.common_tags
  
  public_subnet_tags = merge(local.common_tags, {
    Tier = "Public"
  })
}

Multi-Account Tagging

data "aws_caller_identity" "current" {}

module "vpc" {
  source = "github.com/terraform-community-modules/tf_aws_vpc"

  name = "shared-services-vpc"
  cidr = "10.0.0.0/16"
  
  # ... subnet configuration ...
  
  tags = {
    Account     = data.aws_caller_identity.current.account_id
    AccountType = "shared-services"
    Owner       = "platform-team"
    Terraform   = "true"
  }
}

Tag Inheritance Patterns

Understanding how tags flow through resources:
var.tags (Global)
    |
    ├──> VPC
    ├──> Internet Gateway
    ├──> NAT Gateways
    ├──> Route Tables
    ├──> Subnet Groups
    |
    └──> Subnets
            |
            ├──> Public Subnets
            |       └──> merge(var.tags, var.public_subnet_tags, Name)
            |
            ├──> Private Subnets
            |       └──> merge(var.tags, var.private_subnet_tags, Name)
            |
            ├──> Database Subnets
            |       └──> merge(var.tags, var.database_subnet_tags, Name)
            |
            └──> ElastiCache Subnets
                    └──> merge(var.tags, var.elasticache_subnet_tags, Name)

Best Practices

Recommended Tag Keys:
  • Environment - production, staging, development
  • Terraform - “true” to identify managed resources
  • Owner - team or individual responsible
  • CostCenter - for billing allocation
  • Project - project or application name
  • ManagedBy - management tool or team
Tagging Mistakes to Avoid:
  • Using inconsistent tag key capitalization across resources
  • Not using tags for cost allocation
  • Hardcoding environment-specific values instead of variables
  • Forgetting to tag subnet groups (they inherit from tags)
  • Using spaces in tag keys (use PascalCase or kebab-case)

AWS Reserved Tags

AWS-Reserved Tag Prefixes:Tags starting with aws: are reserved by AWS and cannot be set:
  • aws:createdBy
  • aws:cloudformation:*
Tags for specific services:
  • kubernetes.io/* - Kubernetes/EKS
  • elasticbeanstalk:* - Elastic Beanstalk
  • ecs:* - ECS

Tag Limits

  • Maximum tags per resource: 50
  • Maximum tag key length: 128 characters
  • Maximum tag value length: 256 characters
  • Allowed characters: a-z, A-Z, 0-9, +, -, =, ., _, :, /, @

Output Tag Information

To reference tags in other modules:
module "vpc" {
  source = "github.com/terraform-community-modules/tf_aws_vpc"
  
  # ... configuration ...
  
  tags = {
    Environment = "production"
  }
}

# Reference VPC tags in another module
resource "aws_security_group" "app" {
  vpc_id = module.vpc.vpc_id
  
  tags = merge(
    module.vpc.tags,  # Note: Module doesn't output tags
    {
      Name = "app-sg"
    }
  )
}
The module doesn’t expose a tags output. If you need to reuse tags, store them in local variables and pass them to both the module and other resources.

Tag-Based Resource Filtering

Use tags to filter resources in AWS CLI or other modules:
# List all subnets in production VPC
aws ec2 describe-subnets \
  --filters "Name=tag:Environment,Values=production" \
            "Name=tag:Tier,Values=Private"

# Get all NAT gateways for a project
aws ec2 describe-nat-gateways \
  --filter "Name=tag:Project,Values=customer-portal"

Variables

Complete list of all configuration variables

Subnet Types

Learn about different subnet types and their tags

Build docs developers (and LLMs) love