Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Terraform - Azure Front Door does not handle dynamic blocks correctly - Terraform attempts to alter Front Door when there are no changes #27423

Closed
scott-doyland-burrows opened this issue Jan 6, 2021 · 2 comments

Comments

@scott-doyland-burrows
Copy link

Terraform Version

Terraform v0.14.2
+ provider registry.terraform.io/hashicorp/azurerm v2.41.0

Terraform Configuration Files

The root module which calls the child module:

module "front-door" {
  source                                       = "../../src/frontdoor/1.0.0"
  frontdoor_resource_group_name                = module.resourcegroup.rgname
  frontdoor_name                               = "name4-fd-global-name1"
  frontdoor_loadbalancer_enabled               = true
  backend_pools_send_receive_timeout_seconds   = 240
  enforce_backend_pools_certificate_name_check = false

  frontend_endpoint = [{
    name                              = "name1-name2-com"
    host_name                         = "name1.name2.com"
    custom_https_provisioning_enabled = true
    custom_https_configuration        = { certificate_source = "FrontDoor" }
    session_affinity_enabled          = false
    session_affinity_ttl_seconds      = 0
    waf_policy_link_id                = ""
  },
  {
    name                              = "name4-fd-global-name1"
    host_name                         = "name4-fd-global-name1.azurefd.net"
    custom_https_provisioning_enabled = false
    custom_https_configuration        = { certificate_source = "FrontDoor" }
    session_affinity_enabled          = false
    session_affinity_ttl_seconds      = 0
    waf_policy_link_id                = ""
  }
]

  frontdoor_routing_rule = [{
    name               = "name3api"
    accepted_protocols = ["Https"]
    patterns_to_match  = ["/name3/api","/name3/api/","/name3/api/*"]
    enabled            = true
    configuration      = "Forwarding"
    forwarding_configuration = [{
      backend_pool_name                     = "name3api"
      cache_enabled                         = false
      cache_use_dynamic_compression         = false
      cache_query_parameter_strip_directive = "StripNone"
      custom_forwarding_path                = "/"
      forwarding_protocol                   = "HttpsOnly"
    }]
  },
  {
    name               = "name3importapi"
    accepted_protocols = ["Https"]
    patterns_to_match  = ["/name3/importapi","/name3/importapi/","/name3/importapi/*"]
    enabled            = true
    configuration      = "Forwarding"
    forwarding_configuration = [{
      backend_pool_name                     = "name3importapi"
      cache_enabled                         = false
      cache_use_dynamic_compression         = false
      cache_query_parameter_strip_directive = "StripNone"
      custom_forwarding_path                = "/"
      forwarding_protocol                   = "HttpsOnly"
    }]
  },
  {
    name               = "name3ui"
    accepted_protocols = ["Https"]
    patterns_to_match  = ["/name3/ui","/name3/ui/","/name3/ui/*"]
    enabled            = true
    configuration      = "Forwarding"
    forwarding_configuration = [{
      backend_pool_name                     = "name3ui"
      cache_enabled                         = false
      cache_use_dynamic_compression         = false
      cache_query_parameter_strip_directive = "StripNone"
      custom_forwarding_path                = "/"
      forwarding_protocol                   = "HttpsOnly"
    }]
  }]

  frontdoor_loadbalancer = [{
    name                            = "loadbalancer"
    sample_size                     = 4
    successful_samples_required     = 2
    additional_latency_milliseconds = 0
  }]

  frontdoor_health_probe = [{
    name                = "healthprobe"
    enabled             = true
    path                = "/"
    protocol            = "Https"
    probe_method        = "HEAD"
    interval_in_seconds = 60
  }]

