Ресурсы: главный строительный блок

Ресурс — это единица инфраструктуры, которой управляет Terraform: сервер, бакет, запись DNS. 95% вашего HCL-кода — это блоки resource.

Каждый ресурс имеет адрес вида тип.имя. Этот адрес — его уникальный идентификатор и в коде, и в графе, и в state. Запомните его — на нём держится всё.

Блок resource объявляет, что какой-то объект должен существовать. У него три части: тип ресурса (определяется провайдером, например aws_instance), локальное имя (придумываете вы, например web) и тело с аргументами. Провайдер знает, как этот объект создать, прочитать, обновить и удалить — это жизненный цикл CRUD.

Атрибуты и ссылки

После создания у ресурса появляются атрибуты — значения, которые вычислило облако: ID, IP-адрес, ARN. Их нельзя задать заранее, но можно прочитать и передать в другой ресурс. Так возникает связь:

resource "aws_vpc" "main" {
  cidr_block = "10.0.0.0/16"
}

resource "aws_subnet" "app" {
  vpc_id     = aws_vpc.main.id    # ссылка на атрибут .id
  cidr_block = "10.0.1.0/24"
}

resource "aws_instance" "web" {
  subnet_id = aws_subnet.app.id   # цепочка зависимостей
  ami       = "ami-0abcdef"
}

Здесь aws_subnet.app ссылается на aws_vpc.main.id, а aws_instance.web — на aws_subnet.app.id. Terraform читает эти ссылки и понимает: сначала VPC, потом subnet, потом инстанс. Этот неявный порядок и есть граф зависимостей.

  aws_vpc.main
       |
       v   (web нужен subnet, subnet нужен vpc)
  aws_subnet.app
       |
       v
  aws_instance.web

создаётся СВЕРХУ ВНИЗ, удаляется СНИЗУ ВВЕРХ

Как работает под капотом

Terraform не угадывает порядок — он извлекает зависимости прямо из ссылок и делает топологическую сортировку. Смоделируем это: дан словарь «ресурс → от кого зависит», нужно выдать порядок создания.

deps = {
    "aws_vpc.main": [],
    "aws_subnet.app": ["aws_vpc.main"],
    "aws_instance.web": ["aws_subnet.app"],
    "aws_eip.ip": ["aws_instance.web"],
}

def topo_sort(deps):
    order, visited = [], set()
    def visit(node):
        if node in visited:
            return
        for dep in deps.get(node, []):
            visit(dep)        # сначала зависимости
        visited.add(node)
        order.append(node)
    for node in deps:
        visit(node)
    return order

print("Порядок создания:")
for i, r in enumerate(topo_sort(deps), 1):
    print(f"  {i}. {r}")

«Попробуй сам ▶» — именно так Terraform выстраивает очередь. При удалении он идёт в обратном порядке.

Частые ошибки

  • Хардкод ID вместо ссылки. Прописали vpc_id = "vpc-123" вручную — потеряли связь в графе, и Terraform может создать ресурсы в неверном порядке.
  • Циклические зависимости. A ссылается на B, B на A — граф перестаёт быть ацикличным, Terraform выдаёт ошибку.
  • Менять имя ресурса. Переименование webserver Terraform видит как «удали старое, создай новое», а не «переименуй».

Best practices

  • Всегда связывайте ресурсы через ссылки на атрибуты, а не через захардкоженные ID — это и документация, и корректный граф.
  • Давайте ресурсам осмысленные имена по роли (web, primary_db), а не по типу (instance1).
  • Используйте lifecycle-блок (prevent_destroy, create_before_destroy) для критичных ресурсов.

Разбор глубже

У каждого ресурса есть особый вложенный блок lifecycle, управляющий тем, как Terraform применяет изменения. Аргумент create_before_destroy = true меняет порядок при пересоздании: сначала создаётся новый ресурс, и только потом удаляется старый — это спасает от простоя, когда ресурс нельзя «погасить» ни на секунду. Аргумент prevent_destroy = true ставит предохранитель: Terraform откажется удалять такой ресурс, что критично для продакшен-баз данных. А ignore_changes говорит игнорировать дрифт по конкретным полям — например, если автоскейлер сам меняет число инстансов и спорить с ним не нужно.

Полезно различать аргументы и атрибуты ресурса. Аргументы вы задаёте сами (instance_type, ami) — это вход. Атрибуты вычисляет провайдер после создания (id, arn, public_ip) — это выход, который заранее неизвестен и потому называется computed. Именно computed-атрибуты создают зависимости: когда ресурс B ссылается на A.id, Terraform понимает, что A нужно создать первым, потому что без него значение id просто не существует. Эта связь «вход одного = вычисленный выход другого» и есть фундамент всего графа.

Итог: ресурс — главный блок Terraform с адресом тип.имя и жизненным циклом CRUD. Ссылки на атрибуты автоматически строят граф зависимостей. Дальше посмотрим, как этот граф работает в деталях.

Проверьте себя
1. Как Terraform определяет порядок создания ресурсов?
AПо алфавиту имён
BПо порядку строк в файле
CПо ссылкам на атрибуты — строит граф и делает топосортировку
DСлучайным образом
2. Что произойдёт, если переименовать ресурс с web на server?
ATerraform просто переименует его
BTerraform удалит старый ресурс и создаст новый
CНичего, имена не важны
DОшибка валидации