# ============================================================================= # Пример: доменные зоны компании и записи DNS — целиком через Terraform. # # Задача: держать доменные зоны и их записи в коде (IaC), а не руками в ЛК. # # Ресурсы провайдера Nubes: # - nubes_zones_v2 (сервис 109) — DNS-зона; # - nubes_zones_v2_record (подресурс зоны, record) — запись внутри зоны # (A / CNAME / …). Записи — ОТДЕЛЬНЫЕ ресурсы, поэтому ими можно управлять # адресно: добавить/изменить/удалить одну запись, не трогая зону целиком. # # ⚠️ В HCL пишется `name` поля из схемы (snake_case): `zone_name`, `record_name`, # `record_type`, `record_input`, `record_ttl`. `code` (camelCase) — внутреннее # имя в API, в HCL НЕ используется. # # КАК РАСШИРЯТЬ: чтобы добавить/убрать зону или запись — правьте `var.zones` # (в variables.tf или terraform.tfvars) и делайте `terraform apply`. # Ключи карт (`a`, `b`, `www`, …) — это АДРЕСА объектов в state. Меняйте их # осознанно: смена ключа = «удалить старый объект + создать новый», а не # «переименовать». Само значение `zone_name`/`record_input` меняется обычным # `apply`. # ============================================================================= # ── locals.records: «уплощаем» вложенную структуру «зоны → записи» ──────────── # # var.zones — карта «зона → { zone_name, records }», где records — ещё одна # карта «имя записи → { type, values }». # # Ресурсу записи нужен ОДИН плоский набор, где у каждого элемента УНИКАЛЬНЫЙ # ключ: тогда `for_each` создаст ровно по одному ресурсу на запись и даст # стабильные адреса вида `nubes_zones_v2_record.record["a:www"]`. # # `merge([ ... ]...)` — разворот (`...`) списка карт в один вызов merge — # склеивает карты из каждой зоны в общую. Ключ собираем как "<зона>:<запись>", # чтобы записи с одинаковыми именами в разных зонах не столкнулись. locals { records = merge([ for zk, z in var.zones : { for rk, r in z.records : "${zk}:${rk}" => { zone = zk # какая зона (ключ карты var.zones) name = rk # имя записи type = r.type # тип записи (A / CNAME / …) values = r.values # список значений записи } } ]...) } # ── Зоны ───────────────────────────────────────────────────────────────────── # `for_each = var.zones` — по одному ресурсу на каждый КЛЮЧ карты зон. # `each.key` — короткое имя зоны (a, b, …), `each.value` — её параметры. # `resource_name` — человекочитаемое имя ресурса в Nubes; `zone_name` — сам # домен (FQDN зоны). resource "nubes_zones_v2" "zone" { for_each = var.zones resource_name = "dns-zone-${each.key}" zone_name = each.value.zone_name } # ── Записи ─────────────────────────────────────────────────────────────────── # `for_each = local.records` — по одному ресурсу на запись (плоская карта выше). # `zones_v2_id` — ссылка на id СВОЕЙ зоны (через for_each зоны): Terraform сам # построит зависимость «зона создаётся раньше своих записей». # # `record_input` — платформа ждёт JSON-массив объектов `[{ record = "<значение>" }, # …]`, поэтому собираем его из плоского списка `values` через `jsonencode`. # Одна запись может иметь НЕСКОЛЬКО значений (напр. два A-адреса) — это одна # запись с массивом, а не две отдельные. resource "nubes_zones_v2_record" "record" { for_each = local.records zones_v2_id = nubes_zones_v2.zone[each.value.zone].id record_name = each.value.name record_type = each.value.type record_input = jsonencode([for v in each.value.values : { record = v }]) record_ttl = 3600 }