  frontdoor_backend = [{
    name               = "name3api"
    loadbalancing_name = "loadbalancer"
    health_probe_name  = "healthprobe"
    backend = [{
      enabled     = true
      host_header = "name4-app-prod01-name1api-name3.azurewebsites.net"
      address     = "name4-app-prod01-name1api-name3.azurewebsites.net"
      http_port   = 80
      https_port  = 443
      priority    = 1
      weight      = 50
    },
    {
      enabled     = true
      host_header = "name4-app-prod02-name1api-name3.azurewebsites.net"
      address     = "name4-app-prod02-name1api-name3.azurewebsites.net"
      http_port   = 80
      https_port  = 443
      priority    = 2
      weight      = 50
    }]
  },
  {
    name               = "name3importapi"
    loadbalancing_name = "loadbalancer"
    health_probe_name  = "healthprobe"
    backend = [{
      enabled     = true
      host_header = "name4-app-prod01-name1importapi-name3.azurewebsites.net"
      address     = "name4-app-prod01-name1importapi-name3.azurewebsites.net"
      http_port   = 80
      https_port  = 443
      priority    = 1
      weight      = 50
    },
    {
      enabled     = true
      host_header = "name4-app-prod02-name1importapi-name3.azurewebsites.net"
      address     = "name4-app-prod02-name1importapi-name3.azurewebsites.net"
      http_port   = 80
      https_port  = 443
      priority    = 2
      weight      = 50
    }]
  },
  {
    name               = "name3ui"
    loadbalancing_name = "loadbalancer"
    health_probe_name  = "healthprobe"
    backend = [{
      enabled     = true
      host_header = "name4-app-prod01-name1ui-name3.azurewebsites.net"
      address     = "name4-app-prod01-name1ui-name3.azurewebsites.net"
      http_port   = 80
      https_port  = 443
      priority    = 1
      weight      = 50
    },
    {
      enabled     = true
      host_header = "name4-app-prod02-name1ui-name3.azurewebsites.net"
      address     = "name4-app-prod02-name1ui-name3.azurewebsites.net"
      http_port   = 80
      https_port  = 443
      priority    = 2
      weight      = 50
    }]
  }]
}

The child module:

resource "azurerm_frontdoor" "frontdoor" {
  name                                         = var.frontdoor_name
  resource_group_name                          = var.frontdoor_resource_group_name
  enforce_backend_pools_certificate_name_check = var.enforce_backend_pools_certificate_name_check
  load_balancer_enabled                        = var.frontdoor_loadbalancer_enabled
  backend_pools_send_receive_timeout_seconds   = var.backend_pools_send_receive_timeout_seconds

  dynamic "backend_pool_load_balancing" {
    for_each = var.frontdoor_loadbalancer
    content {
      name                            = backend_pool_load_balancing.value.name
      sample_size                     = backend_pool_load_balancing.value.sample_size
      successful_samples_required     = backend_pool_load_balancing.value.successful_samples_required
      additional_latency_milliseconds = backend_pool_load_balancing.value.successful_samples_required
    }
  }

  dynamic "routing_rule" {
    for_each = var.frontdoor_routing_rule
    content {
      name               = routing_rule.value.name
      accepted_protocols = routing_rule.value.accepted_protocols
      patterns_to_match  = routing_rule.value.patterns_to_match
      frontend_endpoints = values({for x, endpoint in var.frontend_endpoint : x => endpoint.name})
      dynamic "forwarding_configuration" {
        for_each = routing_rule.value.configuration == "Forwarding" ? routing_rule.value.forwarding_configuration : []
        content {
          backend_pool_name                     = forwarding_configuration.value.backend_pool_name
          cache_enabled                         = forwarding_configuration.value.cache_enabled
          cache_use_dynamic_compression         = forwarding_configuration.value.cache_use_dynamic_compression
          cache_query_parameter_strip_directive = forwarding_configuration.value.cache_query_parameter_strip_directive
          custom_forwarding_path                = forwarding_configuration.value.custom_forwarding_path
          forwarding_protocol                   = forwarding_configuration.value.forwarding_protocol
        }
      }
      dynamic "redirect_configuration" {
        for_each = routing_rule.value.configuration == "Redirecting" ? routing_rule.value.redirect_configuration : []
        content {
          custom_host         = redirect_configuration.value.custom_host
          redirect_protocol   = redirect_configuration.value.redirect_protocol
          redirect_type       = redirect_configuration.value.redirect_type
          custom_fragment     = redirect_configuration.value.custom_fragment
          custom_path         = redirect_configuration.value.custom_path
          custom_query_string = redirect_configuration.value.custom_query_string
        }
      }
    }
  }

  dynamic "backend_pool_health_probe" {
    for_each = var.frontdoor_health_probe
    content {
      name                = backend_pool_health_probe.value.name
      enabled             = backend_pool_health_probe.value.enabled
      path                = backend_pool_health_probe.value.path
      protocol            = backend_pool_health_probe.value.protocol
      probe_method        = backend_pool_health_probe.value.probe_method
      interval_in_seconds = backend_pool_health_probe.value.interval_in_seconds
    }
  }

  dynamic "backend_pool" {
    for_each = var.frontdoor_backend
    content {
      name                = backend_pool.value.name
      load_balancing_name = backend_pool.value.loadbalancing_name
      health_probe_name   = backend_pool.value.health_probe_name

      dynamic "backend" {
        for_each = backend_pool.value.backend
        content {
          enabled     = backend.value.enabled
          address     = backend.value.address
          host_header = backend.value.host_header
          http_port   = backend.value.http_port
          https_port  = backend.value.https_port
          priority    = backend.value.priority
          weight      = backend.value.weight
        }
      }
    }
  }

  dynamic "frontend_endpoint" {
    for_each = var.frontend_endpoint
    content {
      name                                    = frontend_endpoint.value.name
      host_name                               = frontend_endpoint.value.host_name
      custom_https_provisioning_enabled       = frontend_endpoint.value.custom_https_provisioning_enabled
      session_affinity_enabled                = frontend_endpoint.value.session_affinity_enabled
      session_affinity_ttl_seconds            = frontend_endpoint.value.session_affinity_ttl_seconds
      web_application_firewall_policy_link_id = frontend_endpoint.value.waf_policy_link_id
      dynamic "custom_https_configuration" {
        for_each = frontend_endpoint.value.custom_https_provisioning_enabled == false ? [] : list(frontend_endpoint.value.custom_https_configuration.certificate_source)
        content {
          certificate_source = frontend_endpoint.value.custom_https_configuration.certificate_source
        }
      }
    }
  }
}

