> ## Documentation Index
> Fetch the complete documentation index at: https://notes.kodekloud.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Resource Attributes and Dependencies

> Explains how Terraform uses resource attribute references and depends_on to build dependency graphs and control Azure resource creation order, avoiding race conditions

This article explains how Terraform resources relate to each other, how values flow between resources, and how Terraform decides the order in which resources are created. Understanding these concepts is essential for reliable Azure deployments: most resources are not standalone—a subnet belongs to a VNet, a storage account lives in a resource group, and VMs depend on networking, disks, and identities.

After reading this article you will clearly understand:

* How Terraform builds its dependency graph.
* When dependencies are discovered implicitly (via attribute references).
* When you must be explicit using `depends_on`.

Basic VNet + Subnet example

<Frame>
  /images/Python\_Basics/section-name/Comments/frame\_100.jpg
</Frame>

A virtual network (VNet) exposes attributes such as `name`, `address_space`, `location`, and `resource_group_name`. A subnet that belongs to that VNet should reference the VNet resource rather than hard-coding values. Referencing resource attributes lets Terraform both pass values and infer the ordering between resources (an implicit dependency).

Example — VNet and Subnet with attribute references (implicit dependency):

```hcl theme={null}
resource "azurerm_resource_group" "rg" {
  name     = var.rg
  location = var.location
}

resource "azurerm_virtual_network" "vnet" {
  name                = var.vnet
  address_space       = ["10.0.0.0/16"]
  location            = azurerm_resource_group.rg.location   # implicit dependency on resource group
  resource_group_name = azurerm_resource_group.rg.name       # implicit dependency on resource group
}

resource "azurerm_subnet" "subnet" {
  name                 = var.subnet
  resource_group_name  = azurerm_resource_group.rg.name      # implicit dependency on resource group
  virtual_network_name = azurerm_virtual_network.vnet.name  # implicit dependency on vnet
  address_prefixes     = ["10.0.1.0/24"]
}
```

Key points

* When the `azurerm_subnet` block references `azurerm_virtual_network.vnet.name`, Terraform obtains that attribute value and also infers that the subnet depends on the VNet. This is an implicit dependency.
* Attribute references both pass configuration values and create ordering information in Terraform’s dependency graph.
* If you hard-code values or rely only on variables (for example `resource_group_name = var.rg`), Terraform cannot infer a dependency between the resources and may attempt parallel creation.

Why reference the resource group in each resource?

* Terraform can create independent-looking resources in parallel. If a resource must be created inside a resource group but you supply only a variable (which is known at plan time), Terraform has no relationship to infer and may run creations concurrently. Referencing the resource group's attributes (for example `azurerm_resource_group.rg.name`) makes the dependency explicit to Terraform (implicitly).

Implicit dependencies are sufficient in most scenarios

<Frame>
  /images/Python\_Basics/section-name/Comments/frame\_100.jpg
</Frame>

Consider a resource group and a storage account; because the storage account references attributes from the resource group, Terraform ensures the resource group is created first.

```hcl theme={null}
resource "azurerm_resource_group" "rg" {
  name     = "my-workshop-rg-wus"
  location = "West US"
}

resource "azurerm_storage_account" "sa" {
  name                     = "saworkshopazuretf098"
  resource_group_name      = azurerm_resource_group.rg.name   # implicit dependency on rg
  location                 = azurerm_resource_group.rg.location
  account_tier             = "Standard"
  account_replication_type = "LRS"
}
```

When depends\_on is required (explicit dependency)

* Use `depends_on` when a logical dependency exists but Terraform cannot infer it through attribute references.
* Common cases requiring `depends_on`: role assignments, policy assignments, diagnostic settings, private endpoints, resource associations, or provider behaviors where an ID is returned but the resource is not fully ready for dependent operations.

Example: NSG-to-subnet association that needs `depends_on` to handle Azure eventual consistency:

