Azure / terraform-provider-azapi

Terraform provider for Azure Resource Manager Rest API
https://registry.terraform.io/providers/Azure/azapi/latest
Mozilla Public License 2.0
168 stars 43 forks source link

```ignore_body_changes``` does not work with imported resources #412

Open NordbergDK opened 4 months ago

NordbergDK commented 4 months ago

ignore_body_changes is not working for resources that are imported. None of the properties put into ignore_body_changes is ignored.

Terraform v1.7.3
on windows_amd64

import {
  id = "/subscriptions/_???_/resourceGroups/_???_/providers/Microsoft.Network/virtualNetworks/vnet-c"
  to = azapi_resource.vnet
}

locals {
  type      = "Microsoft.Network/virtualNetworks@2023-09-01"
  name      = var.name
  location  = var.location
  parent_id = format("/subscriptions/%s/resourceGroups/%s", var.subscription_id, var.resource_group_name)
  tags      = var.tags

  properties_raw = {
    addressSpace         = {
      addressPrefixes = var.address_prefixes
    }

    bgpCommunities       = var.bgp_communities
    ddosProtectionPlan   = var.ddos_protection_plan
    dhcpOptions          = var.dhcp_options
    enableDdosProtection = var.enable_ddos_protection
    enableVmProtection   = var.enable_vm_protection
    encryption           = var.encryption
    flowTimeoutInMinutes = var.flow_timeout_in_minutes
    ipAllocations        = var.ip_allocations
  }

  properties_filtered = [
    for k,v in local.properties_raw : {
      key = k
      value = v
    } if v != null
  ]

  properties = zipmap([for k in local.properties_filtered : k.key],[for v in local.properties_filtered : v.value])
}

resource "azapi_resource" "vnet" {
  type = local.type
  name = local.name
  location = local.location
  parent_id = local.parent_id
  tags = local.tags
  body = jsonencode({
    properties = local.properties
  })
  ignore_body_changes = [
    "properties.subnets",
    "properties.virtualNetworkPeerings"
  ]

  lifecycle {
    ignore_changes = [ timeouts ]
  }
}
# (imported from "/subscriptions/_???_/resourceGroups/_???_/providers/Microsoft.Network/virtualNetworks/vnet-c")
  ~ resource "azapi_resource" "vnet" {
      ~ body                      = jsonencode(
          ~ {
              ~ properties = {
                    addressSpace           = {
                        addressPrefixes = [
                            "10.0.10.0/24",
                        ]
                    }
                    enableDdosProtection   = false
                    encryption             = {
                        enabled     = false
                        enforcement = "AllowUnencrypted"
                    }
                  - subnets                = [
                      - {
                          - id         = "/subscriptions/_???_/resourceGroups/_???_/providers/Microsoft.Network/virtualNetworks/vnet-c/subnets/test"
                          - name       = "test"
                          - properties = {
                              - addressPrefix                     = "10.0.10.0/24"
                              - delegations                       = []
                              - privateEndpointNetworkPolicies    = "Disabled"
                              - privateLinkServiceNetworkPolicies = "Enabled"
                              - serviceEndpoints                  = []
                            }
                          - type       = "Microsoft.Network/virtualNetworks/subnets"
                        },
                    ]
                  - virtualNetworkPeerings = []
                }
            }
        )
        id                        = "/subscriptions/_???_/resourceGroups/_???_/providers/Microsoft.Network/virtualNetworks/vnet-c"
      + ignore_body_changes       = [
          + "properties.subnets",
          + "properties.virtualNetworkPeerings",
        ]
        ignore_casing             = false
        ignore_missing_property   = true
        location                  = "westeurope"
        name                      = "vnet-c"
      ~ output                    = jsonencode({}) -> (known after apply)
        parent_id                 = "/subscriptions/_???_/resourceGroups/_???_"
        removing_special_chars    = false
        schema_validation_enabled = true
        tags                      = {}
        type                      = "Microsoft.Network/virtualNetworks@2023-09-01"

        timeouts {}
    }
Plan: 1 to import, 0 to add, 1 to change, 0 to destroy.

So as you can see terraform wants to delete all the subnets, even if the property is not provided, and it is listed in ignore_body_changes. If I apply, the subnet that exists is in fact deleted. Once the import has run, I can manually add the subnet again in the portal, and terraform will ignore the change. So this only applies to resources at the time they are imported. Let me know if you require any more evidence. I have the setup to perform all the tests you like.

ms-henglu commented 2 months ago

Hi @NordbergDK ,

Thank you for taking time to report this issue!

Yes, the ignore_body_changes is a provider feature which is used to simulate the native lifecycle.ignore_changes feature. And the limitation is that it only works after the resource is imported and the ignore_body_changes is applied.

We recently released v1.13.1 which supports dynamic properties, and it enables the native lifecycle.ignore_changes feature. And it could suppress the changes before the resource is imported.

More details: https://techcommunity.microsoft.com/t5/azure-tools-blog/announcing-azapi-dynamic-properties/ba-p/4121855