# Как terraform init подтягивает модули _Основы Terraform · TerraformLab Knowledge Base_ **TL;DR:** Когда в HCL есть блок module, terraform init скачивает исходники модуля (из registry, git или локальной папки) в .terraform/modules/. В этом курсе модули не пишем; статья, обзор как механика работает. ## Что такое модуль (коротко) Модуль, это переиспользуемая папка с HCL. У модуля есть свои переменные на вход, свои outputs на выход и свои ресурсы внутри. В корневом HCL модуль подключается так: ```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 (публичный или приватный) ```hcl source = "terraform-aws-modules/vpc/aws" version = "5.8.1" ``` Формат `OWNER/NAME/PROVIDER`. Terraform идёт в registry.terraform.io, находит этот модуль, скачивает версию. ### Git-репозиторий ```hcl source = "git::https://github.com/myorg/tf-modules.git//vpc?ref=v1.2.3" ``` - `git::`, префикс схемы. - `//vpc`, путь внутри репо (после двойного слеша). - `?ref=v1.2.3`, тэг, ветка или коммит. Поле `version` для git-источников **не работает**, версия задаётся через `?ref=`. ### Локальный путь ```hcl source = "./modules/vpc" source = "../shared/modules/iam" ``` Относительный путь от файла, где написан `module`. Скачивать нечего, terraform просто использует папку как есть. ### S3 / GCS / HTTP Реже, но возможно: `source = "s3::https://s3.amazonaws.com/bucket/modules.zip"`. Архив скачивается и распаковывается. ## Что делает `terraform init` с модулями 1. Парсит HCL, находит все блоки `module "..." { source = ... }`, включая модули внутри модулей (recursive). 2. Для каждого источника решает, нужно ли качать (registry, git, http , да; локальный путь, нет). 3. Скачивает исходники в подпапку `.terraform/modules//`. 4. Записывает план «какой модуль где живёт» в `.terraform/modules/modules.json`. При следующих `init` без изменений модули не перекачиваются, используются кешированные. После изменения `source` или `version` нужен `terraform get -update` или `init -upgrade`. ## Version constraint для registry-модулей ```hcl module "vpc" { source = "terraform-aws-modules/vpc/aws" version = "~> 5.8" } ``` Pessimistic-оператор (см. [tf-version-constraints](/terraform/kb/tf-version-constraints.md)) работает так же, как для провайдеров: `~> 5.8` разрешит 5.8.x и 5.9.x, но не 6.0. Для git-источников версия задаётся ref'ом: `?ref=v1.2.3` (тэг), `?ref=main` (ветка, но это плохая идея в проде), `?ref=abc123` (коммит). ## Почему модули не в lockfile [tf-lockfile](/terraform/kb/tf-lockfile.md) фиксирует только провайдеры. Для модулей «зафиксированной версии с хешем» нет: - Из 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. ## Команды ```bash terraform init ``` Скачивает модули, если они объявлены в HCL и ещё не закешированы. ```bash terraform init -upgrade ``` Принудительно перекачивает модули с учётом version constraints (для registry) и refs (для git). ```bash terraform get -update ``` Альтернатива init -upgrade именно для модулей, без переподтяжки провайдеров и backend. ```bash cat .terraform/modules/modules.json | jq ``` Видеть, какие модули установлены и из каких источников. Полезно при поиске «откуда взялась эта папка». ## См. также - [terraform init: первая команда в любом проекте](/terraform/kb/tf-init.md) - [.terraform.lock.hcl: фиксация версий провайдеров](/terraform/kb/tf-lockfile.md) - [Version constraints в Terraform: required_version и провайдеры](/terraform/kb/tf-version-constraints.md) - [HCL: язык, на котором пишут Terraform](/terraform/kb/hcl-syntax.md)