linuxlab.io
Учебники▾
  • Линукс и сети
    Файловая система, процессы, TCP/IP, BGP и OSPF
    →
  • Terraform и IaC
    HCL, state, plan/apply на sandbox LocalStack
    →
  • Git и GitHub
    Объектная модель, plumbing, ветвление, GitHub Actions
    →
Все учебники →
ЦеныО платформеВойтиСоздать аккаунт
/
Intro
Lessons
Footer
linuxlab-УчебникиЦеныО платформеКонфиденциальность и куки
Copyright © 2026 LinuxLab. Все права защищены.
linuxlab.io
Учебники▾
  • Линукс и сети
    Файловая система, процессы, TCP/IP, BGP и OSPF
    →
  • Terraform и IaC
    HCL, state, plan/apply на sandbox LocalStack
    →
  • Git и GitHub
    Объектная модель, plumbing, ветвление, GitHub Actions
    →
Все учебники →
ЦеныО платформеВойтиСоздать аккаунт
/
  • Введение
  • Уроки
  • How it works
  • База знаний
  • Шпаргалка
  • Capstone
  • Собеседование
Cluster

← все кластеры

Модули: inputs, outputs, sources, версии

Граница ответственности модуля, источники (local, registry, git, s3), pinning версий и semver, composition vs flat root, типовые антипаттерны. Что хочет услышать lead - где провести границу и зачем.

6 вопросов · ~23 мин чтения

Questions

На этой странице

  1. 01Как провести границу модуля? Что выносить в inputs, что в outputs?
  2. 02Какие источники модулей бывают и как пинить версию?
  3. 03Module composition vs flat root: когда дробить, когда нет?
  4. 04Использовать публичный registry-модуль или писать свой?
  5. 05Какие антипаттерны в модулях ты встречал?
  6. 06Как делаешь breaking change в модуле, который используют 20 команд?

#module-boundary-inputs-outputs

juniorчасто

Как провести границу модуля? Что выносить в inputs, что в outputs?

Что отвечать

