Terraform AWS Provider Default Tags Inheritance and Override Mechanics

Terraform tagging for AWS resources operates as a layered system where a single definition can propagate across hundreds of resources, and where the provider level, module level, and resource level each contribute to the final tag set that is written to AWS. The mechanism is designed to reduce repetition, enforce organizational standards, and allow surgical overrides without breaking the consistency model. The reference material describes the feature as both a convenience for module authors and a governance tool for platform teams who need cost allocation, access control, and auditability to be automatic rather than optional.

A tag in this context is a key value pair that AWS accepts on a taggable resource. Each tag consists of a unique key and its corresponding value. The example form used throughout the documentation is:
tags = { Environment = "Development" Owner = "Luke Skywalker" Department = "Jedi Order" }
The presence of a consistent key set across a large estate makes it possible for billing reports to attribute spend, for IAM policies to reference conditions, and for operational dashboards to filter resources by team or environment. When tags are missing or inconsistent, resources become orphaned in cost reports and compliance checks fail.

Default tags definition and intent

Default tags in Terraform refer to tags applied to all or most of the resources in your configuration. These can be defined at a higher level through variables or modules to avoid repetition and ensure consistency across resources. You can define default tags either by setting them globally through variables or directly within each resource.

The variable pattern shown is:
variable "common_tags" { type = map(string) default = { Environment = "Development" } } tags = var.common_tags
This pattern centralizes a baseline tag set. The impact is that teams no longer need to copy the same map into every resource block, which reduces drift and the chance that a new resource is created without the required metadata. In practice, the variable can be consumed by modules and overridden by callers, which preserves flexibility while maintaining a default.

The HashiCorp Terraform AWS Provider contains over 700 resources to standardize your AWS infrastructure for configuration in accordance with best practices. One of the most common requests we’ve heard is for the ability to define default tags at the provider level of your Terraform configuration. We’re pleased to announce that as of v3.38.0 of the Terraform AWS provider, you are able to define default tags for all resources except Auto Scaling Groups.

The provider level capability changes the workflow from per-resource tagging to a single declaration that is inherited by dependent Terraform modules. Setting default tags at the provider level will not supersede tags set on individual resources as resource tags take precedence.

Provider level configuration requirements

In order to configure default tags you will need:

Terraform 0.12 or later
Terraform AWS Provider v3.38.0 or later

The requirement for Terraform 0.12 or later aligns with the introduction of the more flexible HCL2 expression language and first-class support for provider configurations. The v3.38.0 provider version is the specific release that introduced the default_tags block.

A minimal provider declaration with default tags is:
provider "aws" { default_tags { tags = { Environment = "Test" Owner = "TFProviders" Project = "Test" } } }
Every taggable resource created by that provider automatically inherits these tags. The inheritance is not a merge that is hidden from the user; it is visible in plan output and in the tags that appear in AWS.

The example resources illustrate inheritance alongside explicit tags:
resource "aws_vpc" "example" { cidr_block = "10.1.0.0/16" tags = { Name = "my-vpc-resource" } } resource "aws_subnet" "example" { cidr_block = "10.1.1.0/24" vpc_id = aws_vpc.test.id tags = { Name = "my-subnet-resource" } }
The VPC and subnet will each receive Environment = "Test", Owner = "TFProviders", Project = "Test" from the provider defaults, and in addition the Name tag specified on the resource. Resource tags take precedence over provider defaults when a key collides.

Tags are the foundation of AWS cost management, access control, and operational visibility. Without consistent tagging, you end up with resources you can't attribute to teams, costs you can't allocate, and compliance gaps you can't close. The problem is that tagging is usually an afterthought - someone creates a resource, forgets the tags, and now you've got an untagged EC2 instance eating budget with no owner.

Terraform's default_tags feature, combined with validation and organizational policies, solves this problem at the infrastructure level.

Default tags feature at the provider level

Terraform's AWS provider supports default_tags at the provider level. Every taggable resource created by that provider automatically inherits these tags:

provider "aws" { region = var.aws_region default_tags { tags = { Environment = var.environment Project = var.project_name ManagedBy = "terraform" Team = var.team_name CostCenter = var.cost_center } } }
That's it. Every supported AWS resource created by this provider gets those five tags automatically. The main exception is awsautoscalinggroup, which needs tags configured on the resource itself.

