-
Notifications
You must be signed in to change notification settings - Fork 4.7k
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
azurerm_application_gateway rewrite_rule_set_name causes deployed instance to be always modified #15923
Comments
@ronalddemneri , thanks for raising this issue. Seem I cannot repro this issue on my local with latest azurerm provider and below repro steps. If it's possible, you can try my below tf config to see if the issue still exists. May I ask how many request_routing_rule blocks and rewrite_rule_set blocks you defined in your tf config file while not using dynamic? Could you share the simplest tf config that triggers this issue? And could you double confirm whether below repro steps are expected? Thanks in advance. Repro steps:
|
Hello @neil-yechenwei, Thanks for having a look. to be honest I only tested this with dynamic blocks. I have a single I will try your suggestion and post back the results. |
This is the actual code that I am using: # main.tf
resource "azurerm_application_gateway" "appgw" {
location = var.appgw_location # string
name = var.appgw_name # string
resource_group_name = var.appgw_resource_group # string
zones = var.appgw_av_zones # list(number)
tags = var.tags # map(string)
sku {
name = var.appgw_sku_name # string
tier = var.appgw_sku_tier # string
capacity = var.appgw_sku_capacity # number
}
dynamic "autoscale_configuration" {
for_each = tostring(var.appgw_sku_capacity) == "0" ? ["dummy"] : []
content {
min_capacity = tostring(var.appgw_sku_capacity) == "0" ? lookup(var.appgw_autoscale_params, "appgw_autoscale_min") : null # number
max_capacity = tostring(var.appgw_sku_capacity) == "0" ? lookup(var.appgw_autoscale_params, "appgw_autoscale_max") : null # number
}
}
# Public IP address for the v2 SKU
frontend_ip_configuration {
name = var.appgw_frontend_ip_configuration_name
public_ip_address_id = var.appgw_public_ip_address_id
}
dynamic "frontend_ip_configuration" {
for_each = var.appgw_private ? ["dummy"] : []
content {
name = var.appgw_private ? var.appgw_frontend_priv_ip_configuration_name : null
private_ip_address_allocation = var.appgw_private ? "Static" : null
private_ip_address = var.appgw_private ? var.appgw_private_ip : null
subnet_id = var.appgw_private ? var.appgw_subnet_id : null
}
}
dynamic "frontend_port" {
for_each = var.appgw_fe_port # list(object)
content {
name = lookup(frontend_port.value, "name", null) # string
port = lookup(frontend_port.value, "port", null) # number
}
}
gateway_ip_configuration {
name = var.appgw_gw_ip_config_name # string
subnet_id = var.appgw_subnet_id # string
}
# For v2 SKUs; v1 SKUs uses authentication_certificate
dynamic "trusted_root_certificate" {
for_each = var.trusted_root_certificate_configs
content {
name = lookup(trusted_root_certificate.value, "name", null)
# use filename if supplied, otherwise use data
data = lookup(trusted_root_certificate.value, "data", null) == null ? filebase64(lookup(trusted_root_certificate.value, "filename", null)) : lookup(trusted_root_certificate.value, "data", null)
}
}
dynamic "probe" {
for_each = var.appgw_probes # list(object)
content {
host = lookup(probe.value, "host", null)
interval = lookup(probe.value, "interval", 30)
name = lookup(probe.value, "name", null)
path = lookup(probe.value, "path", "/")
port = lookup(probe.value, "port", 80)
protocol = lookup(probe.value, "protocol", "Https")
timeout = lookup(probe.value, "timeout", 30)
pick_host_name_from_backend_http_settings = lookup(probe.value, "pick_host_name_from_backend_http_settings", false)
unhealthy_threshold = lookup(probe.value, "unhealthy_threshold", 3)
match {
body = lookup(probe.value, "match_body", "")
status_code = lookup(probe.value, "match_status_code", ["200-399"])
}
}
}
dynamic "backend_address_pool" {
for_each = var.appgw_be_address_pools
content {
name = lookup(backend_address_pool.value, "name", null) # string
ip_addresses = length(lookup(backend_address_pool.value, "ip_addresses")) > 0 ? lookup(backend_address_pool.value, "ip_addresses") : null # list(string)
fqdns = length(lookup(backend_address_pool.value, "fqdns")) > 0 ? lookup(backend_address_pool.value, "fqdns") : null # list(string)
}
}
dynamic "backend_http_settings" {
for_each = var.appgw_backend_http_settings
content {
name = lookup(backend_http_settings.value, "name", null)
path = lookup(backend_http_settings.value, "path", "")
probe_name = lookup(backend_http_settings.value, "probe_name", null)
affinity_cookie_name = lookup(backend_http_settings.value, "affinity_cookie_name", "ApplicationGatewayAffinity")
cookie_based_affinity = lookup(backend_http_settings.value, "cookie_based_affinity", "Disabled")
pick_host_name_from_backend_address = lookup(backend_http_settings.value, "pick_host_name_from_backend_address", true)
host_name = lookup(backend_http_settings.value, "host_name", null)
port = lookup(backend_http_settings.value, "port", 443)
protocol = lookup(backend_http_settings.value, "protocol", "Https")
request_timeout = lookup(backend_http_settings.value, "request_timeout", 20)
trusted_root_certificate_names = lookup(backend_http_settings.value, "trusted_root_certificate_names", [])
}
}
dynamic "ssl_certificate" {
for_each = var.appgw_ssl_certificates # list(map(string))
content {
name = ssl_certificate.value.name
key_vault_secret_id = ssl_certificate.value.key_vault_secret_id
}
}
dynamic "http_listener" {
for_each = var.appgw_http_listeners # list(object{})
content {
name = lookup(http_listener.value, "name", null)
frontend_ip_configuration_name = lookup(http_listener.value, "frontend_ip_conf", var.appgw_private ? var.appgw_frontend_priv_ip_configuration_name : var.appgw_frontend_ip_configuration_name)
frontend_port_name = lookup(http_listener.value, "frontend_port_name", null)
protocol = lookup(http_listener.value, "protocol", "Https")
ssl_certificate_name = lookup(http_listener.value, "ssl_certificate_name", null)
host_name = lookup(http_listener.value, "host_name", null)
host_names = lookup(http_listener.value, "host_names", [])
require_sni = lookup(http_listener.value, "require_sni", null)
firewall_policy_id = lookup(http_listener.value, "firewall_policy_id", null)
ssl_profile_name = lookup(http_listener.value, "ssl_profile_name", null)
}
}
dynamic "request_routing_rule" {
for_each = var.appgw_routings # map(string)
content {
name = lookup(request_routing_rule.value, "name", null)
rule_type = lookup(request_routing_rule.value, "rule_type", "Basic")
http_listener_name = lookup(request_routing_rule.value, "http_listener_name", lookup(request_routing_rule.value, "name", null))
backend_address_pool_name = lookup(request_routing_rule.value, "backend_address_pool_name", null)
backend_http_settings_name = lookup(request_routing_rule.value, "backend_http_settings_name", null)
url_path_map_name = lookup(request_routing_rule.value, "url_path_map_name", null)
redirect_configuration_name = lookup(request_routing_rule.value, "redirect_configuration_name", null)
rewrite_rule_set_name = lookup(request_routing_rule.value, "rewrite_rule_set_name", null)
}
}
dynamic "rewrite_rule_set" {
for_each = var.appgw_rewrite_rule_sets
iterator = rset
content {
name = lookup(rset.value, "name")
dynamic "rewrite_rule" {
for_each = rset.value.rewrite_rules
iterator = rule
content {
name = lookup(rule.value, "name", format("%s-%s", lookup(rset.value, "name"), lookup(rule.value, "sequence")))
rule_sequence = lookup(rule.value, "sequence", 100)
condition {
pattern = lookup(rule.value, "pattern", "")
variable = lookup(rule.value, "variable", "")
negate = lookup(rule.value, "negate", false)
ignore_case = lookup(rule.value, "ignore_case", true)
}
dynamic "url" {
for_each = lookup(rule.value, "path", null) != null ? ["dummy"] : []
content {
path = lookup(rule.value, "path")
query_string = lookup(rule.value, "query_string", "")
reroute = lookup(rule.value, "reroute", false)
}
}
dynamic "request_header_configuration" {
for_each = lookup(rule.value, "req_header_name", null) != null ? ["dummy"] : []
content {
header_name = lookup(rule.value, "req_header_name", "")
header_value = lookup(rule.value, "req_header_value", "")
}
}
dynamic "response_header_configuration" {
for_each = lookup(rule.value, "res_header_name", null) != null ? ["dummy"] : []
content {
header_name = lookup(rule.value, "res_header_name", "")
header_value = lookup(rule.value, "res_header_value", "")
}
}
}
}
}
}
ssl_policy {
policy_type = "Custom"
cipher_suites = var.appgw_ssl_cipher_suites
min_protocol_version = "TLSv1_2"
# policy_name = "CustomSSLPolicy"
}
dynamic "ssl_profile" {
for_each = var.appgw_ssl_profiles
content {
name = lookup(ssl_profile.value, "name", null)
verify_client_cert_issuer_dn = lookup(ssl_profile.value, "verify_client_issuer_dn", false)
trusted_client_certificate_names = lookup(ssl_profile.value, "trusted_client_cert_names", [])
ssl_policy {
policy_type = "Custom"
cipher_suites = var.appgw_ssl_cipher_suites
min_protocol_version = "TLSv1_2"
# policy_name = "CustomSSLPolicy"
}
}
}
identity {
identity_ids = var.appgw_identity_ids # list(string)
type = var.appgw_identity_type # list (eg. UserAssigned)
}
}
# variables.tf
variable "appgw_sku_capacity" {
type = number
description = "Number of Application Gateway instances to deploy, in case Autoscaling is not enabled. Mutually exclusive with Autoscaling."
}
variable "appgw_sku_tier" {
type = string
description = "SKU tier of the Application Gateway. Possible values are \"Standard\", \"WAF\", \"Standard_v2\" and \"WAF_v2\"."
}
variable "appgw_sku_name" {
type = string
description = "SKU tier of the Application Gateway. Possible values are \"Standard\", \"WAF\", \"Standard_v2\" and \"WAF_v2\"."
}
variable "appgw_gw_ip_subnet_id" {
type = string
description = "The ID of the Subnet which the Application Gateway should be connected to."
}
variable "appgw_gw_ip_config_name" {
type = string
description = " The Name of this Gateway IP Configuration."
}
variable "appgw_fe_port" {
type = list(object({
name = string,
port = number
}))
description = "List of objects containing the configuration of the frontend ports for the Application Gateway."
}
variable "appgw_be_address_pools" {
type = list(object({
name = string
ip_addresses = list(string)
fqdns = list(string)
}))
description = "List of objects containing the configuration settings for the backend pool(s) of the Application Gateway."
}
variable "appgw_probes" {
type = list(object({
host = string,
interval = number, # Default 30
name = string,
path = string, # Default '/'
protocol = string, # Default Https
pick_host_name_from_backend_http_settings = bool, # Default false
timeout = number, # Default 30
unhealthy_threshold = number # Default 3
match_body = string
match_status_code = list(string) # Default ["200-399"]
}))
description = "List of objects containing the configuration of the health probes for the backend pool(s) of the Application Gateway."
}
variable "appgw_autoscale_params" {
type = map(number)
description = "Map of key/value pairs for configuring the Autoscaling functionality of Application Gateway. Should contain at least a key named \"appgw_capacity_min\"."
}
variable "tags" {
type = map(string)
description = "Map of strings for applying tags to deployed resources."
}
variable "appgw_av_zones" {
type = list(string)
description = "List of the AV zones where the Application Gateway should be deployed (for enabling zone HA)."
}
variable "appgw_resource_group" {
type = string
description = "Name of the resource group where the Applicaiton Gateway will be deployed."
}
variable "appgw_name" {
type = string
description = "Name of the Application Gateway."
}
variable "appgw_location" {
type = string
description = "Location where to deploy the Application Gateway."
}
variable "trusted_root_certificate_configs" {
type = list(map(string))
# name = string
# data = string
# filename = string
# }))
description = "List of trusted root certificates. The needed values for each trusted root certificates are 'name' and 'data' or 'filename'. This parameter is required if you are not using a trusted certificate authority (eg. selfsigned certificate)"
}
variable "appgw_private" {
type = bool
description = "Variable commanding the creation of the Private IP address for v2 SKUs of Application Gateway."
}
variable "appgw_frontend_priv_ip_configuration_name" {
type = string
description = "Application Gateway configuration name for the private IP address."
}
variable "appgw_frontend_ip_configuration_name" {
type = string
description = "Application Gateway configuration name for the public IP address."
}
variable "appgw_private_ip" {
type = string
description = "Private IP address assigned to the Application Gateway, when \"appgw_private\" is set to \"true\"."
}
variable "appgw_subnet_id" {
type = string
description = "Subnet id where the Application Gateway will be connected."
}
variable "appgw_backend_http_settings" {
type = list(object({
name = string
path = string
probe_name = string
affinity_cookie_name = string
cookie_based_affinity = string # Enabled/Disabled
pick_host_name_from_backend_address = bool
host_name = string
port = number # Default 443
protocol = string
request_timeout = number
trusted_root_certificate_names = list(string)
}))
description = "List of objects containing the configuration of the backend HTTP settings for the Application Gateway."
}
variable "appgw_http_listeners" {
type = list(object({
name = string
frontend_ip_conf = string
frontend_port_name = string
protocol = string
ssl_certificate_name = string
host_name = string
host_names = set(string)
require_sni = bool
firewall_policy_id = string
ssl_profile_name = string
}))
description = "List of objects containing the configuration of the HTTP listeners settings for the Application Gateway."
}
variable "appgw_routings" {
type = list(map(string))
/*default = [{
name = ""
rule_type = ""
http_listener_name = ""
backend_address_pool_name = ""
backend_http_settings_name = ""
url_path_map_name = ""
redirect_configuration_name = ""
rewrite_rule_set_name = ""
}]*/
description = "List of maps containing the configuration of request routings for the Application Gateway."
}
variable "appgw_rewrite_rule_sets" {
type = list(object({
name = string
rewrite_rules = list(object({
name = string
sequence = number
pattern = string
variable = string
negate = bool
ignore_case = bool
path = string
query_string = string
reroute = bool
req_header_name = string
req_header_value = string
res_header_name = string
res_header_value = string
}))
}))
description = "Nested map of objects containing the rewrite rules to be applied to the Application Gateway http request rules."
}
variable "appgw_identity_ids" {
type = list(string)
description = "List of the Object IDs of Security Principals that are used on the Application Gateway to read secrets, keys and certificates from an Azure Key Vault."
}
variable "appgw_identity_type" {
type = string
default = "UserAssigned"
description = "Type of the Managed Identity. Possible values are \"SystemAssigned\" and \"UserAssigned\", where the latter is the default setting."
}
variable "appgw_ssl_certificates" {
type = list(map(string))
description = "List of name/key_vault_secret_id for each certificate"
}
variable "appgw_ssl_profiles" {
type = list(object({
name = string
verify_client_issuer_dn = bool
trusted_client_cert_names = list(string)
}))
description = "List of objects containing the configurations of SSL profiles to assign to the Application Gateway."
}
variable "appgw_ssl_cipher_suites" {
type = list(string)
default = [
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384",
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384",
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256"
]
description = "List of the default SSL cipher suites that need to be assigned to the Application Gateway (eg. for deprecating TLS1.0)."
}
variable "appgw_public_ip_address_id" {
type = string
description = "Public IP address ID assigned to the Application Gateway. Required for v2 SKUs."
# default = null
} |
Hello @tombuildsstuff, I am not sure if this is actually related to #6896 or any of the other issues referenced by #15800. But I will try to use the 3.0 provider and see how it works out. Regards, |
Hello @tombuildsstuff, I can confirm that when using the azurerm 3.0-beta provider, tf plan does indeed show that the state matches the configuration. But I still have the Partial output from ...
...
request_routing_rule {
backend_address_pool_id = "/subscriptions/.../gateway-devs-1/backendAddressPools/be-pool-dev"
backend_address_pool_name = "be-pool-dev"
backend_http_settings_id = "/subscriptions/../gateway-devs-1/backendHttpSettingsCollection/be-setting-http-80"
backend_http_settings_name = "be-setting-http-80"
http_listener_id = "/subscriptions/.../-gateway-devs-1/httpListeners/https-listener"
http_listener_name = "https-listener"
id = "/subscriptions/.../gateway-devs-1/requestRoutingRules/routing-rule"
name = "routing-rule"
priority = 0
rule_type = "Basic"
}
rewrite_rule_set {
id = "/subscriptions/.../gateway-devs-1/rewriteRuleSets/rewrite-set-001"
name = "rewrite-set-001"
rewrite_rule {
name = "RewritePath"
rule_sequence = 100
condition {
ignore_case = true
negate = false
pattern = "<some pattern>"
variable = "var_uri_path"
}
...
... Any idea how to fix this (which in fact is the main reason I created this bug)? |
Do you think I need to open a new issue #
Do you think I need to open a new issues for this? |
This functionality has been released in v3.0.0 of the Terraform Provider. Please see the Terraform documentation on provider versioning or reach out if you need any assistance upgrading. For further feature requests or bug reports with this functionality, please create a new GitHub issue following the template. Thank you! |
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. |
Community Note
Terraform (and AzureRM Provider) Version
Affected Resource(s)
azurerm_application_gateway
Terraform Configuration Files
Debug Output
Panic Output
Expected Behaviour
After already applying the Terraform code as a result of adding the name of the
rewrite_rule_set
in therequest_routing_rule
configuration, I expect thatterraform plan
will not prompt for the resource to be updated again (and again).Actual Behaviour
My code is very similar to #8397, except that I am using dynamic blocks. The
rewrite_rule_set
variable is a list of objects, as follows:What happens is that, regardless how many times I run terraform apply, terraform plan will always return that there are changes to be made. As for the aforementioned issue #8397, I get the rewrite_rule_set_id, but not the rewrite_rule_set_name. I have three environments that are all affected by this issue (providing a short snippet below). As for #8397, the request_routing_rule is dissociated from the rewrite_rule_set, but I can still link these together via the portal; the problem is that when applying again, the association will be removed over and over.
Initially, I thought the problem was because I declared the rewrite_rule_set variable as a
set(object{...}))
, but I changed it tolist(object({...}))
so that to keep my ordering, but it still persists.Steps to Reproduce
Noting special to reproduce,
terraform apply
, thenterraform plan
, and you can see that the request routing rule will always be modified.terraform apply
terraform plan
Important Factoids
References
The text was updated successfully, but these errors were encountered: