Introduction
In version 3.38.0 and later of the Terraform AWS Provider, a default_tagsargument is available in the provider configuration block. This argument allows for tagging all resources that support the tags argument. It is intended as an alternative to the repetitive nature of applying the same tags across all resources.
Important Note: Provider Version 5.0.0+
Version 5.0.0 of the provider introduced several fixes to the issues identified below. Before using the workarounds in this article, please follow the version 5 upgrade guide and verify if upgrading the provider resolves these issues for your use case.
Problem
When using both default_tags at the provider level and tags at the resource level in AWS Provider versions 3.38.0 through 4.67.0, you may encounter several known issues.
Issue 1: Identical Tags Error
If the default_tags and resource-level tags arguments are identical, the provider returns an error.
This example configuration produces the error.
provider "aws" {
default_tags {
tags = {
Name = "Example"
}
}
}
resource "aws_vpc" "example" {
tags = {
Name = "Example"
}
}The resulting error is:
Error: "tags" are identical to those in the "default_tags" configuration block of the provider: please de-duplicate and try again
This behavior is tracked in GitHub Issue #19204.
Issue 2: Perpetual Diff on Plan
If default_tags and resource-level tags are partially identical, Terraform plans a continuous update for the matching tags on every run, even when no configuration has changed.
This example configuration produces the issue.
provider "aws" {
default_tags {
tags = {
Match1 = "A"
Match2 = "B"
NoMatch = "X"
}
}
}
resource "aws_vpc" "example" {
tags = {
Match1 = "A"
Match2 = "B"
NoMatch = "Y"
}
}After the initial terraform apply, subsequent plans will show a perpetual diff.
$ terraform plan
# aws_vpc.example will be updated in-place
~ resource "aws_vpc" "example" {
# ...
~ tags = {
+ "Match1" = "A"
+ "Match2" = "B"
# (1 unchanged element hidden)
}
# (28 unchanged attributes hidden)
}This known issue is also discussed in the comments of issue #19204.
Issue 3: No Exclusion Mechanism
There is no way to exclude a resource from receiving default_tags. Tags from the provider block can only be overridden by defining a tag with the same key on the resource; they cannot be removed entirely for a specific resource.
Cause
The default_tags feature was a design implemented by the Terraform AWS Provider to simplify tagging. However, there is no corresponding "global tags" concept in the AWS API. When Terraform reads tag data from AWS, there is no property that indicates whether a tag was created globally or at a resource level. This ambiguity makes it difficult for the provider to correctly categorize and merge tag data. You can find further discussion in this GitHub comment.
Solutions
If you are unable to upgrade to version 5.0.0+ of the AWS provider, you can use one of the following workarounds to avoid the perpetual diff scenario.
Solution 1: Use Resource-Level Tags for Overrides Only
The primary recommendation is to avoid defining identical tags in both the default_tags provider block and the tags argument on individual resources. Use resource-level tags only to override a default tag or to apply tags that are unique to that resource.
Solution 2: Remove Redundant Tags
Review your configuration and remove redundant tag definitions.
- If a tag is defined in
default_tagsand is not being overridden, remove it from the individual resource'stagsblock. - If a tag is defined on all resources, consider removing it from the
default_tagsblock and managing it only at the resource level.
Solution 3: Discontinue default_tags Usage
Stop using the default_tags feature entirely and configure all tags directly on each resource.
Solution 4: Implement a Custom Tagging Module
Replicate the default_tags behavior using local variables and the merge function. This approach gives you explicit control over tag application.
locals {
common_tags = {
tag1 = "A"
tag2 = "B"
tag3 = "C"
}
}
resource "aws_instance" "vm" {
# ...
tags = merge(local.common_tags, var.override_tags)
}For more information, refer to the documentation for the merge function.