1

我正在使用 Terraform 将应用程序部署到 Azure,包括 MySQL 服务器和应用程序服务,并且希望将数据库访问限制为仅应用程序服务。应用服务有一个出站 IP 列表,所以我想我需要在数据库上为这些创建防火墙规则。我发现在 Terraform 中,我不能使用countfor_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.

4

2 回答 2

2

我在那里深入挖掘,我认为我有一个至少对我有用的解决方案:)

这里问题的核心是必须分两步完成整个事情(我们还不能将未知值作为countand的参数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资源 - 我正在使用它来过滤我的资源组中已经存在的 Web 应用程序(并由自动化管理)。我在该列表上执行数据库防火墙规则,下一个 terraform 运行,当新创建的 Web 应用程序在那里时,它也会将最后创建的 Web 应用程序列入白名单。

一个有趣的事情是 IP 的过滤——其中很多是重复的。

于 2021-01-13T12:18:11.857 回答
1

目前,使用-target作为解决方法是更好的选择。因为按照目前 Terraform 的工作方式,它认为这种配置是不正确的。count不推荐使用资源计算输出作为参数for_each。相反,使用在计划时已知的变量或派生的本地值是首选方法。如果您选择继续使用 count/for_each 的计算值,则有时需要您使用-target如上所示的方法来解决此问题。更多详情,请参考这里

此外,该错误将在预发布的 0.14 代码中修复。欲知更多详情,请

于 2020-11-24T04:08:33.353 回答