linuxlab.io
Учебники▾
  • Линукс и сети
    Файловая система, процессы, TCP/IP, BGP и OSPF
    →
  • Terraform и IaC
    HCL, state, plan/apply на sandbox LocalStack
    →
  • Git и GitHub
    Объектная модель, plumbing, ветвление, GitHub Actions
    →
Все учебники →
ЦеныО платформеВойтиСоздать аккаунт
/
Intro
Lessons
Footer
linuxlab-УчебникиЦеныО платформеКонфиденциальность и куки
Copyright © 2026 LinuxLab. Все права защищены.
linuxlab.io
Учебники▾
  • Линукс и сети
    Файловая система, процессы, TCP/IP, BGP и OSPF
    →
  • Terraform и IaC
    HCL, state, plan/apply на sandbox LocalStack
    →
  • Git и GitHub
    Объектная модель, plumbing, ветвление, GitHub Actions
    →
Все учебники →
ЦеныО платформеВойтиСоздать аккаунт
/
  • Введение
  • Уроки
  • How it works
  • База знаний
  • Шпаргалка
  • Capstone
  • Собеседование
home/terraform/kb/Ресурсы и data-источники/tf-count-for-each

kb/resources ── Ресурсы и data-источники ── beginner

count и for_each: несколько ресурсов из одного блока

count создаёт N одинаковых ресурсов по индексам 0..N-1. for_each: ресурсы по ключам из set или map. Правило: count для одинаковых, for_each когда у каждого свои настройки. Если сомневаешься, бери for_each.

view as markdown

Зачем нужны

Допустим, нужно создать пять S3-бакетов: logs-dev, logs-staging, logs-prod, data-dev, data-prod. Можно написать пять resource-блоков. Можно, один с count или for_each.

Это про повторяющиеся ресурсы. Если что-то отличается только индексом или ключом, выносим в count/for_each и не дублируем код.

count. Это самый простой способ

hcl
resource "aws_s3_bucket" "logs" {
  count  = 3
  bucket = "logs-bucket-${count.index}"
}

Это создаст три бакета:

  • aws_s3_bucket.logs[0] с именем logs-bucket-0
  • aws_s3_bucket.logs[1] с именем logs-bucket-1
  • aws_s3_bucket.logs[2] с именем logs-bucket-2

Внутри блока доступна переменная count.index, текущий номер от 0 до N-1.

Чтобы поменять количество, поменяйте число. Terraform увидит «было 3, надо 5» и создаст ещё два.

for_each, когда у каждого свой ключ

hcl
resource "aws_s3_bucket" "regional" {
  for_each = toset(["us", "eu", "ap"])
  bucket = "data-bucket-${each.key}"
}

Создастся три бакета:

  • aws_s3_bucket.regional["us"] с именем data-bucket-us
  • aws_s3_bucket.regional["eu"] с именем data-bucket-eu
  • aws_s3_bucket.regional["ap"] с именем data-bucket-ap

Доступно: each.key (ключ) и each.value (значение): for_each принимает set или map:

hcl
# set, простые ключи, each.key == each.value
for_each = toset(["us", "eu", "ap"])
# map, пара ключ-значение, each.key и each.value разные
for_each = {
  us = { region = "us-east-1", tier = "primary" }
  eu = { region = "eu-central-1", tier = "secondary" }
}
# then: each.key, "us"/"eu", each.value.region, each.value.tier

Главное различие: что происходит при изменении списка

Скажем, у вас три ресурса. Уберите средний.

С count:

hcl
count = 3   →   count = 2

Terraform думает так: «было 3 элемента по индексам 0, 1, 2. Теперь надо 2». Удаляется элемент с индексом 2. Если вы хотели удалить элемент 1. Terraform этого не понимает; он просто сдвинет: индекс 2 пропадёт, элементы 0 и 1 останутся теми же.

Проблема: если убрать элемент из середины списка через count. Terraform пересоздаст всё, что после него. Потому что индексы сдвинутся.

С for_each:

hcl
for_each = toset(["us", "eu", "ap"])  →  for_each = toset(["us", "ap"])

