Ресурсы: главный строительный блок
Ресурс — это единица инфраструктуры, которой управляет 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 выдаёт ошибку.
- Менять имя ресурса. Переименование
web→serverTerraform видит как «удали старое, создай новое», а не «переименуй».
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. Ссылки на атрибуты автоматически строят граф зависимостей. Дальше посмотрим, как этот граф работает в деталях.