Terraform-provider-aws: Default tags for all resources

Created on 13 Mar 2019  路  26Comments  路  Source: hashicorp/terraform-provider-aws

Description

I'd like to set default tags for all resources created within a Terraform stack.

When creating service stacks on AWS with Terraform, I like to tag all resources of a project. Right now, this involves a lot of manual work.
I would like to set default tags with the provider, that then automatically get applied to all AWS resources that support it.

There has been an issue over on the main terraform repo (since 2015) which had plenty of 馃憤 and got closed in favor of creating issues with the respective providers. I couldn't find such an issue though, thus creating this one.

New or Affected Resource(s)

  • aws_*

Potential Terraform Configuration

provider "aws" {
  # ...

  default_tags = {
    Project = "Project_1"
    Environment = "Production"
  }
}

References

  • Original issue: hashicorp/terraform#2283

Community Note

  • Please vote on this issue by adding a 馃憤 reaction to the original issue to help the community and maintainers prioritize this request
  • Please do not leave "+1" or "me too" comments, they generate extra noise for issue followers and do not help prioritize the request
  • If you are interested in working on this issue or have submitted a pull request, please leave a comment
enhancement provider

Most helpful comment

Hi, everyone! 馃憢 Thank you for your interest in this functionality. This would be awesome to have within the Terraform AWS Provider.

I just wanted to drop a quick note in here that the maintainers are on board with thinking about this design and trying to develop this in a way that can be plugged into the existing provider codebase (e.g. per AWS service or resource) with community help if at all feasible. We would likely want to shy away from any large "all or nothing" changes because it will require large amounts of integration testing and hold up other development work within the provider codebase.

Something to also consider here is the related, but opposite problem outlined in https://github.com/hashicorp/terraform/issues/20866 where we could also have the ability to _ignore_ a list of tags, per Terraform AWS Provider.

From an operator perspective, the combined design would hopefully be fairly simple:

# Design sketch - not currently implemented and design may change during development
provider "aws" {
  # ... potentially other configuration ...

  default_tags = {
    Tag1 = "Value1"
    Tag2 = "Value2"
    # ...
  }

  ignore_tags = ["Tag3"]
}

Then any resource could retrieve that information from the meta struct that is available from the provider to appropriately handle adding/removing tags from differences/state values.

However as alluded to above, the underlying implementation work may potentially require some new functionality not currently available (or easily usable) in the Terraform Provider SDK and Terraform AWS Provider codebase. We're hoping to meet about this in the next week or two and can hopefully present more ideas or next steps on the manner. Ideally we would like to have clear documentation on how this can be implemented, documented, and tested so the work can be distributed for each resource when the design work is done.

The aws_autoscaling_group resource will need to be handled specially and will likely require a separate design proposal as it may require potential behavior changes in the resource to align it with the rest of the Terraform AWS Provider resources with regards to resource tagging. 馃憤

All 26 comments

In general (with my limited knowledge on the provider specifics) this should be possible. The one tricky part would be any resources that have additions to the basic tag properties. The one I know that does this is the auto scaling group resources which in addition to a key and value also has a _required_ property which determines whether the tags propagate to instances the ASG creates. So that should probably be taken into account when (if?) implementing this.

@gwkunze sorry for the late reply, am currently on a long vacation, coming back next week and want to have a proper look at this then.

I've never handled the terraform core nor written any plugins, but want to give this a try anyways as I'd love to have this feature.

Is there anything else that you can think of that one would need to keep in mind when implementing this? I'm very confident that my TF knowledge is way more limited than yours haha :-)

Thanks already for this suggestion!

Alright, so I investigated this a little further and am pretty sure that I won't have the resources to tackle this. I have no prior experience with go, never contributed to this repo before and have not a whole lot of time.
I'm happy to help somebody else with minor things but that's all I can do, I think.

