Terraform 0.12 azurerm_app_service outputs as azurerm_mysql_firewall_rule inputs

Hi,

I’ve been trying to find a solution to what must be a common use strategy, I am deploying Azure WordPress App Service along with Azure Database for MySQL server.

As part of the terraform deployment, I have defined output ‘possible_outbound_ip_addresses’ and I am trying to code it so that that output can be used as inputs to the ‘azurerm_mysql_firewall_rule’->‘start_ip_address’ and ‘end_ip_address’ so that the automation takes care of firewall requirements between the web service and database service during the deployment.

Unfortunately the output varies in terms of the quantity of IP addresses returned, and also the multiple IP addresses returned are in a single string, so I suspect we may need some sort of for_each, but that may not necessarily be the best way forward, ideas?

I have tried using ‘split’ which happily separates the values over individual lines but it still isn’t treated as a ‘list’ so the individual elements can’t be parsed into separate firewall rules, so in my azurerm_app_service.tf currently I have:
(
output “possible_outbound_ip_addresses” {
value = ["${split(",",azurerm_app_service.webapp.possible_outbound_ip_addresses)}"]
)
I have additionally used the 0.12 version of ‘list’ by enclosing the value in [square brackets] but I am still unable to reference the values as if they were elements.

I’m happy to make use of any method anybody else has of passing the possible outbound ip addresses from the web app through to the firewall rules for mysql database, but I’ve done the usual research and haven’t found a solution to this yet, surely someone else has encountered this previously?

I’d appreciate any guidance (I come from an ops background as opposed to a dev background, so code it not in my natural leanings!)

Thanks in anticipation…

Hi @g-spencer!

I’m not familiar with azurerm_app_service in particular so I can’t offer detailed advice here, but if the possible_outbound_ip_addresses is a comma-separated string as you noted here then you should be able to turn it into a list of strings like this:

output "possible_outbound_ip_addresses" {
  value = split(",", azurerm_app_service.webapp.possible_outbound_ip_addresses)
}

The extra square brackets around the expression would cause the result to be a list of lists rather than just a single list, so it’s important to leave those out.

Unfortunately I think there’s a further challenge here: Terraform requires that any map or set passed to for_each have known key values during planning, but I suspect that this possible_outbound_ip_addresses attribute isn’t known until after the app service has been created during the apply step. :thinking:

After some quick research into outbound IP addresses it looks like the number of addresses in this list is unpredictable until after the app service is created, so I’m not sure it will be feasible to use this list to create one azurerm_mysql_firewall_rule per IP address. You will probably get an error during initial creation that the for_each value isn’t allowed because it isn’t known yet. Unfortunately, I’m not sure what else to suggest… someone with more Azure App Service experience than I might be able to offer a suggestion.

Hi Martin,

thanks for the prompt response, I’ve followed your recommendation and that does produce a list of entries but unfortunately it’s still not indexable as elements to select.
I had another theory on how to attack this over the weekend, and I’ll have a crack at that.
I did realise later that ‘for_each’ wouldn’t be useful directly in this instance, though I do now intend to maybe make a for_each loop that would allow a single routine to step its way through the elements, so 0 for the first IP, 1 for the second IP etc, so I just have a single loop of code to maintain.
However this is all just an idea at the moment, I’ll report back how and if I get my new approach to work.

thanks,

I know it’s been a while but I’m running into the same issue. Were you able to figure out a way to add all the outbound ip addresses to the sql server firewall rull set?

Hi, could you post the value of azurerm_app_service.webapp.possible_outbound_ip_addresses, maybe slightly obfuscated so not revealing sensitive information?
Makes it easier to see if we can help if we have the exact structure to work with

Hi,

unfortunately I more or less ended up down a dead-end alley with this one.

The possible outbound ip addresses aren’t known until after the webapp is applied, and all the options I could discover relied on declarations to be known before running TF apply in azure devops.
We even stumbled upon a task ‘Terraform Outputs’ but this still didn’t enable us to ‘know’ the values for input to the mysql security rules for application.
[https://github.com/raul-arrieta/terraform-outputs]

The mechanism we settled on in the end was to run the apply, make a note of the ip addresses from the output. then run a destroy.
Experience has shown the first IP and possibly more are not likely to change if a subsequent apply is made reasonably quickly.
Put those IP values in as the allowed addresses in the mysql rules and run an apply again.

This isn’t fantastic automation, but it works for us.
Running an apply, destroy, update code, apply cycle is a little bit counter productive, it would be interesting to hear from the big boys who must surely have a better way.

thanks for your attention guys,

I don’t quite understand, do you mean that the firewall openings have to be in place before the app service is created and that’s not possible because you don’t know the outbound ip’s until the service has been created?
Or is it sufficient that the openings are created afterwards?

If azurerm_app_service.webapp.possible_outbound_ip_addresses = "52.23.25.3,52.143.43.12,52.143.43.17", then you could do something like this

resource "azurerm_mysql_firewall_rule" "example" {
  for_each = split(",", azurerm_app_service.webapp.possible_outbound_ip_addresses)
  name                = "access_from_${each.value}"
  resource_group_name = "${azurerm_resource_group.example.name}"
  server_name         = "${azurerm_mysql_server.example.name}"
  start_ip_address    = each.value
  end_ip_address      = each.value
}

I don’t have an app_service and a mysql deployed, so I can’t verify the syntax

I strongly recommend getting the TF configuration to work first outside Azure DevOps, to avoid that extra layer of complexity.