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

Support for separate frontend_port, backend_address_pool, etc. resources in azurerm_application_gateway #15745

Closed
djryanj opened this issue Mar 8, 2022 · 3 comments
Labels
duplicate service/application-gateway upstream/microsoft Indicates that there's an upstream issue blocking this issue/PR

Comments

@djryanj
Copy link
Contributor

djryanj commented Mar 8, 2022

Community Note

  • Please vote on this issue by adding a 👍 reaction to the original issue to help the community and maintainers prioritize this request
  • Please do not leave "+1" or "me too" comments, they generate extra noise for issue followers and do not help prioritize the request
  • If you are interested in working on this issue or have submitted a pull request, please leave a comment

Description

Currently, the azurerm_application_gateway resource has multiple blocks of configuration inside of it; for example, a backend_address_pool. These can be created dynamically as one would expect using the dynamic syntax, but this is difficult. The end result of this is that the attributes that are exported by the resource for those configuration items are almost all lists/arrays, and accessing information inside of them uses the highly brittle (and incredibly difficult to address meaningfully) array notation, e.g., backend_address_pool[0].

Examining the actual resources Azure's resource explorer, we can see that within an Application Gateway resource each of these blocks actually has not only a unique resource ID (meaning we "should" be able to create an associated Terraform resource), but also a unique type; for example, a backend_address_pool looks like this:

"backendAddressPools": [{
    "name": "pool1",
    "id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg1/providers/Microsoft.Network/applicationGateways/ag1/backendAddressPools/pool1",
    "etag": "W/\"00000000-0000-0000-0000-000000000000\"",
    "properties": {
        "provisioningState": "Succeeded",
        "backendIPConfigurations": [{
            "id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg1/providers/Microsoft.Network/networkInterfaces/vm1/ipConfigurations/ipconfig1"
        }],
        "backendAddresses": [],
        "urlPathMaps": [{
            "id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg1/providers/Microsoft.Network/applicationGateways/ag1/urlPathMaps/url1"
        }],
        "pathRules": [{
            "id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg1/providers/Microsoft.Network/applicationGateways/ag1/urlPathMaps/url1/pathRules/pool1"
        }]
    },
    "type": "Microsoft.Network/applicationGateways/backendAddressPools"
}]

The reason this would be highly beneficial is because when developing a module, it is extremely difficult to create sufficient abstraction between the "bare" Terraform config required for this and a more desirable one in a way that is not brittle (see comment above about only being able to reference these dynamic blocks by array notation).

I have focused on the backend_address_pool configuration block, though this could be for most of the configuration blocks in the azurerm_application_gateway resource. For backend_address_pool specifically, we do have azurerm_network_interface_application_gateway_backend_address_pool_association, but this requires passing in a backend_address_pool ID, which as mentioned cannot be retrieved without knowing its position in the array. For complex configurations, this is difficult, if not impossible. Not to mention that azurerm_network_interface_application_gateway_backend_address_pool_association uses a weird, Terraform-only ID (e.g., {networkInterfaceId}/ipConfigurations/{ipConfigurationName}|{backendAddressPoolId}) which is probably unnecessary given that there are actual IDs available for these resources from the API.

This sort of pattern exists in other azurerm resources; for example, azurerm_network_security_group has support for inline security_rule blocks, but also azurerm_network_security_rule resources exist which functionally do the same thing but are far easier to code modules for since they will get an actual ID that is easy to reference, especially when created dynamically with a for_each.

