Baeldung Pro – Ops – NPI EA (cat = Baeldung on Ops)
announcement - icon

Learn through the super-clean Baeldung Pro experience:

>> Membership and Baeldung Pro.

No ads, dark-mode and 6 months free of IntelliJ Idea Ultimate to start with.

Partner – Orkes – NPI EA (cat=Kubernetes)
announcement - icon

Modern software architecture is often broken. Slow delivery leads to missed opportunities, innovation is stalled due to architectural complexities, and engineering resources are exceedingly expensive.

Orkes is the leading workflow orchestration platform built to enable teams to transform the way they develop, connect, and deploy applications, microservices, AI agents, and more.

With Orkes Conductor managed through Orkes Cloud, developers can focus on building mission critical applications without worrying about infrastructure maintenance to meet goals and, simply put, taking new products live faster and reducing total cost of ownership.

Try a 14-Day Free Trial of Orkes Conductor today.

1. Introduction

In infrastructure provisioning with Terraform, tags play a crucial role in organizing and managing cloud resources. Tags serve as key-value pairs that help group resources by environment, project, owner, or any other business context. While tags and tags_all attributes deal with tags, Terraform introduces a subtle but important distinction between the two.  Understanding tags and tags_all behavior is crucial for maintaining consistency, avoiding configuration drift, and controlling inherited metadata.

In this tutorial, we’ll explore the differences between tags and tags_all in Terraform, how each one works, and when to use them.

2. What Are Tags in Terraform

In Terraform, tags define metadata for cloud resources. They consist of key-value pairs and commonly apply to resources on platforms like AWS, Azure, GCP, and others. In practice, tags help associate additional context with a resource. For example, we can indicate an environment as dev or prod, using tags.

Additionally, Terraform configures tags within a resource block as part of its arguments. In practice, defining a tags argument explicitly instructs the provider to associate the specified key-value pairs with the resource during creation or update. This tagging mechanism is provider-aware, and many cloud providers automatically expose tags or equivalent attributes for resource metadata. When tags are used correctly, they help streamline automation, governance, and monitoring workflows across infrastructure environments.

3. Understanding tags

The tags attribute in Terraform is used to assign custom metadata to cloud resources.  When used in a resource block in Terraform configuration, it instructs the specified provider to attach the set of tags during resource creation or update. For example, let’s assume we want to provision an EC2 instance with Terraform:

resource "aws_instance" "web_server" {
  ami           = "ami-0c55b159cbfafe1f0"
  instance_type = "t2.micro"

  tags = {
    Name        = "web-server"
    Environment = "staging"
    Owner       = "infra-team"
  }
}

In this configuration, Terraform instructs the AWS provider to attach three tags (Name, Environment, and Owner) to the provisioned EC2 instance. These tags used in this example explicitly define the resource categorization (web-server), environment segregation (staging), and ownership tracking (infra-team).

It’s important to note, tags blocks are only applied to a specific resource. Furthermore, tags don’t automatically inherit tags from higher levels, like provider blocks or modules. As a result, we gain precision and control by using tags, but we must manually declare them for each resource to maintain consistent tagging across infrastructure components.

4. Understanding tags_all

The tag_all attribute represents the final set of tags applied by a provider to a resource after combining user-defined tags with any default tags at higher levels. Unlike tags attributes, which contain only explicitly declared tags within a resource block, tags_all captures both explicitly defined and inherited tags.

Let’s consider a practical example using the AWS provider to provision resources:

provider "aws" {
  region = "us-east-1"

  default_tags {
    tags = {
      Project     = "CustomerPortal"
      Environment = "production"
    }
  }
}

resource "aws_instance" "web_server" {
  ami           = "ami-0c55b159cbfafe1f0"
  instance_type = "t2.micro"

  tags = {
    Name  = "web-server"
    Owner = "infra-team"
  }
}

This configuration consists of a provider-level default_tag defining two tags (Project and Environment), and the EC2 instance defines its own tags block (Name and Owner). Now, when this configuration is applied, Terraform merges the tag definitions to produce the following tags_all output:

{
  Name        = "web-server"
  Owner       = "infra-team"
  Project     = "CustomerPortal"
  Environment = "production"
}

As seen, tags_all reflects the final set of tags after Terraform merges user‑defined tags with any defaults at the provider or module level. However, the AWS provider (v3 and newer) treats tags_all as a computed attribute, which evaluates its value during the plan phase rather than reading it directly from the configuration.

Additionally, tags_all can never be declared inside a resource block. Instead, the attribute can still drive drift detection and sometimes trigger plan diffs if the default tags change. Consequently, tags_all is effectively read-only from a configuration standpoint. Yet, Terraform still reconciles any discrepancies it detects at apply time.

5. Dynamic and Conditional Tagging

We can add or omit tags based on variables, locals, or expressions with Terraform’s interpolation syntax. First, let’s consider an example of dynamic tag values:

variable "env" {
  type = string
  default = "dev"
}

resource "aws_s3_bucket" "assets" {
  bucket = "assets-${var.env}"
  tags = {
    Environment = var.env
    Owner       = "frontend-team"
  }
}

In this example, the Environment tag dynamically reflects the value of the env variable. This approach enables consistent tagging across environments while using a single module or resource definition.

Additionally, let’s also consider an example that shows conditional tagging:

variable "is_production" {
  type    = bool
  default = false
}

locals {
  cost_center = var.is_production ? "CC-1001" : null
}

resource "aws_instance" "web" {
  ami           = "ami-0c55b159cbfafe1f0"
  instance_type = "t3.micro"

  tags = {
    Name        = "web-${var.is_production ? "prod" : "test"}"
    CostCenter  = local.cost_center
  }
}

In this example, the configuration demonstrates how to include a CostCenter tag only when provisioning production resources conditionally. Here, Terraform automatically excludes any tag with a null value, resulting in a clean and accurate tag set.

6. Differences Between tags and tags_all

Let’s look at the key differences between tags and the tags_all attribute in Terraform:

tags tags_all
It’s explicitly defined by the user within a resource block It’s automatically computed by Terraform
It specifies only the tags assigned directly to the resource It represents the complete set of tags, including inherited default tags and explicit tags
Does not inherit tags from provider or module-level defaults It inherits and merges tags from provider-level or module-level default_tags configurations
It’s used when specific tags need to be applied to individual resources It’s used internally by Terraform to calculate the full tag set during planning and applying
Terraform applies only these tags to the resource explicitly Terraform computes this attribute to ensure tag consistency and track changes effectively
It’s visible in the configuration and reflected directly in the plan It’s visible in the plan output but not declared in the configuration
It has fine-grained control over resource-specific tagging It’s used for auditing, drift detection, and consistency enforcement with inherited tags
If a key overlaps with a provider‑level default tag, the resource‑level value wins Shows the resolved value after Terraform applies resource beats the default precedence

The table summarizes the differences between tags and tags_all.

Furthermore, when a key exists in both a default tag set and a resource-specific tags block, Terraform uses the value from the tags block. tags_all then records that resolved value, ensuring the resource’s explicit intent always takes precedence.

7. Conclusion

In this article, we examined how Terraform handles tagging through the tags and tags_all attributes. While tags give us explicit control over resource-level metadata, tags_all provides a complete view by including both user-defined and inherited tags.

Understanding their differences helps maintain consistency, enforce tagging standards, and avoid configuration drift.