Default tags in Terraform represent a mechanism for applying consistent metadata across all resources created by a particular provider configuration without repeating the same key-value pairs in every resource block. Each tag consists of a unique key and its corresponding value. For example:
tags = {
Environment = "Development"
Owner = "Luke Skywalker"
Department = "Jedi Order"
}
The structure of a tag map is simple, but the operational consequences of inconsistent tagging are significant. Organizations that allow resources to be created without mandated metadata accumulate untagged EC2 instances that consume budget with no owner, resources that cannot be attributed to teams, costs that cannot be allocated, and compliance gaps that cannot be closed. Tagging is often treated as an afterthought, and once a resource exists without the required keys, retroactive remediation requires manual discovery and change management.
Terraform default tags address this problem at the infrastructure level. 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 higher-level definition creates a single source of truth for organizational metadata, reducing the risk of human omission during resource authoring and enabling policy enforcement through Terraform validation and organizational policies.
Default Tag Definition Through Variables
Variable-based default tags allow a common tag map to be defined once and referenced across many resources.
variable "common_tags" {
type = map(string)
default = {
Environment = "Development"
}
}
tags = var.common_tags
Defining tags in the variable defaults centralizes the metadata. The impact for the user is a reduction in copy-paste errors and a guarantee that any resource that references var.common_tags inherits the current definition without manual updates. When the environment changes from Development to Production, updating the variable default propagates to all dependent resources on the next plan.
The contextual layer connects variable defaults to module design. Modules can expose a common_tags variable with a default map, and consumers of the module inherit the defaults unless they explicitly override them. This pattern makes the overhead of setting up metadata within a Terraform module practical, allowing licensing, documentation URL or compliance-driven tags to be defined inside of the module itself.
Provider-Level Default Tags Configuration
The HashiCorp Terraform AWS Provider contains over 700 resources to standardize AWS infrastructure for configuration in accordance with best practices. One of the most common requests has been for the ability to define default tags at the provider level of your Terraform configuration. As of v3.38.0 of the Terraform AWS provider, you are able to define default tags for all resources except Auto Scaling Groups.
Using default tags at the provider level creates inheritance across the entire configuration. You can set default tags in the provider block of your Terraform configuration. Any tags set here will also be 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.
In order to configure default tags you will need:
- Terraform 0.12 or later
- Terraform AWS Provider v3.38.0 or later
Provider configuration example:
provider "aws" {
default_tags {
tags = {
Environment = "Test"
Owner = "TFProviders"
Project = "Test"
}
}
}
Resource-level example with 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 provider block defines the baseline metadata. The VPC and subnet resources each define a Name tag. The default tags Environment, Owner and Project are applied automatically in addition to the resource-specific Name tag. The impact is that every supported AWS resource created by that provider automatically inherits these tags without explicit repetition.
A more comprehensive baseline often includes cost and ownership metadata:
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
}
}
}
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.
Inheritance Behavior and Precedence Rules
Inheritance from the provider block flows downward to resources and to modules. Setting default tags at the provider level will not supersede tags set on individual resources as resource tags take precedence. This precedence rule ensures that a specific resource can refine or replace a default value without modifying the provider configuration.
The real-world consequence is safe experimentation. A team can define organization-wide defaults for Environment, Project, ManagedBy, Team and CostCenter, and individual resource authors can add Name or Purpose without fear of breaking the baseline. If a resource defines Environment = "Production" explicitly, that value overrides the provider default of Environment = "Test" for that resource only.
The contextual layer connects precedence to module consumption. Because tags set at the provider level are inherited by dependent Terraform modules, a module does not need to pass tags to each sub-module contained within. The module author can rely on the provider defaults to supply mandated metadata, reducing interface complexity.
Overriding Default Tags at Resource Level
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 is straightforward. If default tags are applied via the provider or module, you can add or change specific tags directly within the resource block. The explicit resource tags are merged with defaults, with resource values taking precedence.
Merge default and custom tags is achieved with Terraform functions like merge(). The merge function combines multiple maps, and tags are typically defined as a map. Using merge allows priority to be given to custom tags while retaining defaults for keys not explicitly overridden.
variable "common_tags" {
type = map(string)
default = {
Environment = "Development"
}
}
resource "aws_instance" "my_instance" {
tags = merge(var.common_tags, {
Name = "My Web Server"
})
}
The merge call ensures Environment = "Development" is present from the variable, and Name = "My Web Server" is added. If the resource map also contained Environment, the resource value would win.
The impact for operations is flexibility without duplication. 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, the ability to override at the resource level satisfies both mandate and customization.
Merging Default and Custom Tags with Merge Function
One useful application of the merge function in Terraform is adding additional tags to resources to combine with your default set of tags. The merge() function combines multiple maps, and tags are typically defined as a map.
In module design, the pattern 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.
This pattern allows the module to define its own list of tags that can be overwritten by the consumer of the module as needed. The overhead of setting up metadata within a Terraform module becomes practical, allowing for things like licensing, documentation URL or compliance-driven tags to be defined inside of the module itself while still permitting consumer overrides.
Ignoring Tag Drift from External Systems
External systems can interact with resource tags. Configuration Management Databases or the cloud may add auto-generated tags for certain resources, such as Azure Databricks. When Terraform detects a difference between state and actual tags, it may attempt to revert the external change.
To prevent constant drift, Terraform supports ignoring specific tag keys:
ignore_tags = [
"CostCenter",
]
The configuration option ignoretags allows Terraform to ignore changes to tags with this key. The impact is stability in environments where external automation manages certain tags. Without ignoretags, plans would perpetually show changes and apply operations that conflict with external systems.
Auto Scaling Groups Exception
Due to the dynamic nature of Auto Scaling Groups, they behave differently than other AWS resources. The AWS provider default_tags feature does not apply to Auto Scaling Groups.
The tags include the default ASG tags but not the default tags from your provider configuration. Example tag output for an Auto Scaling Group shows system tags:
{
"Key": "aws:ec2launchtemplate:version",
"Value": "1"
},
{
"Key": "aws:autoscaling:groupName",
"Value": "terraform-20210720164457433400000003"
}
The tags include default ASG tags but not the default tags from your provider configuration. To reprovision an Auto Scaling group and launch a new instance with the appropriate tags, the -replace option is used:
terraform apply -replace aws_autoscaling_group.example
Terraform used the selected providers to generate the following execution plan.
The real-world consequence is that teams must configure tags on the awsautoscalinggroup resource itself, typically via launch template tags and tag propagation settings. Failing to do so results in instances launched by the ASG lacking the organization-wide defaults, creating a blind spot in cost allocation and compliance reporting.
Module-Level Default Tags and Consumer Override
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.
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. 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.
The pattern creates a tags variable for consumer input and a local known as module_tags for internal defaults. 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.
The impact is a practical balance between standardization and flexibility. Organizations can mandate CostCenter and Compliance tags inside modules while still allowing teams to override Project or Team values for specific deployments.
Provider Differences and Tagging Completeness
Tag implementation varies significantly across cloud providers. Understanding how each provider handles defaults, inheritance, and naming constraints prevents subtle misconfigurations that are easy to miss in a plan.
AWS has the most complete tagging support of the three major providers. The completeness manifests as provider-level default_tags, inheritance to modules, and resource-level precedence. The operational benefit is consistent metadata across more than 700 resources with minimal duplication.
Practical Configuration Examples
The default_tags feature, combined with validation and organizational policies, solves the problem at the infrastructure level. Let's set it up properly.
Provider baseline with variables:
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.
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.
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.
The configuration establishes a single provider block that propagates metadata. Teams benefit from reduced boilerplate, auditors benefit from consistent keys, and finance benefits from reliable cost allocation.
Conclusion
Default tags in Terraform transform tagging from a per-resource manual task into a declarative, inherited policy. Provider-level default_tags in the AWS provider, introduced in v3.38.0, allow Environment, Owner, Project, ManagedBy, Team and CostCenter to propagate automatically to every taggable resource without repetition. Precedence rules ensure resource-specific tags override defaults, while the merge function enables sophisticated composition of variable defaults and consumer overrides inside modules.
The Auto Scaling Group exception remains a critical operational caveat. Because ASGs do not inherit provider defaults, launch template tags and explicit ASG tags must be configured, and the -replace option is required to apply corrected tagging to existing groups. Ignoring external tag drift via ignore_tags prevents plan churn when Configuration Management Databases or cloud systems add auto-generated tags.
The broader impact is organizational control. Consistent tagging enables better auditing, cost allocation and access control. Modules become more practical when they can rely on provider defaults for mandated metadata while still allowing consumer overrides for cost-centre or compliance resources. The combination of provider-level defaults, variable-based common tags, merge-based composition and selective ignore rules creates a dense web of controls that reduces untagged resources, eliminates manual duplication, and sustains compliance across hundreds of AWS resources managed with Terraform.