依赖于另一资源的Terraform无效计数参数

时间:2020-02-12 11:09:13

标签: terraform

尝试在Terraform脚本上进行计划或应用时出现以下错误。

Error: Invalid count argument

  on main.tf line 157, in resource "azurerm_sql_firewall_rule" "sqldatabase_onetimeaccess_firewall_rule":
 157:   count               = length(split(",", azurerm_app_service.app_service.possible_outbound_ip_addresses))

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.

我知道这已经失败了,因为在创建app_service之前,它不知道要创建的防火墙规则的数量。我可以只使用参数apply运行-target=azurerm_app_service.app_service,然后在创建app_service之后运行另一个apply。

但是,这对于我们的CI流程不是很好,如果我们想从terraform脚本中创建一个全新的环境,我们只想告诉terraform来构建它,而不必告诉它要构建的每个目标

在terraform中是否有一种方法可以说无需构建目标就可以构建所需的所有东西?

下面还是一个提供上述错误的terraform脚本示例:

provider "azurerm" {
  version = "=1.38.0"
}

resource "azurerm_resource_group" "resourcegroup" {
  name     = "rg-stackoverflow60187000"
  location = "West Europe"
}

resource "azurerm_app_service_plan" "service_plan" {
  name                = "plan-stackoverflow60187000"
  resource_group_name = azurerm_resource_group.resourcegroup.name
  location            = azurerm_resource_group.resourcegroup.location
  kind                = "Linux"
  reserved            = true

  sku {
    tier = "Standard"
    size = "S1"
  }
}

resource "azurerm_app_service" "app_service" {
  name                = "app-stackoverflow60187000"
  resource_group_name = azurerm_resource_group.resourcegroup.name
  location            = azurerm_resource_group.resourcegroup.location
  app_service_plan_id = azurerm_app_service_plan.service_plan.id

  site_config {
    always_on        = true
    app_command_line = ""
    linux_fx_version = "DOCKER|nginxdemos/hello"
  }

  app_settings = {
    "WEBSITES_ENABLE_APP_SERVICE_STORAGE" = "false"
  }
}

resource "azurerm_sql_server" "sql_server" {
  name                         = "mysqlserver-stackoverflow60187000"
  resource_group_name = azurerm_resource_group.resourcegroup.name
  location            = azurerm_resource_group.resourcegroup.location
  version                      = "12.0"
  administrator_login          = "4dm1n157r470r"
  administrator_login_password = "4-v3ry-53cr37-p455w0rd"
}

resource "azurerm_sql_database" "sqldatabase" {
  name                             = "sqldatabase-stackoverflow60187000"
  resource_group_name              =  azurerm_sql_server.sql_server.resource_group_name
  location                         =  azurerm_sql_server.sql_server.location
  server_name                      =  azurerm_sql_server.sql_server.name
  edition                          = "Standard"
  requested_service_objective_name = "S1"
}

resource "azurerm_sql_firewall_rule" "sqldatabase_firewall_rule" {
  name                = "App Service Access (${count.index})"
  resource_group_name = azurerm_sql_database.sqldatabase.resource_group_name
  server_name         = azurerm_sql_database.sqldatabase.name
  start_ip_address    = element(split(",", azurerm_app_service.app_service.possible_outbound_ip_addresses), count.index)
  end_ip_address      = element(split(",", azurerm_app_service.app_service.possible_outbound_ip_addresses), count.index)
  count               = length(split(",", azurerm_app_service.app_service.possible_outbound_ip_addresses))
}

1 个答案:

答案 0 :(得分:2)

要使此功能不出现错误消息中所述的-target解决方法,需要使用Terraform仅可从配置中知道的值(而不是提供者在应用时生成的值)来重新构造问题。

然后,诀窍是找出Azure API使用的配置中的哪些值来确定要返回多少IP地址,并依靠那些 。我对Azure不够了解,无法为您提供具体的答案,但是我在Inbound/Outbound IP addresses上看到这似乎是Azure App Services的操作细节,而不是您可以控制的东西,因此很可能会出现此问题无法解决。

如果确实没有办法根据配置来预测possible_outbound_ip_addresses中有多少个地址,那么另一种方法是将您的配置分为两个部分,一个部分依赖另一个部分。第一个配置您的App Service以及与之一起管理的其他所有内容,然后第二个可能使用the azurerm_app_service data source来检索有关假定已存在的App Service的数据,并根据该数据制定防火墙规则

无论哪种方式,您都需要运行Terraform两次以使必要的数据可用。使用-target的优点是,您在初始引导过程中只需要执行一次有趣的工作流,因此您有可能可以在CI外部进行初始创建以获取最初创建的对象,然后使用CI进行正在进行的更改。只要不替换应用程序服务对象,后续的Terraform计划就已经知道设置了多少个IP地址,因此应该能够正常完成。