ALTERNATIVELY, having these blocks at least export more than just their IDs would be useful (e.g., in request_routing_rule we need backend_address_pool_name, but the backend_address_pool attribute does not export a name attribute. OR, changing the various *_name's in the blocks to *_id's, or supporting both.

New or Affected Resource(s)

  • azurerm_application_gateway
  • azurerm_application_gateway_frontend_port (new)
  • azurerm_application_gateway_backend_address_pool (new)
  • azurerm_application_gateway_gateway_ip_configuration (new)
  • azurerm_application_gateway_frontend_ip_configuration (new)
  • azurerm_application_gateway_backend_http_settings (new)
  • azurerm_application_gateway_http_listener (new)
  • azurerm_application_gateway_request_routing_rule (new)
  • Potentially others...

Potential Terraform Configuration

resource "azurerm_resource_group" "this" {
  name     = "rg1"
  location = "eastus"
}

resource "azurerm_public_ip" "this" {
  name                = "appgw-pip"
  location            = azurerm_resource_group.this.location
  resource_group_name = azurerm_resource_group.this.name
  allocation_method   = "Dynamic"
}


resource "azurerm_application_gateway" "this" {
  name                = "appgw1"
  resource_group_name = azurerm_resource_group.this.name
  location            = azurerm_resource_group.this.location

  sku {
    name     = "Standard_Small"
    tier     = "Standard"
    capacity = 2
  }
}

resource "azurerm_application_gateway_gateway_ip_configuration" "this" {
  name                     = "ipconfig1"
  subnet_id                = var.subnet_id
  application_gateway_name = azurerm_application_gateway.this.name
}

resource "azurerm_application_gateway_frontend_ip_configuration" "this" {
  name                     = "appgw-feip"
  public_ip_address_id     = azurerm_public_ip.this.id
  application_gateway_name = azurerm_application_gateway.this.name
}

resource "azurerm_application_gateway_frontend_port" "this" {
  name                     = "port_80"
  port                     = 80
  application_gateway_name = azurerm_application_gateway.this.name
}

resource "azurerm_application_gateway_backend_address_pool" "this" {
  name                     = "appgw-bep1"
  application_gateway_name = azurerm_application_gateway.this.name
}

resource "azurerm_application_gateway_backend_http_settings" "this" {
  name                     = "http-setting-1"
  cookie_based_affinity    = "Disabled"
  path                     = "/path1/"
  port                     = 80
  protocol                 = "Http"
  request_timeout          = 60
  application_gateway_name = azurerm_application_gateway.this.name
}

resource "azurerm_application_gateway_http_listener" "this" {
  name                           = "listener-1"
  frontend_ip_configuration_name = azurerm_application_gateway_frontend_ip_configuration.this.name
  frontend_port_name             = azurerm_application_gateway_frontend_port.this.name
  protocol                       = "Http"
  application_gateway_name       = azurerm_application_gateway.this.name
}

resource "azurerm_application_gateway_request_routing_rule" "this" {
  name                       = "rule1"
  rule_type                  = "Basic"
  http_listener_name         = azurerm_application_gateway_http_listener.this.name
  backend_address_pool_name  = azurerm_application_gateway_backend_address_pool.this.name
  backend_http_settings_name = azurerm_application_gateway_backend_http_settings.this.name
  application_gateway_name   = azurerm_application_gateway.this.name
}

References

The API documentation would suggest this is possible: https://docs.microsoft.com/en-us/rest/api/application-gateway/application-gateways

@djryanj
Copy link
Contributor Author

djryanj commented Mar 8, 2022

It appears that the various configuration blocks do in fact export attributes like name; take a backend_address_pool for example:

> contains(azurerm_application_gateway.this.backend_address_pool[*].name, "appgw-bep1")
true
> index(azurerm_application_gateway.this.backend_address_pool[*].name, "appgw-bep1")
0

So at the very least the documents need to be updated. This is still suboptimal, because writing something like this:

> azurerm_application_gateway.this.backend_address_pool[index(azurerm_application_gateway.this.backend_address_pool[*].name, "appgw-bep1")].id

While functional, is incredibly non-intuitive.

@tombuildsstuff
Copy link
Member

hi @djryanj

Thanks for opening this issue.

When provisioning an Application Gateway unfortunately it's required to create at least one of most of the sub-components, as such it's not possible to add support for these as separate resources without introducing a perectual diff on the main azurerm_application_gateway resource. This has come up a number of times over the years and is unfortunately blocked on the Azure Service Team who need to add support for this - which is currently being tracked in this upstream issue.

Unfortunately the Azure Networking team have since tried to move this Feature Request to the Azure Feedback site, which has since been shut down - as such we've previously asked for an update to get this re-opened - however it may also be worth opening a new issue on that repository too.

Since the API doesn't allow provisioning an Application Gateway without any subcomponents at the moment unfortunately and so is a feature request for the Azure Service Team - I'm going to close this issue for the moment, but I'd recommend either commenting on the existing issue above, or making a new one so that the Service Team can take a look - but unfortunately there's not much we can do from Terraform's side at the moment.

Thanks!

@github-actions
Copy link

github-actions bot commented Apr 9, 2022

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.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Apr 9, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
duplicate service/application-gateway upstream/microsoft Indicates that there's an upstream issue blocking this issue/PR
Projects
None yet
Development

No branches or pull requests

2 participants