Skip to content

rocketinsights/terraform-blog-default-tags

Repository files navigation

Best Practices for Terraform AWS Tags

Rocket Insights blog article here

Update Jan 1, 2022: Thank you for making this blog post so popular. It is a first page Google search result for aws terraform tags .

Originally we coded the default tags examples for Terraform 0.12 . We updated the example code for Terraform 1.0 and Terragrunt.

I wrote a follow-up article about solutions to bugs and traps when using Terraform AWS tags in production.

AWS tags are key-value labels you can assign to AWS resources that give extra information about it.

Rocket Insights DevOps practice recommends the following AWS tags best practices:

  • Define an explicit ruleset for tag key naming and stick with it.

    Inconsistent tags keys such as "appid", "Application ID", and "App_ID" are frustrating to use.

  • Strive for more tags, not less. Tag all AWS resources.

    The minimum Rocket Insights recommended set of tags are

    Name: human-readable resource name. Note that the AWS Console UI displays the case-sensitive "Name" tag.

    app-id: the application using the resource

    app-role: the resource's technical function, e.g. webserver, database

    app-purpose: the resource's business purpose, e.g. "frontend ui", "payment processor"

    environment: dev, test, or prod

    project: what projects use the resource

    owner: who to contact about the resource

    cost-center: who to bill for the resource usage

    automation-exclude: a true/false value for automation to not modify the resource

    pii: a true/false value if the resource stores personal identifiable information

  • Tag all Terraform created AWS resources with code path info.

    Tagging all AWS resources with the Terraform base and module path results in easier infrastructure maintenance.

Starting with Terraform 0.12.31 and AWS provider v3.38.0, HashiCorp added the default tags feature. You can now configure AWS tags in one place and automatically apply them to all AWS resources. You no longer have to manually add tags blocks to each individual Terraform AWS resource.

This Terraform AWS default tags tutorial demonstrates for both Terraform resources and modules:

  • Basic usage
  • Providing alternate default tags
  • Adding and overriding tags to the default

For the full Terraform code, please visit the Rocket Insights Github repo.

For prettier formatting, please visit the Rocket Insights blog.

Basic Usage

The default tags are defined in the AWS provider section. It automatically applies to all AWS resources

locals {
  // Change the local variable to match the git repo name
  terraform-git-repo = "terraform-blog-default-tags"
}

provider "aws" {
  region = "us-east-1"
  default_tags {
    tags = {
	  owner               = "Rocket Insights"	
      project             = "Project A"
      // This regex results in the terraform git repo name and any sub-directories.
      // For this repo, terraform-base-path is terraform-blog-default-tags/default-tags
      // This tag helps AWS UI users discover what Terraform git repo and directory to modify
      terraform-base-path = replace(path.cwd, "/^.*?(${local.terraform-git-repo}\\/)/", "$1")
    }
  }
}

resource "aws_dynamodb_table" "default-tags-basic" {
  name     = "default-tags-basic"
  .......
}

The final tags for aws_dynamodb_table.default-tags-basic are

"owner"               = "Rocket Insights"
"project"             = "Project A"
"terraform-base-path" = "terraform-blog-default-tags/default-tags"

Alternate Default Tags

If an AWS resource requires alternate default tags, define an alternate AWS provider with new default tags.

provider "aws" {
  alias  = "alt-tags"
  region = "us-east-1"
  default_tags {
    tags = {
      app-id = "Terraform Default Tags"
      owner  = "Alt Owner"
    }
  }
}

resource "aws_dynamodb_table" "default-tags-alternate" {
  provider = aws.alt-tags
  name     = "default-tags-alternate"
  ........
}

The final tags for aws_dynamodb_table.default-tags-alternate are

"app-id" = "Terraform Default Tags"
"owner"  = "Alt Owner"

Add and Override Default Tags

If an AWS resource requires more tags in addition to the default tags, simply add the tag to the built-in resource tags block.

If an AWS resource requires to override a default tag, define a tag with the same key in the tags block

locals {
  terraform-git-repo = "terraform-blog-default-tags"
}

provider "aws" {
  region = "us-east-1"
  default_tags {
    tags = {
      owner               = "Rocket Insights"
      project             = "Project A"
      terraform-base-path = replace(path.cwd, "/^.*?(${local.terraform-git-repo}\\/)/", "$1")
    }
  }
}

resource "aws_dynamodb_table" "default-tags-add" {
  name     = "default-tags-add"
  .......
  
  tags = {
    cost-center = "Rocket Insights Billing"
    project     = "Project Override"
  }
}

The final tags for aws_dynamodb_table.default-tags-add are

"cost-center"         = "Rocket Insights Billing"
"owner"               = "Rocket Insights"
"project"             = "Project Override"
"terraform-base-path" = "terraform-blog-default-tags/default-tags"

Modules Basic Usage

The AWS default tags apply to existing Terraform AWS modules without any changes needed.

locals {
  terraform-git-repo = "terraform-blog-default-tags"
}

provider "aws" {
  region = "us-east-1"
  default_tags {
    tags = {
      owner    = "Rocket Insights"
      project  = "Project A"
      terraform-base-path = replace(path.cwd,
      "/^.*?(${local.terraform-git-repo}\\/)/", "$1")
    }
  }
}

module "default-tags-dynamodb" {
  source = "./tfmodules/default-tags-dynamodb"
}

