Blog

Tagging AWS resources the right way using Terraform
Photo by Saifeddine Rajhi

Tagging AWS resources the right way using Terraform

6mins read
  • AWS
  • tagging
  • best practices
  • cost tracking
  • lifecycle management
  • Terraform

    Content

    Better organization and cost tracking with consistent tagging

    🔖 Introduction

    Keeping your AWS resources organized and tracking costs can be a challenge, especially as your infrastructure grows.

    Tagging resources is a simple yet great solution, but doing it effectively requires following best practices.

    In this blog post, we'll show you how to tag AWS resources using the Infrastructure as Code (IaC) tool Terraform.

    Introduction to Tagging AWS resources

    Tagging AWS resources is important for maintaining an organized and cost-effective cloud infrastructure. Tags are key-value pairs that allow you to categorize and manage resources based on criteria like environment, application, team, etc.

    Consistent tagging provides benefits like betterresource organization, cost allocation, automation, security, and lifecycle management.

    In Terraform, you can tag resources during provisioning. For example, to tag an S3 bucket with environment and team tags:

    ``hcl resource "aws_s3_bucket" "example" { bucket = "my-bucket" tags = { Environment = "Production" Team = "DevOps" } }

    
    You can also define default tags at the provider level, which apply to all resources:
    
    ```hcl
    provider "aws" {
      default_tags {
        tags = {
          Environment = "Production"
          ManagedBy = "Terraform"
        }
      }
    }

    Overriding Default Tags at resource level

    Defining default tags at the provider level using the default_tags block promotes consistency and reduces manual work by automatically applying common tags to all resources provisioned by that provider.

    Benefits of default tags:

    • Consistency: Ensures all resources have a base set of tags applied.
    • Less nanual work: Avoids repetitive tag definitions across resources.

    For example, setting default tags in the AWS provider:

    provider "aws" {
      default_tags {
        tags = {
          Environment = "Production"
          ManagedBy   = "Terraform"
        }
      }
    }

    How to override default tags or add extra tags

    You can override the default tags or add new tags at the resource level by specifying the tags argument.

    This argument takes precedence over the default tags defined at the provider level.

    resource "aws_s3_bucket" "example" {
      bucket = "my-bucket"
      
      # Override default Environment tag and add Purpose tag
      tags = {
        Environment = "Staging"
        Purpose     = "Data Processing"
      }
    }

    In this example, the Environment tag is overridden with the value Staging, while the Purpose tag is added specifically for this S3 bucket resource.

    The ManagedBy default tag is still applied.

    Use cases for resource-level tag customization

    • Environment-specific tags (dev, staging, prod, etc.)
    • Application/project-specific tags.
    • Resource-specific metadata tags (e.g., purpose, owner, expiration).
    • Compliance or regulatory tags based on data sensitivity.
    • Cost allocation tags for specific resources.

    By allowing tag overrides at the resource level, you maintain the benefits of default tags while gaining the flexibility to customize tags based on the specific needs of individual resources.

    Using variables and functions for flexible tagging

    Terraform allows you to define tags as variables and use functions like merge() to combine them with other tags, promoting reusability and flexibility. Defining tags as variables.

    variable "default_tags" {
      default = {
        Environment = "Production"
        ManagedBy   = "Terraform"
      }
    }

    Using the merge() function to combine tags:

    resource "aws_instance" "example" {
      ami           = "ami-0c94855ba95c71c99"
      instance_type = "t2.micro"
    
      tags = merge(
        var.default_tags,
        {
          Name    = "ExampleInstance"
          Project = "MyApp"
        }
      )
    }

    The merge() function combines the default_tags variable with additional resource-specific tags, resulting in all four tags being applied to the EC2 instance.

    Handling special cases:

    Some AWS resources require specific tagging configurations or have limitations on how tags can be applied.

    Tagging Auto Scaling Groups

    Auto Scaling Groups ASG and Launch Templates LT are tricky to tag correctly.

    Without the right configuration, the EC2 instance and attached storage volumes launched by the ASG and LT will not have the default tags attached.

    Without the right configuration, the EC2 instance and attached storage volumes launched by the ASG and LT will not have the default tags attached.

    ASGs require the propagate_at_launch tag configuration.

    resource "aws_autoscaling_group" "example" {
      # ...
      tag {
        key                 = "Environment"
        value               = "Production"
        propagate_at_launch = true
      }
    }

    Tagging Launch templates

    Launch templates require the tag_specifications configuration:

    resource "aws_launch_template" "example" {
      # ...
      
     tag_specifications {
        resource_type = "instance"
        tags = {
          Environment = "Production"
          ManagedBy   = "Terraform"
        }
      }
      tag_specifications {
        resource_type = "volume"
        tags = {
          Persistence = "Permanent"
        }
      }
    }

    Tagging EBS volumes

    When you create Elastic Compute (EC2) instances via Terraform, the Elastic Block Store (EBS) volumes attached to the EC2 are not automatically tagged.

    Untagged EBS volumes are cumbersome to administer.

    You assign the EC2 default tags to the attached EBS storage volume with the aws_instance volume_tags.

    resource "aws_instance" "example" {
      # ...
     
     volume_tags = {
        Name        = "DataVolume"
        Persistence = "Permanent"
      }
    }

    Other special cases

    Certain resources like AMIs, NAT Gateways, or VPC Endpoints may have specific tagging requirements or limitations.

    Always refer to the Terraform provider documentation for the latest guidance on tagging configurations for different resource types.

    Avoiding common pitfalls 🕳 🚶

    Inconsistent tag naming conventions

    Using inconsistent tag keys like appid, app_role, and AppPurpose makes tags harder to use and manage.

    resource "aws_s3_bucket" "example" {
      bucket = "my-bucket"
      tags = {
        appid     = "myapp"
        app_role  = "data-processing"
        AppPurpose = "logs"
      }
    }

    Instead, define an explicit ruleset for tag key naming and stick with it.

    Not tagging all resources (including secondary resources)

    Failing to tag all AWS resources, including secondary or complementary resources like EBS volumes, leads to incomplete visibility and cost tracking.

    resource "aws_instance" "example" {
      ami           = "ami-0c94855ba95c71c99"
      instance_type = "t2.micro"
      tags = {
        Environment = "Production"
      }
    }

    Identical default and resource tags issue

    Having identical tag keys and values in both default_tags and resource tags causes an error in Terraform, requiring deduplicating tags or using workarounds.

    provider "aws" {
      default_tags {
        tags = {
          Name = "Example"
        }
      }
    }
    
    resource "aws_vpc" "example" {
      tags = {
        Name = "Example" # Error: tags are identical
      }
    }

    Perpetual diff for partial tag matches

    When default_tags and resource tags have some matching and some differing tags, Terraform shows a perpetual diff trying to update the matching tags on every plan, requiring workarounds.

    provider "aws" {
      default_tags {
        tags = {
          Match1 = "A"
          Match2 = "B" 
          NoMatch = "X"
        }
      }
    }
    
    resource "aws_vpc" "example" {
      tags = {
        Match1 = "A" # Perpetual diff trying
        Match2 = "B" # to update these
        NoMatch = "Y" 
      }
    }

    Infrastructure drift and tag loss

    Losing tags due to infrastructure drift when resources are modified outside of Terraform. Using IaC consistently helps mitigate this issue.

    Best practices and tips

    Establish a clear tagging strategy and naming convention:

    Define a consistent set of tag keys and naming conventions to use across your infrastructure.

    variable "tag_names" {
        default = {
            environment = "Environment"
            application = "Application"
            team        = "Team"
            costcenter  = "CostCenter"
        }
    }

    Tag resources as you provision them (not after)

    Apply tags to resources during the provisioning process, not after the fact, to ensure consistent tagging from the start.

    resource "aws_s3_bucket" "example" {
      bucket = "my-bucket"
      tags = {
        (var.tag_names.environment) = "Production"
        (var.tag_names.application) = "MyApp"
      }
    }

    Regularly review and audit tags

    Periodically review and audit resource tags to ensure compliance with your tagging strategy and identify any missing or incorrect tags.

    Automate tagging where possible

    Leverage Terraform's features like default_tags, variables, and functions to automatically apply tags during provisioning, reducing manual effort and promoting consistency.

    AWS Resource Groups and Tag Editor

    Have you ever wanted to do the following: "Find all AWS resources in all regions that have the tag team='platform engineering' " ?.

    AWS Resource Groups and Tag Editor are excellent tools that allow you to manage tags across multiple AWS resources and regions effectively.

    Resource Groups

    Resource Groups provide a centralized way to organize and manage collections of AWS resources based on shared tags. With Resource Groups, you can:

    • Find resources across regions that have specific tags applied, such as team='platform engineering'.

    • Identify resources that are missing tags or have incorrect tag values.

    • Automate operations like starting/stopping instances or applying configurations based on resource group membership.

    • View consolidated information about resource status, costs, and configurations within a group.

    Tag Editor

    The Tag Editor is a component of Resource Groups that enables bulk tagging operations across supported AWS services and regions. Using the Tag Editor, you can:

    • Search for resources based on resource types and existing tags, allowing queries like "Find all EC2 instances with team='platform engineering'.
    • Add, modify, or remove tags on multiple resources simultaneously, streamlining tagging efforts.
    • Preview the changes before applying them, ensuring accuracy and avoiding unintended modifications.
    • Use tag-based access control policies to manage resource access based on tag values.

    🔚 Conclusion

    Proper tagging is substantial for organized, cost-friendly AWS setups, make sure to use Terraform's tagging tools and the best ways and avoid common mistakes. Thanks for reading and I hope you learned something about tagging AWS resources in Terraform!



    💡 Thank you for Reading !! 🙌🏻😁📃, see you in the next blog.🤘 Until next time 🎉

    🚀 Thank you for sticking up till the end. If you have any questions/feedback regarding this blog feel free to connect with me:

    ♻️ LinkedIn: https://www.linkedin.com/in/rajhi-saif/

    ♻️ X/Twitter: https://x.com/rajhisaifeddine

    The end ✌🏻

    🔰 Keep Learning !! Keep Sharing !! 🔰

    📻🧡 Resources

    📅 Stay updated

    Subscribe to our newsletter for more insights on AWS cloud computing and containers.