Проблемы с заказом загрузки файлов JSON в пользовательские дочерние модули в Terraform

#terraform #terraform-provider-aws

Вопрос:

Я создал собственный дочерний модуль, который создает множество уникальных ресурсов AWS (сетевой адаптер, EIP, пара ключей, экземпляр EC2, группа безопасности, хранилище Glacier, пользователь/группа/политика IAM и т. Д.). В нашей сфере деятельности, когда новый клиент регистрируется, он получает один из этих дочерних модулей, поэтому каждый экземпляр модуля немного уникален и автономен, но все они состоят из одних и тех же компонентов.

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

Вот моя структура папок:

 main.tf
providers.tf
variables.tf
instances/
|_
  server01.json
  server02.json
  etc...
modules/instance/
|_
  main.tf
  variables.tf
  providers.tf
  outputs.tf
 

Корневой модуль main.tf файл загрузит все файлы JSON в дочерние модули, используя выражение for_each:

 locals {
  json_data = [for f in fileset("./instances/", "*.json"): jsondecode(file("./instances/${f}"))]
}

module "instance" {
  for_each = { for k,v in local.json_data: k => v }
  source = "./modules/instance"
  server_name = each.value.server_name
  server_dns = each.value.server_dns
  server_private_ip = each.value.server_private_ip
  server_customer = each.value.server_customer
  <<more input properties defined...>>
}
 

Проблема-Допустим, что завтра клиент, связанный с server01.json, решит покинуть наш сервис. Я подумал, что простым способом отключить их дочерний модуль в Terraform было бы пометить файл JSON другим расширением (например .bak), чтобы main.tf файл проигнорирует загрузку этого в список json_data и увидит, что все ресурсы, связанные с этим экземпляром, больше не требуются.
На самом деле происходит то, что Terraform сравнивает файл server02.json с файлом состояния module.instance[«0»]. (так как теперь он первый в списке файлов JSON) и видит целую кучу различий и вещей, которые нужно уничтожить/воссоздать. Затем он также распознает, что module.instance[«1»] был удален (поскольку больше нет 2 файлов JSON), и захочет удалить все ресурсы, связанные с файлом server02.json.
Это проблема, потому что она требует уничтожения и восстановления экземпляра EC2, но в этих экземплярах должно сохраняться много данных.

У кого-нибудь есть предложения о том, как справиться с подобным сценарием?
Есть ли способ, чтобы файл состояния module.instance[] связал список с именами файлов JSON вместо их порядка?
Был ли пользовательский модуль неправильным подходом для этого случая использования?

Любые предложения или предложения будут очень признательны!

Ответ №1:

Механизм ресурсов for_each Terraform использует ключи предоставленной карты в качестве ключей для идентификации отдельных экземпляров ресурса.

В вашем случае вы использовали for выражение для проецирования списка на карту, используя строковое представление индекса элемента в качестве ключа, и поэтому Terraform, в свою очередь, эффективно использует индекс элемента в качестве ключа экземпляра, следуя вашим инструкциям для этого.

Если вы хотите, чтобы Terraform отслеживала экземпляры с использованием какой-либо другой схемы ввода, вам нужно будет создать карту, содержащую ключи, которые вы хотите использовать в Terraform. Чтобы выполнить указанное вами требование, это означает сопоставление имени файла с данными JSON, которое вы, возможно, могли бы построить следующим образом:

 locals {
  instances = tomap({
    for fn in fileset("${path.module}/instances/", "*.json") :
    fn => jsondecode(file("${path.module}/instances/${fn}"))
  })
}
 

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

 module "instance" {
  source   = "./modules/instance"
  for_each = local.instances

  server_name       = each.value.server_name
  server_dns        = each.value.server_dns
  server_private_ip = each.value.server_private_ip
  server_customer   = each.value.server_customer
  # ...
}
 

Это приведет к созданию экземпляров модулей с адресами, подобными следующим:

  • module.instance["server01.json"]
  • module.instance["server02.json"]

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

Комментарии:

1. Это блестяще и именно то, что я искал! Спасибо! Теперь я понимаю, что я делал не так. Я все еще новичок в декларативном синтаксисе в Terraform… Трудно отказаться от обычного императивного мышления в области кодирования.