Skip to content

Resource Tagging

Many AWS services implement resource tags as an essential part of managing components. These arbitrary key-value pairs can be utilized for billing, ownership, automation, access control, and many other use cases. Given that these tags are an important aspect of successfully managing an AWS environment, the Terraform AWS Provider implements additional functionality beyond the typical one-to-one resource lifecycle management for easier and more customized implementations.

-> Not all AWS resources support tagging, which can differ across AWS services and even across resources within the same service. Browse the individual Terraform AWS Provider resource documentation pages for the tags argument, to see which support resource tagging. If the AWS API implements tagging support for a resource and it is missing from the Terraform AWS Provider resource, a feature request can be submitted.

Getting Started with Resource Tags

Terraform AWS Provider resources that support resource tags implement a consistent argument named tags which accepts a key-value map, e.g.,

/*Provider bindings are generated by running cdktf get.
See https://cdk.tf/provider-generation for more details.*/
import * as aws from "./.gen/providers/aws";
new aws.vpc.Vpc(this, "example", {
  tags: {
    Name: "MyVPC",
  },
});

The tags for the resource are wholly managed by Terraform except tag keys beginning with aws: as these are managed by AWS services and cannot typically be edited or deleted. Any non-AWS tags added to the VPC outside of Terraform will be proposed for removal on the next Terraform execution. Missing tags or those with incorrect values from the Terraform configuration will be proposed for addition or update on the next Terraform execution. Advanced patterns that can adjust these behaviors for special use cases, such as Terraform AWS Provider configurations that affect all resources and the ability to manage resource tags for resources not managed by Terraform, can be found later in this guide.

For most environments and use cases, this is the typical implementation pattern, whether it be in a standalone Terraform configuration or within a Terraform Module. The Terraform configuration language also enables less repetitive configurations via variables, locals, or potentially a combination of these, e.g.,

import * as cdktf from "cdktf";
/*Provider bindings are generated by running cdktf get.
See https://cdk.tf/provider-generation for more details.*/
import * as aws from "./.gen/providers/aws";
/*Terraform Variables are not always the best fit for getting inputs in the context of Terraform CDK.
You can read more about this at https://cdk.tf/variables*/
const additionalTags = new cdktf.TerraformVariable(this, "additional_tags", {
  default: [{}],
  description: "Additional resource tags",
});
new aws.vpc.Vpc(this, "example", {
  tags: `\${merge(
    ${additionalTags.value},
    {
      Name = "MyVPC"
    },
  )}`,
});

Ignoring Changes to Specific Tags

Systems outside of Terraform may automatically interact with the tagging associated with AWS resources. These external systems may be for administrative purposes, such as a Configuration Management Database, or the tagging may be required functionality for those systems, such as Kubernetes. This section shows methods to prevent Terraform from showing differences for specific tags.

Ignoring Changes in Individual Resources

All Terraform resources support the lifecycle configuration block ignoreChanges argument, which can be used to explicitly ignore all tags changes on a resource beyond an initial configuration or individual tag values.

In this example, the name tag will be added to the VPC on resource creation, however any external changes to the name tag value or the addition/removal of any tag (including the name tag) will be ignored:

/*Provider bindings are generated by running cdktf get.
See https://cdk.tf/provider-generation for more details.*/
import * as aws from "./.gen/providers/aws";
const awsVpcExample = new aws.vpc.Vpc(this, "example", {
  tags: {
    Name: "MyVPC",
  },
});
awsVpcExample.addOverride("lifecycle", [
  {
    ignore_changes: ["${tags}"],
  },
]);

In this example, the name and owner tags will be added to the VPC on resource creation, however any external changes to the value of the name tag will be ignored while any changes to other tags (including the owner tag and any additions) will still be proposed:

/*Provider bindings are generated by running cdktf get.
See https://cdk.tf/provider-generation for more details.*/
import * as aws from "./.gen/providers/aws";
const awsVpcExample = new aws.vpc.Vpc(this, "example", {
  tags: {
    Name: "MyVPC",
    Owner: "Operations",
  },
});
awsVpcExample.addOverride("lifecycle", [
  {
    ignore_changes: ["${tags.Name}"],
  },
]);

Ignoring Changes in All Resources