```hcl theme={null}
resource "azurerm_network_security_group" "nsg" {
  name                = var.nsg
  resource_group_name = azurerm_resource_group.rg.name
  location            = azurerm_resource_group.rg.location
}

resource "azurerm_subnet" "subnet" {
  name                 = var.subnet
  resource_group_name  = azurerm_resource_group.rg.name
  virtual_network_name = azurerm_virtual_network.vnet.name
  address_prefixes     = ["10.0.1.0/24"]
}

resource "azurerm_subnet_network_security_group_association" "subnet_nsg_assoc" {
  subnet_id                 = azurerm_subnet.subnet.id
  network_security_group_id = azurerm_network_security_group.nsg.id

  # Force ordering when Azure's eventual consistency requires both resources to be fully ready
  depends_on = [
    azurerm_subnet.subnet,
    azurerm_network_security_group.nsg,
  ]
}
```

<Callout icon="lightbulb" color="#1CB2FE">
  Use `depends_on` sparingly — only when Terraform cannot infer the dependency. Overusing `depends_on` makes configurations harder to read and less flexible. Always ask: is an attribute reference already providing the dependency?
</Callout>

A typical example configuration (main.tf)

<Frame>
  /images/Python\_Basics/section-name/Comments/frame\_100.jpg
</Frame>

A compact main configuration that creates a resource group, virtual network, subnet, network security group (NSG), and the subnet-to-NSG association. This demonstrates implicit references plus a `depends_on` used for the association.

```hcl theme={null}
# main.tf
resource "azurerm_resource_group" "rg" {
  name     = var.rg
  location = var.location
}

resource "azurerm_virtual_network" "vnet" {
  name                = var.vnet
  address_space       = ["10.0.0.0/16"]
  location            = azurerm_resource_group.rg.location
  resource_group_name = azurerm_resource_group.rg.name
}

resource "azurerm_subnet" "subnet" {
  name                 = var.subnet
  resource_group_name  = azurerm_resource_group.rg.name
  virtual_network_name = azurerm_virtual_network.vnet.name
  address_prefixes     = ["10.0.1.0/24"]
}

resource "azurerm_network_security_group" "nsg" {
  name                = var.nsg
  resource_group_name = azurerm_resource_group.rg.name
  location            = azurerm_resource_group.rg.location
}

resource "azurerm_subnet_network_security_group_association" "subnet_nsg_assoc" {
  subnet_id                 = azurerm_subnet.subnet.id
  network_security_group_id = azurerm_network_security_group.nsg.id

  # Optional: ensure the subnet and NSG are fully provisioned before making the association
  depends_on = [
    azurerm_subnet.subnet,
    azurerm_network_security_group.nsg
  ]
}
```

Provider and variables files

<Frame>
  /images/Python\_Basics/section-name/Comments/frame\_100.jpg
</Frame>

Minimal `provider.tf` and `variables.tf` for the example:

```hcl theme={null}
# provider.tf
terraform {
  required_providers {
    azurerm = {
      source  = "hashicorp/azurerm"
      version = ">= 3.0.0"
    }
  }
}

provider "azurerm" {
  features {}
}
```

```hcl theme={null}
# variables.tf
variable "rg" {
  type        = string
  description = "Resource group name"
}

variable "location" {
  type        = string
  description = "Azure location"
}

variable "vnet" {
  type        = string
  description = "Virtual network name"
}

variable "subnet" {
  type        = string
  description = "Subnet name"
}

variable "nsg" {
  type        = string
  description = "Network Security Group name"
}
```

Example `terraform.tfvars` used to pass values:

```hcl theme={null}
# terraform.tfvars
rg       = "rg-tf-dependencies-01"
location = "eastus"
vnet     = "vnet-tf-dependencies-01"
subnet   = "subnet-tf-dependencies-01"
nsg      = "nsg-tf-dependencies-01"
```

Commands: init, plan, apply

<Frame>
  /images/Python\_Basics/section-name/Comments/frame\_100.jpg
</Frame>

From the directory with your Terraform files:

```bash theme={null}
terraform init
terraform plan -out tfplan
terraform apply --auto-approve tfplan
```

Example (cleaned) output showing correct ordering:

```plaintext theme={null}
azurerm_resource_group.rg: Creating...
azurerm_resource_group.rg: Creation complete after 25s [id=/subscriptions/.../resourceGroups/rg-tf-dependencies-01]
azurerm_virtual_network.vnet: Creating...
azurerm_network_security_group.nsg: Creating...
azurerm_virtual_network.vnet: Creation complete after 4s [id=/subscriptions/.../virtualNetworks/vnet-tf-dependencies-01]
azurerm_network_security_group.nsg: Creation complete after 6s [id=/subscriptions/.../networkSecurityGroups/nsg-tf-dependencies-01]
azurerm_subnet.subnet: Creating...
azurerm_subnet.subnet: Creation complete after 7s [id=/subscriptions/.../subnets/subnet-tf-dependencies-01]
azurerm_subnet_network_security_group_association.subnet_nsg_assoc: Creating...
azurerm_subnet_network_security_group_association.subnet_nsg_assoc: Creation complete after 5s
Apply complete! Resources: 5 added, 0 changed, 0 destroyed.
```

Explanation of the observed order

* The resource group is created first because other resources reference it.
* The VNet and NSG were created in parallel after the resource group because both depend only on the resource group (implicit dependency).
* The subnet waited for the VNet to be created (implicit dependency via `virtual_network_name`).
* The subnet-to-NSG association was applied last; even with attribute references, a `depends_on` can be used to guard against Azure timing issues.

Quick reference table: dependency discovery

| Resource(s)                      | Example attribute reference                                |                                Dependency discovered? | Notes                                                                          |
| -------------------------------- | ---------------------------------------------------------- | ----------------------------------------------------: | ------------------------------------------------------------------------------ |
| Resource Group → Storage Account | `resource_group_name = azurerm_resource_group.rg.name`     |                                        Yes (implicit) | Attribute reference creates ordering.                                          |
| VNet → Subnet                    | `virtual_network_name = azurerm_virtual_network.vnet.name` |                                        Yes (implicit) | Standard pattern for network resources.                                        |
| Subnet NSG Association           | `subnet_id = azurerm_subnet.subnet.id`                     | Often yes (implicit), but may still need `depends_on` | Use `depends_on` when Azure returns IDs before the resource is actually ready. |
| Role or Policy Assignments       | `principal_id = ...` (no direct attribute linking)         |                            No (requires `depends_on`) | Provider-specific eventual consistency can require explicit ordering.          |

<Callout icon="warning" color="#FF6B6B">
  Do not overuse `depends_on`. Using it everywhere defeats Terraform’s automatic dependency tracking, reduces parallelism, and makes configurations harder to maintain. Use `depends_on` only when you observe failures caused by provider eventual consistency or when there is no attribute reference to express the relationship.
</Callout>

Summary

* Attribute references (for example `azurerm_resource_group.rg.name` or `azurerm_virtual_network.vnet.id`) allow values to flow between resources and automatically create implicit dependencies.
* Terraform builds a dependency graph from those references and uses it to determine the correct creation order.
* Use `depends_on` only when Terraform cannot infer a dependency from attribute references (for example, associations or operations that require a resource to be fully provisioned even when an ID is already available).
* Avoid overusing `depends_on` to keep configurations readable, maintainable, and parallel where possible.

Understanding these mechanisms prevents race conditions and broken deployments in real production environments.

Links and references

* [Terraform CLI Docs](https://www.terraform.io/docs/cli)
* [Terraform Dependency Graph](https://www.terraform.io/docs/internals/graph.html)
* [Azure Provider (azurerm) on Terraform Registry](https://registry.terraform.io/providers/hashicorp/azurerm/latest)
* [Azure Resource Manager documentation](https://learn.microsoft.com/azure/azure-resource-manager/)

<CardGroup>
  <Card title="Watch Video" icon="video" cta="Learn more" href="https://learn.kodekloud.com/user/courses/terraform-on-azure/module/866718d2-695e-4ee4-b25d-1aab3b014e85/lesson/70d3a770-63fb-430a-8bbc-278664ed87b1" />
</CardGroup>