The final tags for module.default-tags-dynamodb.aws_dynamodb_table.default-tags-module are

"owner"               = "Rocket Insights"
"project"             = "Project A"
"terraform-base-path" = "terraform-blog-default-tags/default-tags"

Modules and Alternate Tags

If an AWS module requires alternate default tags, define an alternate AWS provider with new default tags and pass the new provider to the module.

provider "aws" {
  alias  = "alt-tags"
  region = "us-east-1"
  default_tags {
    tags = {
      app-id = "Terraform Default Tags"
      owner  = "Alt Owner"
    }
  }
}

module "default-tags-dynamodb-alternate" {
  source = "./tfmodules/default-tags-dynamodb"
  providers = {
    aws = aws.alt-tags
  }
  .......
}

The final tags for module.default-tags-dynamodb-alternate.aws_dynamodb_table.default-tags-module are

"app-id" = "Terraform Default Tags"
"owner"  = "Alt Owner"

Modules with both Default and Alternate Tags

If an AWS module requires both the default and alternate default tags, define the default and alternate AWS providers and pass both providers to module. Then the module assigns the correct AWS provider to the AWS resource.

This is valuable since certain AWS resources like aws_s3_bucket_object have a maximum limit of 10 tags.

locals {
  terraform-git-repo = "terraform-blog-default-tags"
}

provider "aws" {
  region = "us-east-1"
  default_tags {
    tags = {
      owner               = "Rocket Insights"
      project             = "Project A"
      terraform-base-path = replace(path.cwd, "/^.*?(${local.terraform-git-repo}\\/)/", "$1")
    }
  }
}

provider "aws" {
  alias  = "alt-tags"
  region = "us-east-1"
  default_tags {
    tags = {
      app-id = "Terraform Default Tags"
      owner  = "Alt Owner"
    }
  }
}
module "default-and-alternate-tags-dynamodb" {
  source = "./tfmodules/default-and-alternate-tags-dynamodb"
  providers = {
    aws          = aws
    aws.alt-tags = aws.alt-tags
  }
}
// In ./tfmodules/default-and-alternate-tags-dynamodb
provider "aws" {
  alias = "aws"
}
provider "aws" {
  alias = "alt-tags"
}

resource "aws_dynamodb_table" "default-and-alternate-tags-module-basic" {
  name         = "default-and-alternate-tags-module-basic"
  .......
}

resource "aws_dynamodb_table" "default-and-alternate-tags-module-alternate" {
  provider     = aws.alt-tags
  name         = "default-and-alternate-tags-module-alternate"
  .......
}

The final tags for the two DynamoDB resources in the default-and-alternate-tags-dynamodb TF modules uses the assigned AWS provider tags

The final tags for module.default-and-alternate-tags-dynamodb.aws_dynamodb_table.default-and-alternate-tags-module-basic are

// module.default-and-alternate-tags-dynamodb.aws_dynamodb_table.default-and-alternate-tags-module-basic
"owner"               = "Rocket Insights"
"project"             = "Project A"
"terraform-base-path" = "terraform-blog-default-tags/default-tags"

The final tags for module.default-and-alternate-tags-dynamodb.aws_dynamodb_table.default-and-alternate-tags-module-alternate are

// module.default-and-alternate-tags-dynamodb.aws_dynamodb_table.default-and-alternate-tags-module-alternate
"app-id" = "Terraform Default Tags"
"owner"  = "Alt Owner"

Modules and Adding and Overriding Default Tags

If an AWS module requires more tags in addition to the default tags, simply define the module tags variable and add the tag to the module.

locals {
  terraform-git-repo = "terraform-blog-default-tags"
}

provider "aws" {
  region = "us-east-1"
  default_tags {
    tags = {
      owner    = "Rocket Insights"
      project  = "Project A"
      terraform-base-path = replace(path.cwd,
      "/^.*?(${local.terraform-git-repo}\\/)/", "$1")
    }
  }
}

module "default-tags-dynamodb-add" {
  source = "./tfmodules/default-tags-dynamodb-add"

  tags = {
    app-purpose = "Adding Module Tags"
  }
}
// In ./tfmodules/default-tags-dynamodb-add
variable "tags" {
  type        = map(string)
  description = "Additional tags for DynamoDB table"
}
resource "aws_dynamodb_table" "default-tags-module-add" {
  name         = "default-tags-module-add"
  .......
  tags = merge(
    // path.module is a built-in Terraform variable that
    // describes the path of Terraform module
    // For this repo, the terraform-module-path is 
    // tfmodules/default-tags-dynamodb-add
    // Along with the terraform-base-path tag, this tag helps AWS UI users
    // discover what module repo to modify      
  Tagging Terraform module path helps maintenance      
    { terraform-module-path = path.module },
    var.tags
  )
}

The final tags for module.default-tags-dynamodb-add.aws_dynamodb_table.default-tags-module-add are

"app-purpose"           = "Adding Module Tags"
"owner"                 = "Rocket Insights"
"project"               = "Project A"
"terraform-base-path"   = "terraform-blog-default-tags/default-tags"
"terraform-module-path" = "tfmodules/default-tags-dynamodb-add

Conclusion

Terraform default tags for AWS are an easy way to add metadata to all AWS resources. Defining the default tags in one code location follows the best practice of DRY (Don't Repeat Yourself). If you have complex tagging requirements, Terraform is customizable to meet your needs.

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages