> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ubiquex.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Lifecycle Block

> Control how ubx handles resource creation, updates, and destruction.

By default, Pulumi manages the full lifecycle of every resource: create on first apply, update in place when attributes change, destroy on removal. This is correct most of the time. But production infrastructure has exceptions. A production database should never be destroyed by accident — even if someone runs `ubx destroy` or removes the block. An autoscaling group's `desired_capacity` is managed at runtime by AWS Auto Scaling, and Pulumi shouldn't try to reset it on every apply. An EC2 instance that requires zero downtime should have its replacement provisioned before the old one is torn down. The `lifecycle` block is where you express these exceptions — three settings that override the default resource management behavior in precisely targeted ways.

## What you'll learn

* `prevent_destroy` — block accidental destruction of critical resources
* `ignore_changes` — suppress drift on attributes managed outside ubx
* `create_before_destroy` — provision a replacement before tearing down the original

***

## Why this matters

<Info>
  `lifecycle` settings are evaluated by Pulumi at apply time, not at compile time. They're metadata instructions to the Pulumi engine, not ubx expressions. This means they don't interact with the `Pending<T>` system and can't reference resource outputs — they take literal values only.
</Info>

***

## The source code

<CodeGroup>
  ```hcl main.iac theme={"theme":"css-variables"}
  # prevent_destroy — blocks ubx destroy and any plan that would delete this resource.
  unit "aws_rds_instance" "primary" {
    identifier          = "myapp-${input.env}-primary"
    allocated_storage   = 20
    engine              = "postgres"
    engine_version      = "15"
    instance_class      = "db.t3.micro"
    username            = "admin"
    password            = "placeholder"
    skip_final_snapshot = false

    lifecycle {
      prevent_destroy = false  # set to true in prod to block accidental deletion
    }
  }

  # ignore_changes — suppress drift on attributes managed outside ubx.
  # desired_capacity is managed by Auto Scaling at runtime; don't reset it.
  unit "aws_autoscaling_group" "workers" {
    name             = "workers-${input.env}"
    min_size         = 1
    max_size         = 10
    desired_capacity = 2

    lifecycle {
      ignore_changes = ["desired_capacity", "tags"]
    }
  }

  # create_before_destroy — provision replacement before tearing down original.
  # Ensures zero downtime for resources that need replacement on update.
  unit "aws_instance" "api" {
    ami           = "ami-0c02fb55956c7d316"
    instance_type = "t3.small"

    tags = {
      Name = "api-${input.env}"
    }

    lifecycle {
      create_before_destroy = true
    }
  }
  ```

  ```hcl inputs.iac theme={"theme":"css-variables"}
  input "env" {
    type    = "string"
    default = "dev"
  }
  ```

  ```hcl outputs.iac theme={"theme":"css-variables"}
  output "db_identifier" {
    value = unit.aws_rds_instance.primary.id
  }
  ```

  ```hcl ubx.iac theme={"theme":"css-variables"}
  ubx {
    name    = "lifecycle-block"
    runtime = "typescript"
    stacks  = ["dev", "staging", "prod"]
  }

  backend "local" {
    path = ".ubx/state"
  }
  ```
</CodeGroup>

***

## How it works

<Steps>
  <Step title="prevent_destroy is a Pulumi engine flag">
    When `prevent_destroy = true`, Pulumi marks the resource with a destruction guard. Any plan that would destroy it — `ubx destroy`, a block removal, or an attribute change that forces replacement — errors before executing. The only way to remove the resource is to set `prevent_destroy = false` first, then apply, then remove the block.
  </Step>

  <Step title="ignore_changes suppresses drift detection">
    Attributes listed in `ignore_changes` are excluded from Pulumi's diff calculation. Even if the live value differs from what's in `.iac`, Pulumi won't generate an update for those attributes. This is essential for attributes managed by external systems — autoscalers, Kubernetes controllers, manual operator changes.
  </Step>

  <Step title="create_before_destroy changes the replacement order">
    Normally Pulumi destroys then creates (to avoid naming conflicts). `create_before_destroy = true` reverses this: the replacement is provisioned first, then the old resource is destroyed. Required for resources where the old instance must remain available until the new one is ready.
  </Step>
</Steps>

***

## What ubx generates