The use of variables inside the default_tags block allows the same provider configuration to be reused across workspaces, teams, and environments. The impact is that a platform team can define a single provider block and enforce that all resources carry Environment, Project, ManagedBy, Team, and CostCenter without requiring each module author to remember to pass them down.

A comparison of inheritance behavior is shown below.

| Scope | Definition location | Precedence | Inheritance |
| Provider default_tags | provider "aws" block | Lowest, merged unless overridden | Inherited by all resources from that provider, including resources created by child modules |
| Module default tags | locals or variables inside module | Medium, can be merged with caller input | Applied to resources defined inside the module |
| Resource tags | resource "..." block | Highest, overrides provider and module | Applies only to that specific resource |

Override mechanics

If you want to override the default tags with custom tags for specific resources, you can do this by explicitly defining the tags argument in the resource definition.

Override at the resource level: If default tags are applied via the provider or module, you can add or change specific tags directly within the resource block.

Merge default and custom tags: Use Terraform functions like merge() to combine the default tags with custom tags while giving priority to the custom ones.

The merge pattern is important for modules. The Terraform AWS Provider supports a field known as default_tags which can significantly cutdown on the amount of copying & pasting when it comes to apply tags on all resources within a deployment. This is especially useful for Terraform modules, as they don’t require passing along tags to each sub-module contained within. This makes the overhead of setting up metadata within a Terraform module more practical, allowing for things like licensing, documentation URL or compliance-driven tags to be defined inside of the module itself.

An issue that arises with embedding these tags in the modules, is that consumers of Terraform modules usually need some flexibility with specifying tags to the module. As these tags are often used for cost-centre or compliance resources within an organization, and thus mandated to exist on the deployed resources.

To address this, one can make use of the merge function, which allows the module to define it owns list of tags, that can be overwritten by the consumer of the module as needed. This involves creating a tags variable, which can have values passed in by the consumer, and a local known as module_tags. The resultant merging of these two fields yields a local called tags that can be passed in to the Terraform provider.

Any module tag specified to the tags variable will be overwritten by the value provided by the consumer.

In practice this means a module can define a baseline:
module_tags = { ManagedBy = "terraform" } tags = merge(module_tags, var.tags)
The caller can then pass var.tags with Project or CostCenter values that override the defaults where needed.

Auto Scaling Group exception

Due to the dynamic nature of Auto Scaling Groups, they behave differently than other AWS resources.

Default Tags for Auto Scaling Groups: Due to the dynamic nature of Auto Scaling Groups, they behave differently than other AWS resources.

The provider documentation notes that default tags are not applied to awsautoscalinggroup. The default_tags block applies tags to all resources managed by this provider, except for the Auto Scaling groups (ASG).

In the tutorial scenario, the defaulttags block in the AWS provider defines Environment and Service tags:
provider "aws" { profile = "default" region = "us-east-2" default_tags { tags = { Environment = "Test" Service = "Example" HashiCorp-Learn = "aws-default-tags" } } }
The default
tags block applies tags to all resources managed by this provider, except for the Auto Scaling groups (ASG).

When an Auto Scaling group is created, the tags include the default ASG tags but not the default tags from your provider configuration.

Use the -replace option for terraform apply to reprovision the Auto Scaling group and launch a new instance with the appropriate tags.
$ terraform apply -replace aws_autoscaling_group.example
The tags observed on a launch template version can include system tags such as:
{ "Key": "aws:ec2launchtemplate:version", "Value": "1" }, { "Key": "aws:autoscaling:groupName", "Value": "terraform-20210720164457433400000003" }
These tags are generated by AWS and are distinct from user defined default tags. The absence of provider default tags on ASG resources is a known limitation that requires explicit tagging on the resource or via launch template tag specifications.

Ignoring tag changes from external systems

How to ignore changes to Terraform tags?

Suppose external systems interact with resource tags (e.g., Configuration Management Databases or your cloud adds auto-generated tags for certain resources, such as Azure Databricks)

The reference material raises the scenario where external systems modify tags outside of Terraform. When a cloud provider adds auto-generated tags or a CMDB process updates tags, Terraform may detect drift and attempt to revert those changes on the next apply. The practical implication is that teams must decide whether Terraform should be the sole source of truth for tags or whether certain tags are managed externally. The material does not prescribe a specific lifecycle meta argument, but it identifies the problem as a real operational concern for environments where tagging is multi-authored.