Expected Behavior

First issue:
A terraform plan/apply works for a brand new run and creates the Front Door resource.
Immediately after the creation, I run another plan/apply, and terraform says there are changes to make.

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  ~ update in-place

Terraform will perform the following actions:

  # module.front-door.azurerm_frontdoor.frontdoor will be updated in-place
  ~ resource "azurerm_frontdoor" "frontdoor" {
        id                                           = "/subscriptions/041d69fd-5118-4127-bb32-a7c991660ca0/resourceGroups/name4-rg-proto-tfmod/providers/Microsoft.Network/frontDoors/name4-fd-global-name1"
        name                                         = "name4-fd-global-name1"
        tags                                         = {}
        # (7 unchanged attributes hidden)

      ~ backend_pool {
            id                  = "/subscriptions/041d69fd-5118-4127-bb32-a7c991660ca0/resourceGroups/name4-rg-proto-tfmod/providers/Microsoft.Network/frontDoors/name4-fd-global-name1/backendPools/name3importapi"
          ~ name                = "name3importapi" -> "name3api"
            # (2 unchanged attributes hidden)

          ~ backend {
              ~ address     = "name4-app-prod01-name1importapi-name3.azurewebsites.net" -> "name4-app-prod01-name1api-name3.azurewebsites.net"
              ~ host_header = "name4-app-prod01-name1importapi-name3.azurewebsites.net" -> "name4-app-prod01-name1api-name3.azurewebsites.net"
                # (5 unchanged attributes hidden)
            }
          ~ backend {
              ~ address     = "name4-app-prod02-name1importapi-name3.azurewebsites.net" -> "name4-app-prod02-name1api-name3.azurewebsites.net"
              ~ host_header = "name4-app-prod02-name1importapi-name3.azurewebsites.net" -> "name4-app-prod02-name1api-name3.azurewebsites.net"
                # (5 unchanged attributes hidden)
            }
        }
      ~ backend_pool {
            id                  = "/subscriptions/041d69fd-5118-4127-bb32-a7c991660ca0/resourceGroups/name4-rg-proto-tfmod/providers/Microsoft.Network/frontDoors/name4-fd-global-name1/backendPools/name3ui"
          ~ name                = "name3ui" -> "name3importapi"
            # (2 unchanged attributes hidden)

          ~ backend {
              ~ address     = "name4-app-prod01-name1ui-name3.azurewebsites.net" -> "name4-app-prod01-name1importapi-name3.azurewebsites.net"
              ~ host_header = "name4-app-prod01-name1ui-name3.azurewebsites.net" -> "name4-app-prod01-name1importapi-name3.azurewebsites.net"
                # (5 unchanged attributes hidden)
            }
          ~ backend {
              ~ address     = "name4-app-prod02-name1ui-name3.azurewebsites.net" -> "name4-app-prod02-name1importapi-name3.azurewebsites.net"
              ~ host_header = "name4-app-prod02-name1ui-name3.azurewebsites.net" -> "name4-app-prod02-name1importapi-name3.azurewebsites.net"
                # (5 unchanged attributes hidden)
            }
        }
      ~ backend_pool {
            id                  = "/subscriptions/041d69fd-5118-4127-bb32-a7c991660ca0/resourceGroups/name4-rg-proto-tfmod/providers/Microsoft.Network/frontDoors/name4-fd-global-name1/backendPools/name3api"
          ~ name                = "name3api" -> "name3ui"
            # (2 unchanged attributes hidden)

          ~ backend {
              ~ address     = "name4-app-prod01-name1api-name3.azurewebsites.net" -> "name4-app-prod01-name1ui-name3.azurewebsites.net"
              ~ host_header = "name4-app-prod01-name1api-name3.azurewebsites.net" -> "name4-app-prod01-name1ui-name3.azurewebsites.net"
                # (5 unchanged attributes hidden)
            }
          ~ backend {
              ~ address     = "name4-app-prod02-name1api-name3.azurewebsites.net" -> "name4-app-prod02-name1ui-name3.azurewebsites.net"
              ~ host_header = "name4-app-prod02-name1api-name3.azurewebsites.net" -> "name4-app-prod02-name1ui-name3.azurewebsites.net"
                # (5 unchanged attributes hidden)
            }
        }



      ~ frontend_endpoint {
          ~ custom_https_provisioning_enabled = false -> true
          ~ host_name                         = "name4-fd-global-name1.azurefd.net" -> "name1.name2.com"
            id                                = "/subscriptions/041d69fd-5118-4127-bb32-a7c991660ca0/resourceGroups/name4-rg-proto-tfmod/providers/Microsoft.Network/frontDoors/name4-fd-global-name1/frontendEndpoints/name4-fd-global-name1"
          ~ name                              = "name4-fd-global-name1" -> "name1-name2-com"
            # (2 unchanged attributes hidden)

          + custom_https_configuration {
              + certificate_source = "FrontDoor"
            }
        }
      ~ frontend_endpoint {
          ~ custom_https_provisioning_enabled = true -> false
          ~ host_name                         = "name1.name2.com" -> "name4-fd-global-name1.azurefd.net"
            id                                = "/subscriptions/041d69fd-5118-4127-bb32-a7c991660ca0/resourceGroups/name4-rg-proto-tfmod/providers/Microsoft.Network/frontDoors/name4-fd-global-name1/frontendEndpoints/name1-name2-com"
          ~ name                              = "name1-name2-com" -> "name4-fd-global-name1"
            # (2 unchanged attributes hidden)

            # (1 unchanged block hidden)
        }

      ~ routing_rule {
            id                 = "/subscriptions/041d69fd-5118-4127-bb32-a7c991660ca0/resourceGroups/name4-rg-proto-tfmod/providers/Microsoft.Network/frontDoors/name4-fd-global-name1/routingRules/name3ui"
          ~ name               = "name3ui" -> "name3api"
          ~ patterns_to_match  = [
              - "/name3/ui",
              - "/name3/ui/",
              - "/name3/ui/*",
              + "/name3/api",
              + "/name3/api/",
              + "/name3/api/*",
            ]
            # (3 unchanged attributes hidden)

          ~ forwarding_configuration {
              ~ backend_pool_name                     = "name3ui" -> "name3api"
                # (5 unchanged attributes hidden)
            }
        }
      ~ routing_rule {
            id                 = "/subscriptions/041d69fd-5118-4127-bb32-a7c991660ca0/resourceGroups/name4-rg-proto-tfmod/providers/Microsoft.Network/frontDoors/name4-fd-global-name1/routingRules/name3api"
          ~ name               = "name3api" -> "name3ui"
          ~ patterns_to_match  = [
              - "/name3/api",
              - "/name3/api/",
              - "/name3/api/*",
              + "/name3/ui",
              + "/name3/ui/",
              + "/name3/ui/*",
            ]
            # (3 unchanged attributes hidden)

          ~ forwarding_configuration {
              ~ backend_pool_name                     = "name3api" -> "name3ui"
                # (5 unchanged attributes hidden)
            }
        }
        # (3 unchanged blocks hidden)
    }

Plan: 0 to add, 1 to change, 0 to destroy.

Issue 2:
If I allow the plan/apply to make the changes it fails - but I suspect this is not really a concern, and its a by-product of the first issue.

Steps to Reproduce

terraform apply

Additional Context

References

Potentially related:
hashicorp/terraform-provider-azurerm#8208

@ghost
Copy link

ghost commented Jan 6, 2021

This issue has been automatically migrated to hashicorp/terraform-provider-azurerm#10065 because it looks like an issue with that provider. If you believe this is not an issue with the provider, please reply to hashicorp/terraform-provider-azurerm#10065.

@ghost ghost closed this as completed Jan 6, 2021
@ghost
Copy link

ghost commented Feb 6, 2021

I'm going to lock this issue because it has been closed for 30 days ⏳. This helps our maintainers find and focus on the active issues.

If you have found a problem that seems similar to this, please open a new issue and complete the issue template so we can capture all the details necessary to investigate further.

@ghost ghost locked as resolved and limited conversation to collaborators Feb 6, 2021
This issue was closed.
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

2 participants