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
- Ignoring Changes to Specific Tags
- Ignoring Changes in Individual Resources
- Ignoring Changes in All Resources
- Managing Individual Resource Tags
- Propagating Tags to All Resources
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
.