Azurerm_linux_virtual_machine getting recreated on every apply

Hello all,

we are using Terraform v0.12.29 to manage a group of VMs on Azure using azurerm 2.10.0.

Whenever we run “apply”, Terraform wants to recreate the VMs:

It seems to me, that this is related to the public and private IPs, since these are marked with “-”.

The configuration of the public IP and network interface looks like this:

resource "azurerm_public_ip" "pip" {
     count               = var.public_ip ? 1 : 0
  name                = "${local.name_prefix}-pip"
  location            = var.location
  resource_group_name = var.resource_group_name
  #allocation_method   = "Dynamic"
  allocation_method   = var.public_ip_dynamic ? "Dynamic" : "Static"
  sku                 = "Basic"

  tags = var.common_tags


resource "azurerm_network_interface" "nic" {
  name                = "${local.name_prefix}-nic"
  location            = var.location
  resource_group_name = var.resource_group_name
  count = var.nic == [1] ? 1:0
  ip_configuration {
    name                          = "nic-config"
    subnet_id                     = var.subnet_id
    private_ip_address_allocation = "Dynamic"
    public_ip_address_id          = var.public_ip ? azurerm_public_ip.pip[0].id : null

  tags = var.common_tags

resource "azurerm_linux_virtual_machine" "vm" {
  name                          = "${local.name_prefix}-vm"
  location                      = var.location
  resource_group_name           = var.resource_group_name
  network_interface_ids         = var.nic == [1] ? [azurerm_network_interface.nic[0].id] : var.nic
  size                          = var.vm_size
  admin_username                = var.admin_username
  computer_name                 = "${local.name_prefix}-host"
  custom_data                   = var.custom_data

Anyone have an idea what we may be doing wrong?

Best regards and thanks in advance!

Hi @SBiesinger,

I’m not very familiar with Azure in particular so I’m not sure exactly what might cause this, but from reading your screenshot of the plan output I notice that the only attribute showing a change that corresponds with an argument in your configuration is custom_data; the others are all attributes the provider populates itself after the apply operation succeeds.

Unfortunately, the provider has marked custom_data as being sensitive and so we can’t see exactly what has changed. However, a typical reason for an argument to show changes even though nothing has changed in the configuration is if the remote API is normalizing the data in some way that the provider isn’t accounting for.

It’s generally a provider’s responsibility to decide for each change in the configuration whether it represents normalization (a different way of writing the same thing), or a meaningful change (the new value means something different), but sometimes the remote API has normalization behavior that the provider doesn’t know about yet and so it can be mis-classified as a meaningful change.

From referring to the Azure provider documentation I see that custom_data is commonly a multi-line string. Therefore I wonder if the Azure API is normalizing the string you’ve given in a way that the Azure provider doesn’t expect, such as converting the newline characters from the Unix standard to the Windows standard or vice-versa.

The configuration snippet you shared doesn’t show how var.custom_data is populated so I can’t be sure if this is the correct explanation, but I hope it gives you a useful lead for further debugging. If you have access to the raw Terraform state snapshots for this configuration you could also look there to see what value is stored for custom_data and see if it exactly matches your var.custom_data value. The state snapshots are in JSON format, so you should hopefully be able to just search in there for custom_data and find the relevant attribute.

Hi @apparentlymart,

thank you very much, custom_data was in fact causing the issue.

For anyone else coming across this problem, it seems to be related to these issues:

The accepted workaround until the release of Terraform v0.13 seems to be to ignore changes to custom_data as per so:

lifecycle {
ignore_changes = [custom_data]

once again many thanks to @apparentlymart for the quick an helpful reply!