Мы пока не трогаем автотесты (Terratest, проверки в CI - это отдельная большая тема). Сегодня - дисциплина ручной проверки: подними, проверь, снеси. Звучит просто, но именно на "снеси" спотыкается большинство.
Зачем вообще тестировать инфра-код руками
Terraform - это инфраструктура как код, и слово "код" тут не для красоты. Код имеет свойство содержать ошибки. Опечатка в имени подсети, неправильный CIDR, забытый атрибут, провайдер, который молча создал ресурс не там - все это всплывает только когда ты реально вызываешь terraform plan и apply против живого облака.
Ручной тест отвечает на простые вопросы:
- Применяется ли конфигурация вообще, без ошибок валидации и API?
- Получается ли в итоге то, что ты задумал - сеть со связностью, виртуалка с доступом, бакет с правами?
- Корректно ли все это удаляется обратно? (да, destroy тоже надо тестировать)
- Идемпотентен ли код - если прогнать apply дважды, второй раз показывает "No changes"?

Почему песочница, а не прод
Главное правило: тестовое окружение должно быть физически изолировано от боевого. Не "та же инфраструктура, только осторожненько", а отдельная песочница, где не страшно сломать вообще все.
В Yandex Cloud для этого есть удобная иерархия: организация - облако (cloud) - каталог (folder). Каталог - это и есть твоя естественная граница изоляции. Заводишь отдельный folder под тесты, например с именем sandbox или dev-playground, и работаешь только в нем. Если что-то пойдет не так, ты сносишь ресурсы только внутри этого каталога и прод даже не заметит.
Почему именно отдельный каталог, а не отдельный state в том же:
- Права (IAM) выдаются на каталог. Можно дать тестовому сервисному аккаунту доступ только к песочнице - и он физически не дотянется до прода.
- Биллинг и квоты видно отдельно. Легко понять, сколько жрет именно тестирование.
- Случайный terraform destroy в песочнице не превратится в катастрофу.
Код: Выделить всё
terraform {
required_providers {
yandex = {
source = "yandex-cloud/yandex"
}
}
}
provider "yandex" {
zone = "ru-central1-a"
folder_id = var.sandbox_folder_id
}
variable "sandbox_folder_id" {
type = string
description = "ID тестового каталога - НИКОГДА не прод"
}
Цикл ручного теста: apply, проверка, destroy
Полный круг выглядит так. Создаем минимальную, но осмысленную инфраструктуру - сеть, подсеть и виртуалку с тегами, по которым потом легко найти все тестовое:
Код: Выделить всё
resource "yandex_vpc_network" "test" {
name = "tf-test-net"
labels = {
env = "sandbox"
owner = "ivan"
cleanup = "auto"
}
}
resource "yandex_vpc_subnet" "test" {
name = "tf-test-subnet"
zone = "ru-central1-a"
network_id = yandex_vpc_network.test.id
v4_cidr_blocks = ["10.10.0.0/24"]
}
resource "yandex_compute_instance" "test" {
name = "tf-test-vm"
platform_id = "standard-v3"
zone = "ru-central1-a"
resources {
cores = 2
memory = 2
}
boot_disk {
initialize_params {
image_id = "fd8..." # подставь актуальный image_id из YC
size = 10
}
}
network_interface {
subnet_id = yandex_vpc_subnet.test.id
nat = true
}
labels = {
env = "sandbox"
cleanup = "auto"
}
}
Код: Выделить всё
# 1. Что вообще будет создано - читаем глазами
terraform plan
# 2. Создаем
terraform apply
# 3. Проверяем работоспособность руками:
# - пингуем внешний IP виртуалки
# - ssh-имся внутрь
# - дергаем нужный сервис
ping <внешний_IP>
ssh user@<внешний_IP>
# 4. Прогон на идемпотентность - должно быть No changes
terraform apply
# 5. Сносим ВСЕ, что подняли
terraform destroy
Проблема забытых ресурсов и денег
Главный враг тут - человеческая память. Ты создал три тестовых стенда за день, два снес, про третий забыл. Виртуалка с публичным IP и диском тихо капает деньги, пока ты про нее не вспомнишь в момент прихода счета.
Что с этим делать системно:
1. Лейблы и теги на всем. Заметил, я везде вешал labels с env=sandbox и cleanup=auto. Это не для красоты - это твой будущий фильтр. В любой момент можно через yc-cli найти все, что помечено как тестовое:
Код: Выделить всё
yc compute instance list --folder-id <sandbox_folder_id> \
--format json | jq '.[] | select(.labels.cleanup == "auto") | .name'
Код: Выделить всё
#!/bin/bash
# Снести все виртуалки с меткой cleanup=auto в песочнице
FOLDER="<sandbox_folder_id>"
for id in $(yc compute instance list --folder-id "$FOLDER" \
--format json | jq -r '.[] | select(.labels.cleanup=="auto") | .id'); do
echo "Удаляю инстанс $id"
yc compute instance delete "$id"
done
3. Отдельный каталог как естественный нюкер. Если вся песочница - это один folder и в нем нет ничего ценного, можно просто периодически выгребать его целиком. Это самый надежный способ не платить за забытое.
4. Бюджеты и алерты в биллинге. Поставь в YC оповещение на сумму по тестовому каталогу. Условные 500 рублей в месяц на песочницу - и если порог пробит, тебе прилетит письмо раньше, чем счет.
Кстати, все то же применимо и к OpenTofu - это форк Terraform, команды plan, apply, destroy и работа со state идентичны, так что вся дисциплина переносится один в один.
Типичные грабли
- Тестируешь на проде "по-быстрому". Классика. Один неудачный destroy с не тем -target, и ты объясняешь команде, куда делась боевая база.
- Забыл, что terraform destroy сносит только то, что в текущем state. Ресурсы, созданные вне Terraform или из другого workspace, останутся жить и капать деньгами.
- Не проверил идемпотентность. Apply прошел, но повторный показывает changes - значит, дрейф уже заложен в код.
- Публичный IP и внешний диск остались после destroy. Бывает, когда они вынесены в отдельный ресурс с lifecycle prevent_destroy или просто не попали в зависимость.
- Лейблы написал, а фильтровать по ним забыл. Метки без процесса поиска - просто украшение.
Сделай руками, без этого урок не усвоится:
- Заведи в Yandex Cloud отдельный каталог под песочницу. Настрой провайдера на его folder_id.
- Опиши сеть, подсеть и одну виртуалку с лейблами env=sandbox и cleanup=auto.
- Прогони terraform plan, прочитай вывод. Затем apply.
- Проверь, что виртуалка живая - зайди по ssh.
- Прогони apply еще раз. Убедись, что видишь "No changes" - это идемпотентность.
- Напиши однострочник на yc-cli, который выводит все ресурсы с меткой cleanup=auto.
- Сделай terraform destroy. Проверь в консоли облака, что не осталось ни диска, ни IP.
- Почему отдельный каталог (folder) в Yandex Cloud лучше для тестов, чем отдельный state в том же каталоге, что и прод?
- Что проверяет повторный прогон terraform apply сразу после первого и почему "No changes" - это хорошо?
- Какие тестовые ресурсы terraform destroy НЕ удалит и откуда они берутся?
- Зачем вешать лейблы env и cleanup на ресурсы, если у тебя и так есть terraform destroy?
Ручной тест Terraform - это не "запустил apply и ушел". Это полный цикл: plan, apply, проверка работоспособности, проверка идемпотентности, destroy. Прод не трогаем никогда - под тесты заводим изолированную песочницу, в Yandex Cloud это отдельный каталог. Лейблы env и cleanup плюс биллинг-алерты - твоя страховка от забытых ресурсов и неожиданных счетов. И главное правило, которое стоит вытатуировать: что подняли в тесте - то и снесли. Дисциплина destroy экономит больше нервов и денег, чем любой умный скрипт.