Terraform увидит «был ключ eu, его больше нет», удалит только aws_s3_bucket.regional["eu"]. Остальные не тронет.

Правило большого пальца: если список может меняться в середине, берите for_each. count, только для случаев когда количество растёт/убывает с конца или когда элементы реально взаимозаменяемы.

Когда что использовать

  • count, три одинаковые виртуалки в auto-scaling-like подходе, отказоустойчивость через дублирование, ресурсы где порядок не важен.
  • for_each, почти всегда остальное. Бакеты для разных окружений, IAM-роли для разных сервисов, sg-rules с разными портами.

Если не уверены, берите for_each. Он более явный и безопасный.

Ссылки и аутпуты

hcl
resource "aws_s3_bucket" "regional" {
  for_each = toset(["us", "eu", "ap"])
  bucket   = "data-${each.key}"
}
# одна конкретная
output "us_bucket_arn" {
  value = aws_s3_bucket.regional["us"].arn
}
# все ARN'ы списком
output "all_arns" {
  value = [for k, b in aws_s3_bucket.regional : b.arn]
}
# все ARN'ы по ключу
output "arns_by_region" {
  value = { for k, b in aws_s3_bucket.regional : k => b.arn }
}

Подводные камни

  • count и for_each нельзя одновременно. Только один из них в одном ресурсе.

  • count = 0 или for_each = [], валидно. Терраформ создаст ноль ресурсов. Это легальный способ условно отключить блок: count = var.create ? 1 : 0.

  • for_each требует statically-knowable ключей. Ключи должны быть известны на этапе plan. Нельзя for_each = data.aws_some_thing.dynamic если эти данные становятся известны только после apply другого ресурса. Лечится через зависимости или статические значения.

  • При миграции с count на for_each, пересоздание. Терраформ считает это другим ресурсом. Адреса меняются (x[0] → x["key"]). Лечится через moved {} блок (advanced).

  • Map ключи всегда строки. for_each = { 1 = "a", 2 = "b" } ругнётся: числа надо в строки конвертить: { "1" = "a", "2" = "b" }.

  • Большие for_each = медленный plan. Если у вас for_each по 500 элементам, plan будет дёргать API 500 раз. Думайте о масштабе.

§ команды

bash
terraform state list

Покажет все ресурсы с индексами/ключами: aws_s3_bucket.regional["us"], aws_s3_bucket.regional["eu"]...

bash
terraform state show 'aws_s3_bucket.regional["us"]'

Один конкретный из for_each. Обратите внимание на одинарные кавычки вокруг: без них shell съест двойные.

bash
terraform plan -target='aws_s3_bucket.regional["us"]'

Точечный plan только для одного элемента for_each.

§ см. также

  • tf-resource-blockБлок resource: главный кирпич Terraformresource, это блок, который говорит Terraform «создай мне такую штуку в облаке». У него три части: тип ресурса (что это), имя (как зовём внутри), и аргументы (как настроить). 90% времени в Terraform, это написание таких блоков.
  • tf-referencesСсылки в HCL: как читать aws_s3_bucket.demo.bucketЛюбое значение в HCL можно получить через адрес: var.x, local.x, aws_s3_bucket.demo.arn, module.net.vpc_id, data.aws_region.current.name. Понимание этого синтаксиса, половина продуктивности в Terraform.
  • hcl-typesТипы данных в HCL: string, number, list, map, objectHCL поддерживает примитивы (string, number, bool) и сложные типы: list, set, map, tuple, object. Эта статья, про синтаксис каждого и про разницу между похожими (list vs tuple, map vs object): meta_description: "Типы данных HCL: string, number, bool, list, set, map, tuple, object. Что выбрать для variable, где разница между list и tuple, синтаксис каждого с примерами."
  • tf-localslocals: вычисляемые внутренние именаlocals: блок с именами, видимыми только внутри HCL (не вход, не выход). Удобны для DRY: один раз сосчитал общий префикс или теги: везде используешь через local.x. Не путать с variable (вход) и output (выход).
Footer
linuxlab-
Copyright © 2026 LinuxLab. Все права защищены.
Учебники
Цены
О платформе
Конфиденциальность и куки