hashicorp / terraform-provider-aws

The AWS Provider enables Terraform to manage AWS resources.
https://registry.terraform.io/providers/hashicorp/aws
Mozilla Public License 2.0
9.82k stars 9.17k forks source link

Support for tag resource with dynamic keys #3143

Closed dippynark closed 4 years ago

dippynark commented 6 years ago

Terraform Version

v0.11.2

Affected Resource(s)

For our specific usecase, just aws_subnet, although this affects all resources that support tags

Terraform Configuration Files

We want to use the AWS Terraform provider to tag our public subnet for each Kubernetes cluster that needs to use it to house ELBs. The problem at the moment is that we cannot modify AWS resource tags from a Terraform environment that did not create the resource we want to tag. We also cannot specify dynamic keys for our resources.

Current format

With the current AWS Terraform provider, the best we can do is the following:

Terraform environment A

resource "aws_subnet" "public" {
  vpc_id                  = "${aws_vpc.main.id}"
  cidr_block              = "${aws_vpc.main.cidr_block}"

  tags {
    kubernetes.io/cluster/cluster_b = ""
    kubernetes.io/cluster/cluster_c = ""
    # We want to do something like the following
    # but these sorts of variable substitutions are not possible
    #kubernetes.io/cluster/${var.cluster_b_id} = ""
    #kubernetes.io/cluster/${var.cluster_c_id} = ""
  }
}

Proposed format

We would like an "aws_tag" resource that can use the resource ID imported from a remote state to change resource tags with dynamic keys

Terraform environment A

output public_subnet_id {
  value = "${aws_subnet.public.id}
}

Terraform environment B

resource "aws_tag" "cluster_tag" {
  subnet_id = "${data.remote_state.environment_a.public_subnet_id}"
  key = "kubernetes.io/cluster/${var.cluster_id}"
  value = ""
}

Terraform environment C

resource "aws_tag" "cluster_tag_" {
  subnet_id = "${data.remote_state.environment_a.public_subnet_id}"
  key = "kubernetes.io/cluster/${var.cluster_id}"
  value = ""
}

Expected Behavior

We should be able to assign resource tags with dynamic keys from a terraform environment that doesn't control the resource itself - just uses the resource ID imported from remote state

Actual Behaviour

We currently can only set static tag keys for resources and only from the same Terraform environment that the resource is created in

References

For our usecase, this change is required to use a single public subnet to house ELBs for multiple Kubernetes clusters housed in private subnets - https://github.com/kubernetes/kubernetes/issues/29298#issuecomment-356826381

rifelpet commented 6 years ago

This issue isn't specific to the AWS provider, here's a related issue on hashicorp/terraform with a workaround that uses kubernetes tagging as an example.

seh commented 6 years ago

@rifelpet, I think you missed the main thrust of this request, which also came up in hashicorp/terraform#17352. The idea is to add tags to resources that already exist, whether they're managed by Terraform or not. This is useful for cases like the subnet accumulating related Kubernetes clusters, where you create the subnet once—likely somewhere else, whether with another tool or in another Terraform root module—and later want to add tags to it as, say, new Kubernetes clusters are born.

Less clear is how to handle removal, in the absence of some kind of reference count. The simplest approach would be to have @dippynark's proposed "aws_tag" resource delete the tag from the target resource when the referencing "aws_tag" resource goes away, assuming full ownership over the tag.

gswallow commented 6 years ago

I looked into the Go code in the AWS provider repository, as was suggested in hashicorp/terraform#17352 (I don't know Go well enough to be helpful there, sorry). To me, it looks like each AWS resource type that the provider supports has its own method to manage tags; there isn't an "add tags to anything" resource that would invoke AWS's EC2 CreateTags API.

I created a janky workaround using the "external" provider, here: https://gist.github.com/gswallow/47a77aa153ff485dbd7f50eba667de15. So far, I've been able to run it more than once and it doesn't complain about updating the tag if it already exists.

jaksonwkr commented 5 years ago

I did some test here and EKS insert required tags on VPC and Subnets, but the new resource aws_tag can be very useful on another use case. It can be something like this:

resource "aws_tag" "tags_resources" {
  vpc_tags {
    tag {
      key   = "foo"
      value = "bar"
    }

    tag {
      key   = "bar"
      value = "foo"
    }
  }

  subnet_tags {
    tag {
      key   = "foo"
      value = "bar"
    }

    tag {
      key   = "bar"
      value = "foo"
    }
  }
}

So it can be enhanced with other resources types that accepts tags.

pgporada commented 5 years ago

My issue at https://discuss.hashicorp.com/t/ignoring-changes-to-tags-with-glob/2201/1 may be of use to this conversation.

bflad commented 4 years ago

A new aws_ec2_tag resource for managing individual EC2 resource tags has been merged and will release with version 2.67.0 of the Terraform AWS Provider, later next week. This resource should only be used in cases where EC2 resources are created outside Terraform (e.g. AMIs), being shared via Resource Access Manager (RAM), or implicitly created by other means (e.g. Transit Gateway VPN Attachments).

# Example configuration in Terraform 0.12 and later syntax
resource "aws_ec2_transit_gateway" "example" {}

resource "aws_customer_gateway" "example" {
  bgp_asn    = 65000
  ip_address = "172.0.0.1"
  type       = "ipsec.1"
}

resource "aws_vpn_connection" "example" {
  customer_gateway_id = aws_customer_gateway.example.id
  transit_gateway_id  = aws_ec2_transit_gateway.example.id
  type                = aws_customer_gateway.example.type
}

resource "aws_ec2_tag" "example" {
  resource_id = aws_vpn_connection.example.transit_gateway_attachment_id
  key         = "Name"
  value       = "Hello World"
}

As with any Terraform 0.12.6 or later configuration, this resource can be combined with for_each support to manage multiple resource tags, if necessary.

Thanks to @joestump and others who made the implementation possible. 👍

Another interesting new feature in this area is the recently released provider-level ignore_tags configuration, which can be used to ignore certain tag keys or tag key prefixes for all resources of the provider and can be very helpful when working with Kubernetes environments.

ghost commented 4 years ago

This has been released in version 2.67.0 of the Terraform AWS provider. Please see the Terraform documentation on provider versioning or reach out if you need any assistance upgrading.

For further feature requests or bug reports with this functionality, please create a new GitHub issue following the template for triage. Thanks!

ghost commented 4 years ago

I'm going to lock this issue because it has been closed for 30 days ⏳. This helps our maintainers find and focus on the active issues.

If you feel this issue should be reopened, we encourage creating a new issue linking back to this one for added context. Thanks!