Tutorial workflow and HCP Terraform integration

AWS recommends that you define a robust and consistent tagging strategy to enable better auditing, cost, and access control for your AWS resources. The AWS Terraform provider v3.38.0+ allows you to add default tags to all resources that the provider creates, making it easier to implement a consistent tagging strategy for all of the AWS resources you manage with Terraform.

In this tutorial, you will configure a set of default tags for your AWS resources. Then, you will override those tags for a specific resource. You will also learn how to use the default tags configuration to manage Auto Scaling group tags.

You can complete this tutorial using the same workflow with either Terraform Community Edition or HCP Terraform. HCP Terraform is a platform that you can use to manage and execute your Terraform projects. It includes features like remote state and execution, structured plan output, workspace resource summaries, and more.

This tutorial assumes that you are familiar with the Terraform and HCP Terraform workflows. If you are new to Terraform, complete Get Started tutorials first.

For this tutorial, you will need:
- Clone the example repository for this tutorial, which contains configuration for an EC2 instance and an Auto Scaling group with default tags.
$ git clone https://github.com/hashicorp-education/learn-terraform-aws-default-tags
- Change into the repository directory.
$ cd learn-terraform-aws-default-tags
- Open main.tf to review the example configuration.

The default_tags block in the AWS provider defines Environment and Service tags. The example configuration sets profile = "default", region = "us-east-2", and defines three default tags: Environment = "Test", Service = "Example", HashiCorp-Learn = "aws-default-tags".

Set the TFCLOUDORGANIZATION environment variable to your HCP Terraform organization name. This will configure your HCP Terraform integration.
$ export TF_CLOUD_ORGANIZATION=
Initialize your configuration.

The workflow demonstrates how default tags propagate to EC2 instances while requiring explicit handling for Auto Scaling groups. The impact is that users experience consistent tagging with minimal repetition for most resources, and a deliberate override path for the ASG exception.

Practical patterns for organization wide tagging

The provider level default_tags can be combined with variable driven values to create an organizational standard. The five tag example with Environment, Project, ManagedBy, Team, CostCenter is representative of a typical finance and governance model. The tags are applied automatically to every supported resource, which reduces the chance of untagged resources and supports cost allocation reports in AWS Cost Explorer.

When modules are used, the merge pattern preserves consumer flexibility. A module can define its own internal tags for licensing or documentation URL, while allowing the caller to supply cost centre or compliance tags that take precedence. This creates a hierarchy where organizational mandates are enforced at the provider level, module defaults provide context, and resource level tags allow fine grained exceptions.

The override behavior where resource tags take precedence over provider defaults ensures that a resource can be uniquely identified without disabling the default set. For example, a VPC can keep the provider defaults for Environment and Project, and add Name = "my-vpc-resource" for identification.

Conclusion

Terraform default tags provide a layered mechanism for enforcing consistent metadata across AWS resources while preserving the ability to override at the resource level. The provider level default_tags feature introduced in Terraform AWS Provider v3.38.0 with Terraform 0.12 or later allows a single declaration to propagate to all taggable resources created by that provider, with inheritance into dependent modules. Resource level tags override provider defaults on a key collision basis, and merge functions enable modules to combine internal defaults with caller supplied values.

The Auto Scaling Group exception remains a distinct operational consideration. Because of the dynamic nature of Auto Scaling Groups, default tags are not applied automatically and tags must be configured explicitly on the resource or via launch template settings. Reprovisioning with terraform apply -replace is presented as a way to apply corrected tags to existing ASG resources.

External systems that modify tags outside of Terraform introduce drift scenarios that must be managed through policy and lifecycle decisions. The tagging model described supports cost management, access control, and operational visibility by making consistent tagging the default rather than an afterthought. The tutorial workflow with HCP Terraform integration shows how the configuration can be exercised in practice using the learn-terraform-aws-default-tags repository, with Environment and Service tags as the baseline example.

Sources

  1. Terraform Tags
  2. Default tags in the Terraform AWS provider
  3. Manage AWS tagging standards with Terraform default tags
  4. Terraform AWS default and override tags
  5. AWS Default Tags Tutorial

Related Posts