#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