I'm really interested in this and might be able to put some time into it in the next couple of weeks. I was thinking a little bit on @gwkunze's comment about resources like ASG, and wanted to throw out a couple of potential syntax options.

  1. Handle the extra properties in the provider configuration:

    provider "aws" {
      # ...
    
      default_tags = [
        {
          key   = "Environment"
          value = "Development"
    
          aws_autoscaling_group {
            propagate_at_launch = true
          }
        },
        {
          key   = "Project"
          value = "Test Project"
        }
      ]
    }
    
  2. Handle the extra properties in the resource configuration:

    resource "aws_autoscaling_group" "asg" {
      # ...
    
      default_tags_propagate_at_launch = true
    }
    
  3. Don't handle them at all; ASG's don't get default tags applied, you just have to specify them explicitly as normal tags.

There don't appear to be any other resources where this happens, so it's hard to generalize a decent syntax. But I lean towards #2 as the cleanest way to deal with the fact that propagate_at_launch is required - just make this property required too. (Or maybe required only when there are default tags, if that's possible.)

Thoughts?

There don't appear to be any other resources where this happens, so it's hard to generalize a decent syntax.

In terms of tag propagation, there are other "AWS" resources where this is supported (though not sure how this is done in terraform):

  • CloudFormation propagates all tags applied to a stack to resources it contains on creation (this behaviour cannot be changed as far as I am aware).
  • ECS can propagate tags from task definitions to the service (the parameter is even called propagateTags.

@jonscheiding my first instinct is to prefer the first option to keep all that configuration together, but on the other hand, the auto-scaling group is just one exception and even there it has become less necessary as EC2 Launch Templates gives you more than enough to replace the need for the "propagateAtLaunch"-feature and using the regular tag syntax, ie:

tags = {
  Foo = "BAR"
}

if much nicer to read/write than

tags = [
  {
    key = "Foo"
    value = "BAR"
  }
]

Even if you give extra properties a reasonable default.

So on second though I think I prefer the second or third option. I can also suggest a potential slight alternative to the second option: specifying a list of tag-keys you want propagated, something like:

resource "aws_autoscaling_group" "asg" {
  propagate_tags = ["Environment", "Project"]
}

@gwkunze If I'm understanding you correctly, you're proposing we go from

resource "aws_autoscaling_group" "asg" {
  tag { 
    key   = "some key"
    value = "some value"
    propagate_at_launch = true
  }
}

to something like

resource "aws_autoscaling_group" "asg" {
  tags = {
    "some key" = "some value"
  }

  propagate_tags_at_launch = ["some key", "Environment"] # You can include default tags here
}

I think I like that syntax better too, but I would shy away from undertaking a breaking schema change.

I've been trying to wrap my head around the Terraform and AWS provider code to understand how this would be implemented. It seems like I would have to touch every resource [that supports tags] and probably add a CustomizeDiff handler that adds the provider-level tags. (For example, here.)

AFAICT there is not any kind of like "provider-level CustomizeDiff" that could be used to do this more DRY-ly.

Can anybody who understands this code better than me confirm that?

@jonscheiding I meant going to that syntax only for the "default tag" functionality on the provider itself, not change the existing structure for auto scaling groups. So the change would be something like add a default_tags field on the provider and add propagate_tags_at_launch or maybe propagate_default_tags_at_launch (for more clarity) to asg resources.

Hi, everyone! 馃憢 Thank you for your interest in this functionality. This would be awesome to have within the Terraform AWS Provider.

I just wanted to drop a quick note in here that the maintainers are on board with thinking about this design and trying to develop this in a way that can be plugged into the existing provider codebase (e.g. per AWS service or resource) with community help if at all feasible. We would likely want to shy away from any large "all or nothing" changes because it will require large amounts of integration testing and hold up other development work within the provider codebase.

Something to also consider here is the related, but opposite problem outlined in https://github.com/hashicorp/terraform/issues/20866 where we could also have the ability to _ignore_ a list of tags, per Terraform AWS Provider.

From an operator perspective, the combined design would hopefully be fairly simple:

# Design sketch - not currently implemented and design may change during development
provider "aws" {
  # ... potentially other configuration ...

  default_tags = {
    Tag1 = "Value1"
    Tag2 = "Value2"
    # ...
  }

  ignore_tags = ["Tag3"]
}

Then any resource could retrieve that information from the meta struct that is available from the provider to appropriately handle adding/removing tags from differences/state values.

However as alluded to above, the underlying implementation work may potentially require some new functionality not currently available (or easily usable) in the Terraform Provider SDK and Terraform AWS Provider codebase. We're hoping to meet about this in the next week or two and can hopefully present more ideas or next steps on the manner. Ideally we would like to have clear documentation on how this can be implemented, documented, and tested so the work can be distributed for each resource when the design work is done.

The aws_autoscaling_group resource will need to be handled specially and will likely require a separate design proposal as it may require potential behavior changes in the resource to align it with the rest of the Terraform AWS Provider resources with regards to resource tagging. 馃憤

While I'm not 100% certain about this, the propagate_tags_at_launch as a list to select which tags to propagate concept proposed above could also be applied to the existing aws_autoscaling_group as a change in interface. At the very least that would normalize tags across all resources making it less of an exception. It could even potentially be made as a backwards-compatible change by having the provider check the type of tags, if it's a list it's in the "old" list-of-objects format and it it's a map(string) it's the same as any other tagged ec2 resource.

I'm somewhat ambivalent about this change as it moves the terraform syntax further away from the raw API which could make it slightly more complex to learn. But on the other hand, consistency is important and it seems with the introduction of Launch Templates Amazon themselves are making "propagate_at_launch" superfluous.

Hi, would it possible to use wildcards with ignore_tags?

Apparently using ignore_changes lifecycle for tags actually works as prefix wilrdcard in Terraform 0.11.x. Would be nice to have similar functionality for 0.12.x

Hi folks 馃憢

To provide an update here, we are settling in on certain details that are in an effort to support this provider-level default tagging across all resources. For example, see the refactoring effort to make this much functionality easier to implement within each resource (https://github.com/terraform-providers/terraform-provider-aws/issues/10688) and the related provider-level ignore tagging enhancement (https://github.com/terraform-providers/terraform-provider-aws/issues/10689).

Hi, would it possible to use wildcards with ignore_tags?

Great question and yes! In our proof of concept implementation for ignoring tags, we are including both an argument to ignore a list of exact tag keys and a separate argument to ignore a list of tag key prefixes. Both are optional and can be combined if desired. Proof of Concept PR


Given certain limitations of the current Terraform Plugin SDK, we anticipate that providing the ability to support default tagging and have the terraform plan output of each resource correctly show the behavior (without amassing a large amount of boilerplate code in each resource) will likely require some additional design and implementation work beyond the above. The Terraform teams at HashiCorp are actively looking into this effort over the coming months, however we cannot provide a more precise timeline at the moment.

The ignore tags proof of concept with the aws_subnet and aws_vpc resources has been merged and will release with version 2.35.0 of the Terraform AWS Provider in a week. 馃帀

@bflad: It appears that #10688 and #10689 have been released in 2.53 and 2.60 respectively. Can you give an update based on your experiences so on what additional requirements (if any) you've uncovered through those efforts?

Hey Y'all!

PM for the AWS provider here.

I'm planning to spend some time over the next month or two diving into this topic and researching how this feature would benefit the wide variety of tagging workflows in our user base.

If you're interested in chatting for 30-45 minutes about tagging generally and more specifically about this feature, drop an @, comment, or grab some time on my calendar!

Look forward to chatting with some of y'all about tagging!

@maryelizbeth I don't know that I want to waste your time expanding this to 30 minutes but we use tagging for cost-reporting and some access-control decisions so this feature would be really useful for us to ensure that nobody forgets a particular resource, especially as AWS features change.

@maryelizbeth like Chris, we're using tagging for cost allocation. I'm working with many several teams on several projects, so generating a set of default tags for a project, then propagating those out through all the modules to all of the resources becomes far from a trivial change to add tags to existing projects.

@maryelizbeth Cost allocation is one reason we use tags for. Another one is the mapping between resources deployed and the repo/location that holds the related terraform stuff. This way you can fetch tags from a resource on AWS and find the related TF code.

That's very helpful since we have a repo per project (which usually maps to an AWS Account). Inside this we have a bunch of subdirectoies following a defined structure. For example shared/vpc holds the entities that that create the VPC resources (subnets etc). services/x holds all the stuff (lets say EC2 autoscaling groups, ALBs etc.) required to deploy services _X_. If service X depends on other stuff we use remote_states to handle this (which allows us to resolve dependencies via solaris). Since we have the remote state in every directory the key of the remote state is unique in the project.

In practice this currently looks like this:

provider "aws" {
  # ...
}

locals {
  common_tags = {
    env                = "production"
    terraform_repo     = "github.com/some_org/some_repo"
    terraform_state_id = "some_unique_key"
  }
}

terraform {
  backend "s3" {
    key            = "tfstate/some_unique_key"
    # ...
  }
}

resource "aws_cloudfront_distribution" "this" {
  tags = merge(
    local.common_tags,
    {
      "AdditionalTag" = "foobar"
    },
  )
}

Now we can look at the tags of the CF dist (via web console for example) and now exactly that this resource is managed in repo github.com/some_org/some_repo. A quick git grep for some_unique_key and we know where to modify the distribution.

In line with the previous responses, we use tags for a number of things:

  • cost allocation tracking (most important, and highly visible to leadership/auditors)
  • infrastructure-as-code source reference: because we share accounts between teams who might use different frameworks/tooling to manage their infrastructure, and when our automated auditing tools find issues with existing resources, this way, we know how to find the origin and slipstream fixes

    • service ownership tracking - resources are tagged with team email and notification email so the owner of any resource is immediately clear, and we get to do some nice automation based on this. When resources cannot be tagged, this is done using name prefices (the AWS provider has good support for that in some plac es) or the tags of logical parent resources.

  • IAM access control for some resource types (like EC2 SSM session attachment privileges)

My personal take on the status quo is: Because Terraform modules support interface definitions based on variables{} (even with type narrowing now), and because we added the tagging semantics to our (AWS) module standard and linting rules already, there exists a reasonable stopgap solution to the tagging problem at the expense of more HCL code.

Like others that already replied, I mainly use tagging for cost reporting. Also, sometimes I use tags to group resource in a group (like the application that they are part of or the team that owns the resource).

Is there an alternative way to group all resources created by Terraform? one that can be used in the latest release

Hey Y'all!

First - a big thank you to everyone upthread that helped me complete research for this feature. I've published the conclusions internally at HashiCorp and we'll be reviewing them for future implementation. Once we've reached a conclusion on the design of the feature, we'll consider it for implementation. That consideration will likely come after the completion of the Aug-Oct Roadmap.

@mattfysh We have a short guide here on resource tagging which may contain some helpful information! However, I'd recommend reaching out the community on the HashiCorp Community Forum
as there is a larger number of people that will be able to help in contrast to here on GitHub where the myriad of issues is monitored by a much smaller number of maintainers.

Thank you for the update. Just adding another use-case if it will help your design. I have some Terraform-managed resources (from different repos) in an AWS account with non-TF resources. If I apply a tag to all of the Terraform-managed resources I can map the resources to the repos and then it's easier for someone observing in the AWS Console UI instead of having to run terraform state list | xargs -n 1 terraform state show -state=.terraform/terraform.tfstate and then diffing manually.

Another use case:
Datadog / Prometheus dashboard filtering based on tags (e.g. $environment)

FWIW, AWS CDK uses recursive tagging and I've found it a really helpful feature. Not super familiar with Terraform but figured I'd share these docs in case it helps with your design here: https://docs.aws.amazon.com/cdk/latest/guide/tagging.html

Was this page helpful?
0 / 5 - 0 ratings