As of version 2.60.0 of the Terraform AWS Provider, there is support for ignoring tag changes across all resources under a provider. This simplifies situations where certain tags may be externally applied more globally and enhances functionality beyond ignoreChanges to support cases such as tag key prefixes.

In this example, all resources will ignore any addition of the lastScanned tag:

/*Provider bindings are generated by running cdktf get.
See https://cdk.tf/provider-generation for more details.*/
import * as aws from "./.gen/providers/aws";
new aws.provider.AwsProvider(this, "aws", {
  ignoreTags: [
    {
      keys: ["LastScanned"],
    },
  ],
});

In this example, all resources will ignore any addition of tags with the kubernetesIo/ prefix, such as kubernetesIo/cluster/name or kubernetesIo/role/elb:

/*Provider bindings are generated by running cdktf get.
See https://cdk.tf/provider-generation for more details.*/
import * as aws from "./.gen/providers/aws";
new aws.provider.AwsProvider(this, "aws", {
  ignoreTags: [
    {
      keyPrefixes: ["kubernetes.io/"],
    },
  ],
});

Any of the ignoreTags configurations can be combined as needed.

The provider ignore tags configuration applies to all Terraform AWS Provider resources under that particular instance (the default provider instance in the above cases). If multiple, different Terraform AWS Provider configurations are being used (e.g., multiple provider instances), the ignore tags configuration must be added to all applicable provider configurations.

Managing Individual Resource Tags

Certain Terraform AWS Provider services support a special resource for managing an individual tag on a resource without managing the resource itself. One example is the awsEc2Tag resource. These resources enable tagging where resources are created outside Terraform such as EC2 Images (AMIs), shared across accounts via Resource Access Manager (RAM), or implicitly created by other means such as EC2 VPN Connections implicitly creating a taggable EC2 Transit Gateway VPN Attachment.

\~> NOTE: This is an advanced use case and can cause conflicting management issues when improperly implemented. These individual tag resources should not be combined with the Terraform resource for managing the parent resource. For example, using awsVpc and awsEc2Tag to manage tags of the same VPC will cause a perpetual difference where the awsVpc resource will try to remove the tag being added by the awsEc2Tag resource.

-> Not all services supported by the Terraform AWS Provider implement these resources. Browse the Terraform AWS Provider resource documentation pages for a resource with a type ending in tag. If there is a use case where this type of resource is missing, a feature request can be submitted.

/*Provider bindings are generated by running cdktf get.
See https://cdk.tf/provider-generation for more details.*/
import * as aws from "./.gen/providers/aws";
new aws.ec2Tag.Ec2Tag(this, "example", {
  key: "Owner",
  resourceId: "${aws_vpn_connection.example.transit_gateway_attachment_id}",
  value: "Operations",
});

To manage multiple tags for a resource in this scenario, forEach can be used:

# Terraform 0.12 and later syntax
# ... other configuration ...

resource "aws_ec2_tag" "example" {
  for_each = { "Name" : "MyAttachment", "Owner" : "Operations" }

  resource_id = aws_vpn_connection.example.transit_gateway_attachment_id
  key         = each.key
  value       = each.value
}

The inline map provided to forEach in the example above is used for brevity, but other Terraform configuration language features similar to those noted at the beginning of this guide can be used to make the example more extensible.

Propagating Tags to All Resources

As of version 3.38.0 of the Terraform AWS Provider, the Terraform Configuration language also enables provider-level tagging as an alternative to the methods described in the Getting Started with Resource Tags section above. This functionality is available for all Terraform AWS Provider resources that currently support tags, with the exception of the awsAutoscalingGroup resource. Refactoring the use of variables or locals may look like:

/*Provider bindings are generated by running cdktf get.
See https://cdk.tf/provider-generation for more details.*/
import * as aws from "./.gen/providers/aws";
new aws.provider.AwsProvider(this, "aws", {
  defaultTags: [
    {
      tags: {
        environment: "Production",
        owner: "Ops",
      },
    },
  ],
});
new aws.vpc.Vpc(this, "example", {
  tags: {
    Name: "MyVPC",
  },
});

In this example, the environment and owner tags defined within the provider configuration block will be added to the VPC on resource creation, in addition to the name tag defined within the VPC resource configuration. To access all the tags applied to the VPC resource, use the read-only attribute tagsAll, e.g., awsVpcExampleTagsAll.