Saturday, 22 August 2026

Preventing Perpetual Diffs with Nested Blocks in Terraform Resources

 We may run into a situation where Terraform shows changes on every terraform plan, even though we have not changed our Terraform code. As an example, this can happen with nested service_endpoint blocks in an Azure subnet when the order in Terraform is different from the order returned by Azure. Terraform then thinks the resource has changed and shows an update on every plan. In this example, we can see how the problem looks and how changing the order of the blocks can fix the perpetual diff.


Root Cause

The Azure API returns the service_endpoint blocks in a different order from the order defined in our Terraform code. Terraform compares the order of these nested blocks and therefore detects changes even though the actual service endpoints have not changed.

Terraform code was defined as below

# AKS Subnet
resource "azurerm_subnet" "aks" {
  name                              = "mydemo-aks-snet"
  resource_group_name               = "mydemo-rg"
  virtual_network_name              = "mydemo-vnet"
  address_prefixes                  = ["${var.SUBNET_CIDR_AKS}"]
  private_endpoint_network_policies = "Enabled"
  default_outbound_access_enabled   = true

  service_endpoint {
    service = "Microsoft.AzureActiveDirectory"
  }
  service_endpoint {
    service = "Microsoft.AzureCosmosDB"
  }
  service_endpoint {
    service = "Microsoft.EventHub"
  }
  service_endpoint {
    service = "Microsoft.KeyVault"
  }
  service_endpoint {
    service = "Microsoft.ServiceBus"
  }
  service_endpoint {
    service = "Microsoft.Sql"
  }
  service_endpoint {
    service = "Microsoft.Storage"
  }
  service_endpoint {
    service = "Microsoft.Web"
  }
}

Observation

After running terraform plan several times, we noticed that Azure consistently returned the service endpoints in the same order. The order was different from the order in our Terraform configuration, but it remained consistent across plan runs.

Fix

We changed the order of the service_endpoint blocks in Terraform to match the order consistently returned by the Azure API. After this change, Terraform no longer detected changes on every plan.

# AKS Subnet
resource "azurerm_subnet" "aks" {
  name                              = "mydemo-aks-snet"
  resource_group_name               = "mydemo-rg"
  virtual_network_name              = "mydemo-vnet"
  address_prefixes                  = ["${var.SUBNET_CIDR_AKS}"]
  private_endpoint_network_policies = "Enabled"
  default_outbound_access_enabled   = true

  # Order matches what the Azure API returns on read to avoid perpetual plan diffs
  service_endpoint {
    service = "Microsoft.KeyVault"
  }
  service_endpoint {
    service = "Microsoft.AzureCosmosDB"
  }
  service_endpoint {
    service = "Microsoft.ServiceBus"
  }
  service_endpoint {
    service = "Microsoft.AzureActiveDirectory"
  }
  service_endpoint {
    service = "Microsoft.Web"
  }
  service_endpoint {
    service = "Microsoft.EventHub"
  }
  service_endpoint {
    service = "Microsoft.Storage"
  }
  service_endpoint {
    service = "Microsoft.Sql"
  }
}


No comments:

Popular Posts