Объединить два списка в Terraform 0.12 - concat () - PullRequest
2 голосов
/ 07 июня 2019

Я хочу объединить два массива в Terraform 0.12. В моем примере есть публичные и частные подсети. Я хочу назначить их обоим в те же списки доступа к сети. Сокращен следующий код:

data "aws_subnet_ids" "private" {
    vpc_id = aws_vpc.main.id
    tags = {
        subnet-type  = "private"
    }
}

data "aws_subnet_ids" "public" {
    vpc_id = aws_vpc.main.id
    tags = {
        subnet-type  = "public"
    }
}


resource "aws_network_acl" "networks" {
    vpc_id = aws_vpc.main.id

    subnet_ids = concat(data.aws_subnet_ids.private.ids, data.aws_subnet_ids.public.ids)
    [...]
}

Если я использую следующие выходы:

output "private_subnets" {
  value = data.aws_subnet_ids.private.ids
}

output "public_subnets" {
  value = data.aws_subnet_ids.public.ids
}

Генерируется следующий вывод:

private_subnets = [
  "subnet-243zr427rhhfjseb9",
  "subnet-we789rh2438fchb6e",
  "subnet-092rz7g82fhhkui74",
]
public_subnets = [
  "subnet-12230qegvg764e9d",
  "subnet-123465svgvgf0d7e",
]

Так что все должно работать. Но выдается следующая ошибка:

iptizer@machine:~/src/infra$ terraform12 apply
[...]

Error: Invalid function argument

  on nacls.tf line 19, in resource "aws_network_acl" "networks":
  19:     subnet_ids = concat(data.aws_subnet_ids.private.ids, data.aws_subnet_ids.public.ids)
    |----------------
    | data.aws_subnet_ids.private.ids is set of string with 3 elements

Invalid value for "seqs" parameter: all arguments must be lists or tuples; got
set of string.

Ошибка .. или что мне не хватает?

Ответы [ 2 ]

2 голосов
/ 10 июня 2019

Terraform 0.12 делает более сильное различие между списком и установленными значениями, чем 0.11, и включает в себя некоторые дополнительные проверки, подобные этой.

В этом конкретном случае concat завершается неудачей таким образом, потому что объединение требует всеэлементы должны иметь четко определенный порядок, так что результат также может иметь четко определенный порядок.Наборы не упорядочены, поэтому эта проверка установлена, чтобы напомнить вам о необходимости явно выбирать подходящий порядок при преобразовании в список или вообще не преобразовывать в список.

В данном конкретном случаене похоже, что упорядочение особенно важно, поэтому лексического упорядочения, реализованного с помощью sort, может быть достаточно:

  subnet_ids = concat(
    sort(data.aws_subnet_ids.private.ids),
    sort(data.aws_subnet_ids.public.ids),
  )

(потому что преобразование из списка набора строк всписок строк также накладывает лексическое упорядочение, это функционально эквивалентно tolist для наборов строк. Я обычно предпочитаю sort здесь, потому что это подсказка будущему читателю, что результат будет в лексическом порядке.)

Другой вариант - сказать в мире множеств и использовать вместо него setunion:

  subnet_ids = setunion(
    data.aws_subnet_ids.private.ids,
    data.aws_subnet_ids.public.ids,
  )

, так как между этими двумя списками не должно быть дубликатовна самом деле не имеет значения, какой подход вы используете здесь, но для полноты отметим, что в случае, если оба этих набора содержали один и тот же идентификатор подсети, операция setunionбудет дедуплицировать их, потому что каждое уникальное значение может появляться только ноль или один раз в наборе.


В то время, когда я пишу это, где count по-прежнему является основным способом создания одного экземпляра ресурса вэлемент в коллекции, преобразование в список , в конечном итоге обычно требуется, чтобы отдельные экземпляры могли иметь требуемый порядок. Как только for_each будет реализован , будет преимущество в использовании наборов, а не списков в ситуациях, подобных этой:

resource "aws_instance" "per_subnet_example" {
  # resource-level for_each is not implemented at the time of writing,
  # but planned for a future release.
  for_each = setunion(
    data.aws_subnet_ids.private.ids,
    data.aws_subnet_ids.public.ids,
  )

  # ...
}

При использовании for_each над набором вместо count, Terraform будет идентифицировать каждый экземпляр по значению из набора, а не по последовательным индексам, поэтому один экземпляр из этого ресурса может иметь адрес aws_instance.per_subnet_example["subnet-abc123"], и это означает, что при добавлении и удалении элементов из этого набора Terraform может просто создать/ уничтожить соответствующий отдельный экземпляр, а не создавать заново все после изменения в упорядоченной последовательности.

Поставщики Terraform используют подобные наборы в тех местах, где это имеет смысл, чтобы упростить использование этого шаблона for_each.как только он прибудет, но, к сожалению, это означает, что нам нужно в то же время написать несколько явных преобразований типов, чтобы было ясно, что мы работаем с этими значениями в виде последовательности, а не в виде набора.

0 голосов
/ 07 июня 2019

Уже есть похожая проблема, о которой сообщалось для той же проблемы terraform-provider / terraform-provider-aws.

https://github.com/terraform-providers/terraform-provider-aws/issues/7522

и есть обходной путь, упомянутый для того же самого. Для вашего сценария вы можете обойти, как показано ниже

Работа вокруг # 1

locals {                                                            
  private_subnet_ids_string = join(",", data.aws_subnet_ids.private.ids)
  private_subnet_ids_list = split(",", local.private_subnet_ids_string)
  public_subnet_ids_string = join(",", data.aws_subnet_ids.public.ids)
  public_subnet_ids_list = split(",", local.public_subnet_ids_string)             
}

resource "aws_network_acl" "networks" {
    vpc_id = aws_vpc.main.id

    subnet_ids = concat(local.private_subnet_ids_list, local.public_subnet_ids_list)
    [...]
}

Работа вокруг # 2

resource "aws_network_acl" "networks" {
    vpc_id = aws_vpc.main.id

    subnet_ids = concat(tolist(data.aws_subnet_ids.private.ids), tolist(data.aws_subnet_ids.public.ids))
    [...]
}

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...