Представь, что тебе нужно поднять не одну виртуалку, а три одинаковых - под кластер, под нагрузочное тестирование или просто три воркера для очереди. Самый прямолинейный путь - скопировать блок resource три раза, поменять имена и жить дальше. Через неделю прилетает задача "сделай пять штук", и ты снова идёшь копипастить, а потом ловишь опечатку в четвёртой копии и полдня дебажишь, почему одна ВМ не та.
Инфраструктура как код (iac) придумана ровно для того, чтобы такого не было. Если у тебя есть N одинаковых сущностей, описание должно быть ОДНО, а количество - параметром. В Terraform за это отвечает мета-аргумент count. Он работает с любым ресурсом и говорит коротко: "сделай вот этого добра ровно столько-то копий". Это первый и самый простой инструмент размножения ресурсов в terraform yandex cloud, и в этом уроке мы разберём не только как он создаёт копии, но и где он коварно подставляет.

Механика: count, count.index и адресация копий
Идея простая. Ты добавляешь в блок ресурса строчку count = N, и Terraform создаёт N экземпляров этого ресурса. Внутри блока становится доступен объект count.index - это порядковый номер текущей копии, нумерация с нуля. То есть при count = 3 у тебя будут индексы 0, 1 и 2. Через count.index удобно генерировать уникальные имена, раздавать разные IP из подсети, тянуть значения из списка по позиции.
Когда ресурс размножен через count, Terraform хранит его уже не как одиночку, а как список экземпляров. Из-за этого меняется адресация:
- resource_type.name[0] - обращение к конкретной копии по индексу. Например, yandex_compute_instance.worker[0] - первая виртуалка.
- resource_type.name
- - так называемый splat-оператор (звёздочка). Он разворачивает все копии в список значений. Например, yandex_compute_instance.worker
- .id даст список из всех id созданных машин.
Практика: три одинаковые ВМ в Yandex Cloud
Соберём типовой пример - три одинаковые виртуалки. Сначала минимальный каркас провайдера и сеть, потом сами машины через count.
Код: Выделить всё
terraform {
required_providers {
yandex = {
source = "yandex-cloud/yandex"
}
}
}
provider "yandex" {
zone = "ru-central1-a"
}
resource "yandex_vpc_network" "net" {
name = "demo-net"
}
resource "yandex_vpc_subnet" "subnet" {
name = "demo-subnet"
zone = "ru-central1-a"
network_id = yandex_vpc_network.net.id
v4_cidr_blocks = ["10.10.0.0/24"]
}
resource "yandex_compute_instance" "worker" {
count = 3
name = "worker-${count.index}"
zone = "ru-central1-a"
resources {
cores = 2
memory = 2
}
boot_disk {
initialize_params {
image_id = "fd8vmcue7aajpmeo39kk"
}
}
network_interface {
subnet_id = yandex_vpc_subnet.subnet.id
nat = true
}
}
Теперь вытащим результаты через splat. Выводы (outputs) - идеальное место показать, как разворачиваются все копии разом:
Код: Выделить всё
output "worker_names" {
value = yandex_compute_instance.worker[*].name
}
output "worker_external_ips" {
value = yandex_compute_instance.worker[*].network_interface.0.nat_ip_address
}
output "first_worker_id" {
value = yandex_compute_instance.worker[0].id
}
count хорошо ложится не только на ВМ. Тот же приём работает, например, для сервисных аккаунтов, когда нужно завести несколько одинаковых "пользователей-роботов":
Код: Выделить всё
resource "yandex_iam_service_account" "robot" {
count = 3
name = "robot-sa-${count.index}"
}
А вот теперь главная ловушка, ради которой стоило читать весь урок. count нумерует копии по позиции в списке, а не по имени или какому-то стабильному ключу. Привязка идёт жёстко к индексу: worker[0], worker[1], worker[2].
Допустим, у тебя три воркера, и ты решил убрать СРЕДНИЙ - worker-1. В нормальном мире ты ждёшь, что удалится одна машина, а две останутся на месте. В мире count происходит другое. Ты уменьшаешь count до 2, и Terraform думает так: раньше был список из трёх, теперь из двух, значит лишний элемент - тот, что в конце, [2]. Но содержимое-то сдвинулось. Машина, которая была worker[2], теперь должна стать worker[1]. И Terraform на полном серьёзе планирует:
- worker[1] - изменить (потому что теперь там данные бывшего worker[2], имя поменяется на worker-1);
- worker[2] - удалить.
Второе ограничение тоньше. Значение count вычисляется на стадии планирования, ДО применения. Поэтому в count нельзя подставлять величину, которая зависит от ещё не созданных ресурсов. Классика - попытка сделать count одного ресурса равным количеству или атрибуту другого ресурса, который сам ещё не существует:
Код: Выделить всё
# Так Terraform упрётся: значение count неизвестно на этапе плана
resource "yandex_compute_instance" "vm" {
count = length(yandex_vpc_subnet.subnet[*].id)
# ...
}
Когда count - это нормальноЕсли коротко: count - это про "сколько штук", а не про "какие именно". Когда тебе важна стабильная идентичность каждого элемента, count - не твой инструмент, и в следующих уроках мы возьмём for_each, который привязывает экземпляры к ключам, а не к позициям.
Не подумай, что count - зло. Он отлично подходит, когда выполнены два условия: копии действительно взаимозаменяемы (никому не важно, кто из них [0], а кто [1]) и ты не планируешь выдёргивать элементы из середины. Типичные хорошие сценарии:
- пул одинаковых stateless-воркеров, который масштабируется только числом;
- условное создание ресурса через count = var.enabled ? 1 : 0 (включить/выключить блок флагом);
- N однотипных сервисных аккаунтов или ключей, где порядок не имеет значения.
Мини-лаба: повтори руками
- Подними три ВМ через count = 3 с именами worker-0..worker-2, прогони terraform plan apply, найди в state суффиксы [0], [1], [2].
- Добавь output со splat worker
- .name и отдельный output с worker[0].id, сравни вывод list против одиночного значения.
- Теперь самое важное: уменьши count до 2, сделай plan (НЕ apply, если жалко машин) и внимательно прочитай, что Terraform собирается пересоздавать. Убедись, что лишним он считает хвост, а не "среднюю" машину.
- Поэкспериментируй: оберни ресурс в count = var.create ? 1 : 0 и переключи флаг - почувствуй count как условный выключатель.
- С какого числа начинается нумерация count.index и где её удобно применять?
- Чем отличается обращение worker[0] от worker
- и что вернёт каждое?
- Что произойдёт, если из списка ресурсов с count удалить элемент из середины, и почему это опасно для ВМ с данными?
- Почему нельзя задать count, равный атрибуту другого ресурса, создаваемого в том же apply?
count = N штампует N копий одного ресурса, count.index (с нуля) даёт каждой копии уникальный номер для имён и адресов. Доступ к копиям - через индекс [0] или splat [*]. Главный подводный камень - привязка к позиции: удаление из середины сдвигает индексы и пересоздаёт всё правее, поэтому расти и режь только с хвоста. И помни: count хочет число заранее, на этапе плана, и не дружит со значениями, которые ещё не вычислены. Когда копии взаимозаменяемы - count идеален; когда важна стабильная идентичность - дождись урока про for_each.