Terraform for_each, если значение существует в объекте - PullRequest
1 голос
/ 11 июля 2020

Я хотел бы динамически создать несколько подсетей и таблиц маршрутов из файла .tfvars, а затем связать каждый su bnet с соответствующей таблицей маршрутов, если он указан.

Вот мой файл .tfvars:

vnet_spoke_object                      = {
    specialsubnets                     = {
        Subnet_1                       = {
            name                       = "test1"
            cidr                       = ["10.0.0.0/28"]
            route                      = "route1"
        }
        Subnet_2                       = {
            name                       = "test2"
            cidr                       = ["10.0.0.16/28"]
            route                      = "route2"
        }
        Subnet_3                       = {
            name                       = "test3"
            cidr                       = ["10.0.0.32/28"]
        }
    }
}

route_table                            = {
    route1                             = {
        name                           = "route1"
        disable_bgp_route_propagation  = true
        route_entries                  = {
            re1                        = {
                name                   = "rt-rfc-10-28"
                prefix                 = "10.0.0.0/28"
                next_hop_type          = "VirtualAppliance"
                next_hop_in_ip_address = "10.0.0.10"
            }
        }
    }
    route2                             = {
        name                           = "route2"
        disable_bgp_route_propagation  = true
        route_entries                  = {
            re1                        = {
                name                   = "rt-rfc-10-28"
                prefix                 = "10.0.0.16/28"
                next_hop_type          = "VirtualAppliance"
                next_hop_in_ip_address = "10.0.0.10"
            }
        }
    }
}

... и вот мой сценарий сборки:

provider "azurerm" {
    version                        = "2.18.0"
    features{}
}

variable "ARM_LOCATION" {
    default                        = "uksouth"
}

variable "ARM_SUBSCRIPTION_ID" {
    default                        = "asdf-b31e023c78b8"
}

variable "vnet_spoke_object" {}
variable "route_table" {}

module "names" {
    source                         = "./nbs-azure-naming-standard"
    env                            = "dev"
    location                       = var.ARM_LOCATION
    subId                          = var.ARM_SUBSCRIPTION_ID
}

resource "azurerm_resource_group" "test" {
    name                           = "${module.names.standard["resource-group"]}-vnet"
    location                       = var.ARM_LOCATION
}

resource "azurerm_virtual_network" "test" {
    name                           = "${module.names.standard["virtual-network"]}-test"
    location                       = var.ARM_LOCATION
    resource_group_name            = azurerm_resource_group.test.name
    address_space                  = ["10.0.0.0/16"]
}

resource "azurerm_subnet" "test" {
    for_each                       = var.vnet_spoke_object.specialsubnets
    name                           = "${module.names.standard["subnet"]}-${each.value.name}"
    resource_group_name            = azurerm_resource_group.test.name
    virtual_network_name           = azurerm_virtual_network.test.name
    address_prefixes               = each.value.cidr
}

resource "azurerm_route_table" "test" {
    for_each                       = var.route_table
    name                           = "${module.names.standard["route-table"]}-${each.value.name}"
    location                       = var.ARM_LOCATION
    resource_group_name            = azurerm_resource_group.test.name
    disable_bgp_route_propagation  = each.value.disable_bgp_route_propagation
    dynamic "route" {
        for_each                   = each.value.route_entries
        content {
            name                   = route.value.name
            address_prefix         = route.value.prefix
            next_hop_type          = route.value.next_hop_type
            next_hop_in_ip_address = contains(keys(route.value), "next_hop_in_ip_address") ? route.value.next_hop_in_ip_address: null
        }
    }
}

Эта часть отлично работает при создании ресурсов vnet / subnet / route, но проблема, с которой я сталкиваюсь, заключается в динамическом связывании каждый su bnet к таблице маршрутов, указанной в .tfvars. Не все подсети будут иметь связанную с ними таблицу маршрутов, поэтому потребуется только запустить IF , ключ / значение route указан.

resource "azurerm_subnet_route_table_association" "test" {
    for_each                       = {
        for key, value in var.vnet_spoke_object.specialsubnets:
            key => value
            if value.route != null
    }

    lifecycle {
        ignore_changes             = [
            subnet_id
        ]
    }
    subnet_id                      = azurerm_subnet.test[each.key].id
    route_table_id                 = azurerm_route_table.test[each.key].id
}

Ошибка, с которой я сталкиваюсь с приведенным выше кодом:

Error: Unsupported attribute

  on main.tf line 65, in resource "azurerm_subnet_route_table_association" "test":
  65:             if value.route != null

This object does not have an attribute named "route".

Я пробовал разные способы безуспешно, и я здесь в растерянности, и был бы признателен за любые рекомендации.

1 Ответ

1 голос
/ 11 июля 2020

Основываясь на вашем сценарии, я предполагаю, что vnet_spoke_object на входе выглядит так:

vnet_spoke_object = {
  specialsubnets = {
    subnetA = {
      cidr  = "..."
    }
    subnetB = {
      cidr  = "..."
      route = "..."
    }
  }
}

Проблема в том, что отсутствует route запись не разрешается в null , вызывает пани c или cra sh. Вам нужно будет написать свой ввод следующим образом (с явным null s):

vnet_spoke_object = {
  specialsubnets = {
    subnetA = {
      cidr  = "..."
      route = null
    }
    subnetB = {
      cidr  = "..."
      route = "..."
    }
  }
}

или поиск маршрут по имени и укажите null значение по умолчанию в выражении генератора карты, например:

for_each                       = {
    for key, value in var.vnet_spoke_object.specialsubnets:
        key => value
        if lookup(value, "route", null) != null
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...