Что такое модуль (коротко)
Модуль, это переиспользуемая папка с HCL. У модуля есть свои переменные на вход, свои outputs на выход и свои ресурсы внутри. В корневом HCL модуль подключается так:
module "vpc" {source = "terraform-aws-modules/vpc/aws"
version = "5.8.1"
name = "demo-vpc"
cidr = "10.0.0.0/16"
}
Курс не пишет свои модули в beginner-треке, это интермедиат- материал. Но знать, что делает init при наличии чужих модулей, полезно: рано или поздно встретишь чужой репо.
Источники модулей в поле source
Поле source определяет, где terraform возьмёт исходники. Форматов
несколько:
Terraform Registry (публичный или приватный)
source = "terraform-aws-modules/vpc/aws"
version = "5.8.1"
Формат OWNER/NAME/PROVIDER. Terraform идёт в registry.terraform.io,
находит этот модуль, скачивает версию.
Git-репозиторий
source = "git::https://github.com/myorg/tf-modules.git//vpc?ref=v1.2.3"
git::, префикс схемы.//vpc, путь внутри репо (после двойного слеша).?ref=v1.2.3, тэг, ветка или коммит.
Поле version для git-источников не работает, версия задаётся
через ?ref=.
Локальный путь
source = "./modules/vpc"
source = "../shared/modules/iam"
Относительный путь от файла, где написан module. Скачивать нечего,
terraform просто использует папку как есть.
S3 / GCS / HTTP
Реже, но возможно: source = "s3::https://s3.amazonaws.com/bucket/modules.zip".
Архив скачивается и распаковывается.
Что делает terraform init с модулями
- Парсит HCL, находит все блоки
module "..." { source = ... }, включая модули внутри модулей (recursive). - Для каждого источника решает, нужно ли качать (registry, git, http , да; локальный путь, нет).
- Скачивает исходники в подпапку
.terraform/modules/<key>/. - Записывает план «какой модуль где живёт» в
.terraform/modules/modules.json.
При следующих init без изменений модули не перекачиваются,
используются кешированные. После изменения source или version
нужен terraform get -update или init -upgrade.
Version constraint для registry-модулей
module "vpc" {source = "terraform-aws-modules/vpc/aws"
version = "~> 5.8"
}
Pessimistic-оператор (см. tf-version-constraints) работает так
же, как для провайдеров: ~> 5.8 разрешит 5.8.x и 5.9.x, но не 6.0.
Для git-источников версия задаётся ref'ом: ?ref=v1.2.3 (тэг),
?ref=main (ветка, но это плохая идея в проде), ?ref=abc123
(коммит).
Почему модули не в lockfile
tf-lockfile фиксирует только провайдеры. Для модулей «зафиксированной версии с хешем» нет:
- Из registry качается ровно та версия, что указана в
version. Если в registry эту версию подменят (не должно, но теоретически): хешей нет, terraform этого не заметит. - Из git хешем работает сам git-ref'ы. Используй коммит-SHA, не ветку, это даёт детерминизм.
Это известное ограничение. В критичных проектах модули часто vendor'ят в монорепо или приватный registry с iммутабельными тэгами.
Подводные камни
source = "./modules/vpc"против"modules/vpc", обе работают, но первый явный. Без точки terraform всё равно поймёт, но IDE и линтеры, иногда нет.- Локальные модули НЕ кешируются в
.terraform/modules/. Они читаются из исходного пути на каждыйplan. Если изменил локальный модуль, изменения видны сразу. versionне работает с git/local источниками. Если в registry- модуле ты привык писатьversion, а потом переехал на git и забыл убратьversion, terraform не падает, но игнорирует поле.- Транзитивные модули. Модуль А внутри тянет модуль Б. После
initоба окажутся в.terraform/modules/. Удалить модуль А, надо сноваinit, чтобы terraform забыл и про Б. terraform get, старая отдельная команда, обновлявшая модули без полного init. С 0.12+ заменена наinit -upgrade, но иногда встречается в старых README.