# lifecycle: управляем поведением resource _Ресурсы и data-источники · TerraformLab Knowledge Base_ **TL;DR:** Блок lifecycle настраивает четыре поведения: create_before_destroy (zero-downtime), prevent_destroy (защита от удаления), ignore_changes (игнор drift по атрибутам), replace_triggered_by (пересоздать по сигналу). ## Зачем нужен lifecycle По умолчанию Terraform: «если в HCL стало по-другому, изменить ресурс. Если убрал из HCL, удалить ресурс». Это работает в 90% случаев, но иногда поведение надо подкрутить. `lifecycle`, вложенный блок внутри `resource`, четыре опции для тонкой настройки. ## `prevent_destroy = true`, защита от случайностей Самая ценная опция для продакшена: ```hcl resource "aws_db_instance" "main" { identifier = "prod-db" # ... Параметры ... lifecycle { prevent_destroy = true } } ``` Теперь любая попытка удалить этот ресурс через `apply` или `destroy` упадёт с ошибкой: ``` Error: Instance cannot be destroyed Resource aws_db_instance.main has lifecycle.prevent_destroy set, but the plan calls for this resource to be destroyed. ``` Чтобы реально снести, придётся убрать `prevent_destroy = true`, сделать apply, и только потом снова попробовать destroy. **Двухшаговая защита**. Нужно осознанно сначала отключить флаг. Ставьте на: prod-БД, S3-бакеты с данными, KMS-ключи, любые ресурсы, потеря которых катастрофа. ## `create_before_destroy = true`, zero-downtime По умолчанию при пересоздании ресурса (`-/+`) Terraform делает так: 1. Удалить старый. 2. Создать новый. Между шагами 1 и 2, даунтайм. Для критичных ресурсов это плохо. ```hcl resource "aws_launch_template" "web" { name_prefix = "web-" instance_type = "t3.micro" lifecycle { create_before_destroy = true } } ``` Теперь Terraform: 1. Создаст новый. 2. Переключит зависимости на новый. 3. Удалит старый. Нюансы: - Должно быть **уникальное имя**. Нельзя `name = "web"`, будет конфликт (старый ещё не удалён, новый с тем же именем не создаётся). Используйте `name_prefix` или включайте суффикс в имя. - У некоторых ресурсов есть ограничения. AWS RDS, нельзя два экземпляра с одинаковым identifier. ## `ignore_changes = [...]`, игнор drift Иногда атрибут меняется снаружи Terraform, и нам всё равно. Тег `last_modified` от внешнего скрипта. Auto-scaling меняет `desired_capacity`. Без `ignore_changes` каждый plan показывал бы изменение и предлагал «откатить». ```hcl resource "aws_autoscaling_group" "web" { name = "web-asg" min_size = 2 max_size = 10 desired_capacity = 2 lifecycle { # ASG-controller меняет desired_capacity автоматически, не трогаем. ignore_changes = [desired_capacity] } } ``` Можно игнорировать всё: ```hcl lifecycle { ignore_changes = all } ``` Но это редко правильно, превращает ресурс в «создано один раз, дальше не управляем». ## `replace_triggered_by = [...]`, пересоздать по сигналу Иногда надо пересоздать ресурс когда меняется **другой** ресурс. Например, EC2-инстанс должен пересоздаваться при изменении user_data, даже если AWS-провайдер не считает это причиной для replace. ```hcl resource "null_resource" "config_version" { triggers = { config_hash = filemd5("config.yaml") } } resource "aws_instance" "web" { ami = "ami-..." instance_type = "t3.micro" lifecycle { replace_triggered_by = [null_resource.config_version] } } ``` Если файл `config.yaml` поменялся, `null_resource.config_version` пересоздастся (новый triggers): и `aws_instance.web` тоже пересоздастся. ## Подводные камни - **`prevent_destroy` не защищает от `terraform state rm`.** Если кто-то руками удалит ресурс из state, Terraform его больше не «видит», и `prevent_destroy` не сработает. Защищает только от изменений через apply/destroy. - **`create_before_destroy` требует уникальности.** Имя, identifier, любой атрибут с unique-constraint, должен быть динамическим. Иначе будет конфликт. - **`ignore_changes` не работает на add/remove.** Если вы добавите тег в HCL который раньше не существовал, `ignore_changes = [tags]` не помешает Terraform его создать. Игнор работает только когда облако и HCL расходятся, а не когда вы явно меняете HCL. - **lifecycle нельзя в data-блоке.** Это конструкция только для resource. - **lifecycle блок один на ресурс.** Несколько lifecycle-блоков в одном resource, ошибка. - **Не использовать `ignore_changes` чтобы скрыть проблему.** Если plan показывает странный drift, сначала разберитесь, **почему** атрибут меняется снаружи. Лечите причину, не симптом. ## Команды ```bash terraform plan ``` Покажет если lifecycle блокирует destroy: с понятным сообщением. ```bash terraform state list | xargs -I {} terraform state show {} | grep prevent_destroy ``` Найти все ресурсы с защитой prevent_destroy: полезно перед миграциями. ## См. также - [Блок resource: главный кирпич Terraform](/terraform/kb/tf-resource-block.md) - [terraform destroy: снести всё, что было создано](/terraform/kb/tf-destroy.md) - [terraform apply: применить план в реальном облаке](/terraform/kb/tf-apply.md)