Модуль - чёрный ящик с контрактом. Inputs - всё что меняется между использованиями (имя bucket'а, регион, теги). Outputs - всё что понадобится снаружи для других ресурсов (ARN, endpoint, id). Внутри модуля - locals и реализация. Антипаттерн: модуль с 40 input'ами, где половина дублирует поля ресурса AWS - тогда модуль не упрощает, а добавляет слой. Хороший модуль скрывает решения, не транзакционно пробрасывает каждую опцию провайдера.

Что хотят услышать

Senior должен: - назвать критерий «хорошего» input'а: меняется между вызовами, не имеет разумного default'а в контексте модуля - сказать что output должен возвращать то, что нужно соседним ресурсам, а не «всё на всякий случай» - каждый output это часть public API, его удаление - breaking change - различить три уровня: variable (вход), local (внутренняя константа), output (выход). Local НЕ output - не утекает - упомянуть `description` обязательно для всех variable и output, особенно для shared-модулей: это документация, читают коллеги через `terraform-docs`

Подводные камни

  • ✗ Сделать input на каждый атрибут AWS-ресурса - модуль превращается в alias для ресурса, без добавленной ценности
  • ✗ Не дать default'ы на input'ы там, где разумный default есть - каждый вызывающий пишет одно и то же
  • ✗ Выложить через output sensitive значение без `sensitive = true` - случайно засветишь в CI-логах

Follow-up

  • ? Чем отличается `variable` от `local` концептуально?
  • ? Можно ли давать default на input типа `object()`? Что внутри?
  • ? Когда output должен быть `sensitive = true`?

Глубина в базе знаний

  • [[tf-module-basics]]
  • Контракт модуля: input variables и outputs
  • locals: вычисляемые внутренние имена
  • Блок output: что Terraform возвращает наружу
tags: modules, fundamentals

#module-sources-and-pinning

intermediateчасто

Какие источники модулей бывают и как пинить версию?

Что отвечать

Источники: local path (`./modules/x`), Terraform registry (`hashicorp/vpc/aws`), git (`git::https://github.com/...`), HTTP archive, S3, GCS. Pinning: registry поддерживает `version = "~> 4.0"` через semver. Git - через `ref=v1.2.3` в URL (тег, branch, sha). `~> 4.0` значит «4.x где x ≥ 0», `~> 4.2.0` - «4.2.x». Антипаттерн: git без `ref` или `ref=main` - на разных машинах разные версии, Terraform даже warning'а не даст.

Что хотят услышать

Senior должен: - назвать что registry-модули version-resolved через `~>`-constraint с автоматическим выбором max-патч-версии - для git предпочесть `ref=<тег>` или `ref=<sha>` - tag можно переписать, sha immutable - объяснить что `~>` (pessimistic) удобнее `>=` (open-ended): патчи прилетают сами, мажор требует осознанного апгрейда - упомянуть `terraform get -update` или `terraform init -upgrade` для пересчёта pinned-версий внутри constraint - назвать что `terraform init` качает модули в `.terraform/modules` и фиксирует их там; lockfile фиксирует ТОЛЬКО provider-версии, не модули

Подводные камни

  • ✗ Использовать `ref=main` или ветку - вчера работало, сегодня неожиданный diff
  • ✗ Думать что `.terraform.lock.hcl` фиксирует версии модулей - не фиксирует, только провайдеры
  • ✗ Поставить `>= 4.0` без верхней границы - 5.0 прилетит автоматом и сломает совместимость

Follow-up

  • ? Что точно делает `~> 4.2` и `~> 4.2.0` - разница?
  • ? Зачем нужен `terraform init -upgrade` после смены `version`?
  • ? Чем модули отличаются от провайдеров в lockfile-стратегии?

Глубина в базе знаний

  • Источники модулей: local, git, registry, archive, S3
  • [[tf-module-versioning]]
  • Version constraints в Terraform: required_version и провайдеры
  • .terraform.lock.hcl: фиксация версий провайдеров
tags: modules, versioning

#module-composition-vs-flat

intermediateиногда

Module composition vs flat root: когда дробить, когда нет?

Что отвечать

Flat root - всё в одном `main.tf` или нескольких файлах одного root-модуля. Composition - root собран из child-модулей, каждый делает свою часть (vpc, eks, rds). Flat работает до ~30-50 ресурсов на root; дальше начинаются проблемы: plan дольше, blast radius больше, git-conflict'ы чаще. Composition разделяет инфру на куски, каждый со своим состоянием. Но composition сама по себе не панацея: слишком мелкие модули («модуль на каждый ресурс») создают шум без пользы.

Что хотят услышать

Senior должен: - назвать критерий дробления: уровень самостоятельности (VPC живёт без EKS), частота изменений, blast radius - сказать что нет смысла делать модуль из 1-2 ресурсов; модуль должен инкапсулировать решение, а не группу alias'ов - упомянуть что каждый root-модуль = свой state. Composition внутри одного root - просто файлы; разделение state - это отдельные root-модули с outputs через remote_state или ssm-параметры - назвать root-модуль как «единицу деплоя»: всё что один человек раскатывает одной командой, должно жить в одном root

Подводные камни

  • ✗ Сделать «модуль на каждый ресурс» думая что это «чистый код» - композиционная сложность взлетает, отладка хуже
  • ✗ Дробить root на десятки маленьких state-файлов без причины - outputs между ними через remote_state становятся узким местом
  • ✗ Не выделять stable-уровень (network, IAM) отдельно от часто меняющегося (apps) - blast radius всего проекта

Follow-up

  • ? Когда `terragrunt` помогает в composition?
  • ? Чем плох «модуль на каждый ресурс»?
  • ? Как делить state на env'ы: один root + workspaces или несколько root'ов?

Глубина в базе знаний

  • Композиция модулей: module of modules, передача провайдеров
  • [[tf-module-basics]]
  • Terraform Stacks, нативная multi-stack оркестрация
tags: modules, composition, architecturebook: mastering.terraform.epub:ch8

#module-where-public-or-local

intermediateиногда

Использовать публичный registry-модуль или писать свой?

Что отвечать

Зависит. Готовый модуль (`terraform-aws-modules/vpc/aws`) экономит недели, покрывает edge-cases которые ты не предусмотришь, поддерживается сообществом. Минус: 100+ input'ов «на все случаи жизни», читать сложно, твой use case часто хочет subset. Свой модуль проще под твою команду, документированнее под твою конвенцию, но писать и поддерживать на тебе. Компромисс: оборачивать чужой в свой «прихожую» с урезанным API.

Что хотят услышать

Senior должен: - назвать что готовые модули хороши на старте - VPC, RDS, EKS уже отлажены тысячами пользователей - сказать про подход обёртки: свой модуль как facade над публичным, фиксируешь только нужные тебе input'ы, добавляешь конвенцию тегирования - предупредить что любая зависимость от внешнего модуля - читай changelog при апгрейде. Бывают breaking changes между мажорами - упомянуть что для критичной инфры (security boundaries) лучше свой - чужой может незаметно поменять default'ы между версиями

Подводные камни

  • ✗ Использовать community-модуль с `version = "~> 1.0"` и забыть - через год прилетает 1.20 с новыми default'ами
  • ✗ Форкнуть публичный модуль и забыть синкаться - через пару лет свой fork становится legacy без security-патчей
  • ✗ Писать свой VPC-модуль с нуля «потому что чужой громоздкий» - тратишь недели на воспроизведение того, что уже есть

Follow-up

  • ? Что лучше: использовать `terraform-aws-modules/vpc/aws` напрямую или обернуть в свой?
  • ? Как ты бы пинил версию community-модуля в проде?
  • ? Когда форкнуть чужой модуль оправдано?

Глубина в базе знаний

  • Источники модулей: local, git, registry, archive, S3
  • [[tf-module-basics]]
  • [[tf-when-not-to-use]]
tags: modules, decisions

#module-antipatterns

seniorредко

Какие антипаттерны в модулях ты встречал?

Что отвечать

Самые частые: модуль с count=0/1 для условного создания ресурсов (теперь это `for_each` или toggle через входной флаг); модуль, который оборачивает один ресурс без добавления значения; модули с Terraform-логикой в путанной структуре `for_each`-of-`for_each`-of; «универсальные» модули с 80 input'ами, где половина никогда не используется; жёстко зашитые имена ресурсов вместо variable.

Что хотят услышать

Senior должен: - указать на «модуль-обёртку без значения»: если внутри один resource и пять input'ов один в один в его атрибуты - значит модуль не упрощает, можно убрать - назвать «module bloat»: 80+ input'ов означает что модуль решает слишком много задач, разделить - сказать про count для условного создания как legacy-подход. С for_each и `for_each = var.enabled ? toset([\"x\"]) : toset([])` читаемее - упомянуть что нельзя коммитить `.terraform/` директорию вместе с модулем - её содержимое provider-binary'и под локальную ОС

Подводные камни

  • ✗ Сделать `count = var.create ? 1 : 0` - выходит ресурс с адресом `module.x.aws_s3_bucket.demo[0]`, что ломает refactoring
  • ✗ Использовать `null_resource` внутри модуля как hack для side-effect - работает странно, идемпотентность размывается
  • ✗ Захардкодить tags = { Project = "my-app" } внутри модуля - не переиспользовать; теги должны быть variable с merge'ом

Follow-up

  • ? Как сделать optional ресурс внутри модуля без `count = 0/1`?
  • ? Почему `null_resource` чаще антипаттерн, чем решение?
  • ? Когда модуль становится «слишком большим» по input'ам?

Глубина в базе знаний

  • [[tf-module-basics]]
  • count и for_each: несколько ресурсов из одного блока
  • [[tf-when-not-to-use]]
tags: modules, antipatternsbook: Packt.Terraform.Cookbook.pdf:ch5

#module-versioning-breaking-changes

seniorиногда

Как делаешь breaking change в модуле, который используют 20 команд?

Что отвечать

Главное правило: breaking change - это мажорная версия, без исключений. Тег `v2.0.0`, в README - migration guide, в CHANGELOG - что сломалось. Команды апгрейдятся в своём темпе. Если изменение можно сделать backward-compatible через новый optional input - сделай так и пометь старый как deprecated в `description`. Внутри модуля для переноса ресурсов в state - `moved {}` блок (с 1.1+). Это позволяет переименовать ресурс или вынести его в подмодуль без destroy+create.

Что хотят услышать

Senior должен: - назвать semver как контракт: patch = bugfix, minor = новое поведение с дефолтом=старое, major = breaking - сказать про `moved {}` блок как способ безболезненно реструктурировать внутренности модуля - state user'а переедет автоматически на их следующем apply - упомянуть deprecated через `description = \"DEPRECATED: use bar instead\"` - не строгий механизм, но видно в terraform-docs - назвать что для shared-модулей нужна CI на сам модуль: terraform test, проверка backward-compat через сценарии с разными комбинациями input'ов

Подводные камни

  • ✗ Переименовать ресурс внутри модуля без `moved {}` блока - все потребители получают destroy+create на их следующем apply
  • ✗ Выпустить minor с breaking change - 20 команд проснутся утром со сломанным CI
  • ✗ Назвать tag без `v` префикса (`1.2.3` вместо `v1.2.3`) - Terraform не поймёт version constraint correctly

Follow-up

  • ? Как `moved {}` блок помогает в module-refactoring без destroy?
  • ? Чем pessimistic constraint `~> 2.0` помогает потребителям при breaking change?
  • ? Что положить в CHANGELOG для major-релиза модуля?

Глубина в базе знаний

  • [[tf-module-versioning]]
  • moved блок: переименование без destroy
  • Паттерны рефакторинга: count→for_each, split files, extract module
  • [[tf-test-framework]]
tags: modules, versioning, breaking-changesbook: mastering.terraform.epub:ch9
Footer
linuxlab-
Copyright © 2026 LinuxLab. Все права защищены.
Учебники
Цены
О платформе
Конфиденциальность и куки