Правила брандмауэра MySQL из исходящих IP-адресов службы приложений в Azure с использованием Terraform

#azure #azure-web-app-service #terraform #firewall #azure-mysql-database

#azure #azure-web-app-service #terraform #брандмауэр #azure-mysql-database

Вопрос:

Я использую Terraform для развертывания приложения в Azure, включая сервер MySQL и службу приложений, и хочу ограничить доступ к базе данных только службой приложений. Служба приложений имеет список исходящих IP-адресов, поэтому я думаю, что мне нужно создать для них правила брандмауэра в базе данных. Я обнаружил, что в Terraform я не могу использовать count или for_each динамически создавать эти правила, поскольку значение заранее неизвестно.

Мы также рассмотрели возможность жесткого кодирования количества, но документы Azure не подтверждают количество IP-адресов. С учетом этого и после просмотра разных чисел в комментариях stackoverflow я обеспокоен тем, что в какой-то момент это число может измениться и прервать будущие развертывания.

Ошибка вывода предполагает использование -target в качестве обходного пути, но документы Terraform явно не рекомендуют этого из-за потенциальных рисков.

Есть предложения по решению? Есть ли обходной путь или есть другой подход, который лучше подходит?

Нефункциональный код, который я использую до сих пор, чтобы дать лучшее представление о том, что я пытаюсь сделать:

 ...
locals {
    appIps = split(",", azurerm_app_service.appService.outbound_ip_addresses)
}

resource "azurerm_mysql_firewall_rule" "appFirewallRule" {

  count = length(appIps)

  depends_on            = [azurerm_app_service.appService]
  name                  = "appService-${count.index}"
  resource_group_name   = "myResourceGroup"
  server_name           = azurerm_mysql_server.databaseServer.name
  start_ip_address      = local.appIps[count.index]
  end_ip_address        = local.appIps[count.index]
}
...
  

Это возвращает ошибку:

 
Error: Invalid count argument

  on main.tf line 331, in resource "azurerm_mysql_firewall_rule" "appFirewallRule":
 331:   count = length(local.appIps)

The "count" value depends on resource attributes that cannot be determined
until apply, so Terraform cannot predict how many instances will be created.
To work around this, use the -target argument to first apply only the
resources that the count depends on.

  

Ответ №1:

Я копаю глубже, и я думаю, что у меня есть решение, которое работает, по крайней мере, для меня 🙂

Суть проблемы здесь заключается в необходимости выполнить все это в 2 этапа (мы не можем иметь еще неизвестные значения в качестве аргументов для count и for_each ). Это можно решить с помощью явной императивной логики или действий (например, использовать -target один раз или закомментировать, а затем раскомментировать). Кроме того, он не является декларативным, он также не подходит для автоматизации через CI / CD (я использую Terraform Cloud, а не локальную среду).

Итак, я делаю это только с ресурсами Terraform, единственным «обязательным» шаблоном является запуск конвейера (или локального запуска) дважды.

Проверьте мой фрагмент:

 data "azurerm_resources" "web_apps_filter" {
  resource_group_name = var.rg_system_name
  type                = "Microsoft.Web/sites"
  required_tags = {
    ProvisionedWith = "Terraform"
  }
}

data "azurerm_app_service" "web_apps" {
  count = length(data.azurerm_resources.web_apps_filter.resources)

  resource_group_name = var.rg_system_name
  name                = data.azurerm_resources.web_apps_filter.resources[count.index].name
}

data "azurerm_resources" "func_apps_filter" {
  resource_group_name = var.rg_storage_name
  type                = "Microsoft.Web/sites"
  required_tags = {
    ProvisionedWith = "Terraform"
  }
}

data "azurerm_app_service" "func_apps" {
  count = length(data.azurerm_resources.func_apps_filter.resources)

  resource_group_name = var.rg_storage_name
  name                = data.azurerm_resources.func_apps_filter.resources[count.index].name
}

locals {
  # flatten ensures that this local value is a flat list of IPs, rather
  # than a list of lists of IPs.
  # distinct ensures that we have only uniq IPs

  web_ips = distinct(flatten([
    for app in data.azurerm_app_service.web_apps : [
      split(",", app.possible_outbound_ip_addresses)
    ]
  ]))

  func_ips = distinct(flatten([
    for app in data.azurerm_app_service.func_apps : [
      split(",", app.possible_outbound_ip_addresses)
    ]
  ]))
}

resource "azurerm_postgresql_firewall_rule" "pgfr_func" {
  for_each = toset(local.web_ips)

  name                = "web_app_ip_${replace(each.value, ".", "_")}"
  resource_group_name = var.rg_storage_name
  server_name         = "${var.project_abbrev}-pgdb-${local.region_abbrev}-${local.environment_abbrev}"
  start_ip_address    = each.value
  end_ip_address      = each.value
}

resource "azurerm_postgresql_firewall_rule" "pgfr_web" {
  for_each = toset(local.func_ips)

  name                = "func_app_ip_${replace(each.value, ".", "_")}"
  resource_group_name = var.rg_storage_name
  server_name         = "${var.project_abbrev}-pgdb-${local.region_abbrev}-${local.environment_abbrev}"
  start_ip_address    = each.value
  end_ip_address      = each.value
}
  

Самая важная часть — это azurerm_resources ресурс — я использую его для фильтрации того, какие веб-приложения уже существуют в моей группе ресурсов (и управляются автоматизацией). Я выполняю правила брандмауэра БД в этом списке, при следующем запуске terraform, когда появится вновь созданное веб-приложение, оно также внесет в белый список последнее созданное веб-приложение.

Интересной вещью также является фильтрация IP-адресов — многие из них дублируются.

Ответ №2:

На данный момент использование -target в качестве обходного пути является лучшим выбором. Поскольку в настоящее время Terraform работает, он считает такую конфигурацию неправильной. Не рекомендуется использовать выходные данные, вычисленные с использованием ресурсов, в качестве аргументов count и. for_each Вместо этого предпочтительным подходом является использование переменных или производных локальных значений, которые известны во время планирования. Если вы решите продолжить использование вычисленных значений для count / for_each , иногда вам потребуется обойти это, используя -target , как показано выше. Для получения более подробной информации, пожалуйста, обратитесь к here

Кроме того, ошибка будет исправлена в коде предварительной версии 0.14. Для получения более подробной информации, пожалуйста

Комментарии:

1. Любые мысли о том, какой подход использовать, если applies выполняются с помощью CI / CD в Terraform Cloud? У меня нет возможности использовать -target