<CodeGroup>
  ```typescript index.ts theme={"theme":"css-variables"}
  // Generated by ubx — do not edit
  import * as pulumi from "@pulumi/pulumi";
  import * as aws from "@pulumi/aws";

  const config = new pulumi.Config();
  const env = config.get("env") || "dev";

  const primary = new aws.rds.Instance("primary", {
      identifier: `myapp-${env}-primary`,
      allocatedStorage: 20,
      engine: "postgres",
      engineVersion: "15",
      instanceClass: "db.t3.micro",
      username: "admin",
      password: "placeholder",
      skipFinalSnapshot: false,
  }, { retainOnDelete: false }); // prevent_destroy maps to retainOnDelete

  const workers = new aws.autoscaling.Group("workers", {
      name: `workers-${env}`,
      minSize: 1,
      maxSize: 10,
      desiredCapacity: 2,
  }, { ignoreChanges: ["desiredCapacity", "tags"] });

  const api = new aws.ec2.Instance("api", {
      ami: "ami-0c02fb55956c7d316",
      instanceType: "t3.small",
      tags: { Name: `api-${env}` },
  }, { deleteBeforeReplace: false }); // create_before_destroy = true

  export const dbIdentifier = primary.id;
  ```

  ```python __main__.py theme={"theme":"css-variables"}
  # Generated by ubx — do not edit
  import pulumi
  import pulumi_aws as aws

  config = pulumi.Config()
  env = config.get("env") or "dev"

  primary = aws.rds.Instance("primary",
      identifier=f"myapp-{env}-primary",
      allocated_storage=20,
      engine="postgres",
      engine_version="15",
      instance_class="db.t3.micro",
      username="admin",
      password="placeholder",
      skip_final_snapshot=False,
      opts=pulumi.ResourceOptions(retain_on_delete=False),
  )

  workers = aws.autoscaling.Group("workers",
      name=f"workers-{env}",
      min_size=1,
      max_size=10,
      desired_capacity=2,
      opts=pulumi.ResourceOptions(ignore_changes=["desired_capacity", "tags"]),
  )

  api = aws.ec2.Instance("api",
      ami="ami-0c02fb55956c7d316",
      instance_type="t3.small",
      tags={"Name": f"api-{env}"},
      opts=pulumi.ResourceOptions(delete_before_replace=False),
  )

  pulumi.export("db_identifier", primary.id)
  ```

  ```go main.go theme={"theme":"css-variables"}
  // Generated by ubx — do not edit
  package main

  import (
      "github.com/pulumi/pulumi-aws/sdk/v6/go/aws/autoscaling"
      "github.com/pulumi/pulumi-aws/sdk/v6/go/aws/ec2"
      "github.com/pulumi/pulumi-aws/sdk/v6/go/aws/rds"
      "github.com/pulumi/pulumi/sdk/v3/go/pulumi"
      "github.com/pulumi/pulumi/sdk/v3/go/pulumi/config"
  )

  func main() {
      pulumi.Run(func(ctx *pulumi.Context) error {
          cfg := config.New(ctx, "")
          env := cfg.Get("env")
          if env == "" { env = "dev" }

          primary, err := rds.NewInstance(ctx, "primary", &rds.InstanceArgs{
              Identifier: pulumi.Sprintf("myapp-%s-primary", env),
              AllocatedStorage: pulumi.Int(20),
              Engine: pulumi.String("postgres"),
              EngineVersion: pulumi.String("15"),
              InstanceClass: pulumi.String("db.t3.micro"),
              Username: pulumi.String("admin"),
              Password: pulumi.String("placeholder"),
              SkipFinalSnapshot: pulumi.Bool(false),
          }, pulumi.RetainOnDelete(false))
          if err != nil { return err }

          _, err = autoscaling.NewGroup(ctx, "workers", &autoscaling.GroupArgs{
              Name: pulumi.Sprintf("workers-%s", env),
              MinSize: pulumi.Int(1),
              MaxSize: pulumi.Int(10),
              DesiredCapacity: pulumi.Int(2),
          }, pulumi.IgnoreChanges([]string{"desiredCapacity", "tags"}))
          if err != nil { return err }

          _, err = ec2.NewInstance(ctx, "api", &ec2.InstanceArgs{
              Ami: pulumi.String("ami-0c02fb55956c7d316"),
              InstanceType: pulumi.String("t3.small"),
              Tags: pulumi.StringMap{"Name": pulumi.Sprintf("api-%s", env)},
          }, pulumi.DeleteBeforeReplace(false))
          if err != nil { return err }

          ctx.Export("dbIdentifier", primary.ID())
          return nil
      })
  }
  ```
</CodeGroup>

***

## Common mistakes

<Warning>
  `prevent_destroy = true` in a ubx block does NOT prevent you from removing the block — it only blocks plans that would execute a destroy. To actually protect a resource, you must keep the block present with `prevent_destroy = true`. Removing the block and running `ubx apply` will still destroy the resource if `prevent_destroy` is not set.
</Warning>

<Tip>
  Use `ignore_changes` sparingly — it hides drift between your `.iac` source and reality. If you find yourself adding many attributes to `ignore_changes`, it may be a sign that the resource is better managed outside ubx entirely, with a `data` block to read its outputs.
</Tip>

***

## Run it

```bash theme={"theme":"css-variables"}
ubx validate
ubx plan             # see lifecycle metadata in plan output
ubx apply
```

***

## What you learned

<Check>`prevent_destroy = true` blocks any plan that would destroy the resource</Check>
<Check>`ignore_changes` suppresses drift on attributes managed by external systems</Check>
<Check>`create_before_destroy = true` provisions the replacement before destroying the original</Check>

***

## Next steps

<CardGroup cols={2}>
  <Card title="Timeouts and ignore_changes" href="/v1/tutorials/11-timeouts-and-ignore-changes">
    Override provider-default wait times
  </Card>

  <Card title="lifecycle reference" href="/v1/language/lifecycle">
    Full lifecycle block syntax
  </Card>
</CardGroup>

***

<Info>
  Full runnable example: [github.com/ubiquex/ubx-examples/10-lifecycle-block](https://github.com/ubiquex/ubx-examples/tree/main/10-lifecycle-block)
</Info>
