• HashiCorp Developer

  • HashiCorp Cloud Platform
  • Terraform
  • Packer
  • Consul
  • Vault
  • Boundary
  • Nomad
  • Waypoint
  • Vagrant
Terraform
  • Install
  • Tutorials
    • About the Docs
    • Configuration Language
    • Terraform CLI
    • Terraform Cloud
    • Terraform Enterprise
    • CDK for Terraform
    • Provider Use
    • Plugin Development
    • Registry Publishing
    • Integration Program
  • Registry(opens in new tab)
  • Try Cloud(opens in new tab)
  • Sign up
State

Skip to main content
11 tutorials
  • Import Terraform Configuration
  • Migrate State to Terraform Cloud
  • Manage Resources in Terraform State
  • Target Resources
  • Troubleshoot Terraform
  • Manage Resource Drift
  • Manage Resource Lifecycle
  • Version Remote State with the Terraform Cloud API
  • Use Refresh-Only Mode to Sync Terraform State
  • Develop Configuration with the Console
  • Use Configuration to Move Resources

  • Resources

  • Tutorial Library
  • Certifications
  • Community Forum
    (opens in new tab)
  • Support
    (opens in new tab)
  • GitHub
    (opens in new tab)
  • Terraform Registry
    (opens in new tab)
  1. Developer
  2. Terraform
  3. Tutorials
  4. State
  5. Manage Resource Lifecycle

Manage Resource Lifecycle

  • 8min

  • TerraformTerraform

Lifecycle arguments help control the flow of your Terraform operations by creating custom rules for resource creation and destruction. Instead of Terraform managing operations in the built-in dependency graph, lifecycle arguments help minimize potential downtime based on your resource needs as well as protect specific resources from changing or impacting infrastructure.

Prerequisites

This tutorial assumes you are familiar with the standard Terraform workflow. If you are unfamiliar with Terraform, complete the Get Started tutorials first.

For this tutorial, you will need:

  • The Terraform CLI, version 0.14 or later.
  • AWS Credentials configured for use with Terraform.
  • The awscli configured.

Create infrastructure

Start by cloning the example repository. This configuration builds an EC2 instance and a security group rule to allow port 8080 access to the instance.

$ git clone https://github.com/hashicorp/learn-terraform-lifecycle-management

Change into the repository directory.

$ cd learn-terraform-lifecycle-management

Confirm your AWS CLI region.

$ aws configure get region
us-east-2

Open the terraform.tfvars file and edit the region to match your AWS CLI configuration.

region = "us-east-2"

Open the main.tf file and review your configuration. Your two main resources are an EC2 instance and a security group that allows TCP access on port 8080.

##...

resource "aws_instance" "example" {
  ami                    = data.aws_ami.ubuntu.id
  instance_type          = "t2.micro"
  vpc_security_group_ids = [aws_security_group.sg_web.id]
  user_data              = <<-EOF
              #!/bin/bash
              apt-get update
              apt-get install -y apache2
              sed -i -e 's/80/8080/' /etc/apache2/ports.conf
              echo "Hello World" > /var/www/html/index.html
              systemctl restart apache2
              EOF
  tags = {
    Name          = "terraform-learn-state-ec2"
    drift_example = "v1"
  }
}

resource "aws_security_group" "sg_web" {
  name = "sg_web"
  ingress {
    from_port   = "8080"
    to_port     = "8080"
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }
  // connectivity to ubuntu mirrors is required to run `apt-get update` and `apt-get install apache2`
  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

Initialize your configuration.

$ terraform init

Initializing the backend...

Initializing provider plugins...
- Reusing previous version of hashicorp/aws from the dependency lock file
- Installing hashicorp/aws v3.26.0...
- Installed hashicorp/aws v3.26.0 (signed by HashiCorp)

Terraform has been successfully initialized!

You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.

If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.

Apply your configuration. Enter yes when prompted to accept your changes.

$ terraform apply


## …

Apply complete! Resources: 2 added, 0 changed, 0 destroyed.

Outputs:

instance_id = "i-099bb19ca402a6761"
public_ip = "3.138.139.170"

When your apply operation completes, run terraform state list to review the resources managed by Terraform in your state file.

$ terraform state list
data.aws_ami.ubuntu
aws_instance.example
aws_security_group.sg_web

Prevent resource deletion

To prevent destroy operations for specific resources, you can add the prevent_destroy attribute to your resource definition. This lifecycle option prevents Terraform from accidentally removing critical resources.

Add prevent_destroy to your EC2 instance.

resource "aws_instance" "example" {
  ami                    = data.aws_ami.ubuntu.id
  instance_type          = "t2.micro"
  vpc_security_group_ids = [aws_security_group.sg_web.id]
  user_data              = <<-EOF
              #!/bin/bash
              apt-get update
              apt-get install -y apache2
              sed -i -e 's/80/8080/' /etc/apache2/ports.conf
              echo "Hello World" > /var/www/html/index.html
              systemctl restart apache2
              EOF
  tags = {
    Name          = "terraform-learn-state-ec2"
    drift_example = "v1"
  }

+ lifecycle {
+   prevent_destroy = true
+ }
}

Run terraform destroy to observe the behavior.

$ terraform destroy
aws_security_group.sg_web: Refreshing state... [id=sg-0c9b526ba6f89d910]
aws_instance.example: Refreshing state... [id=i-099bb19ca402a6761]
â•·
│ Error: Instance cannot be destroyed
│
│   on main.tf line 31:
│   31: resource "aws_instance" "example" {
│
│ Resource aws_instance.example has lifecycle.prevent_destroy set, but the
│ plan calls for this resource to be destroyed. To avoid this error and
│ continue with the plan, either disable lifecycle.prevent_destroy or reduce
│ the scope of the plan using the -target flag.

The prevent_destroy attribute is useful in situations where a change to an attribute would force a replacement and create downtime.

Create resources before they are destroyed

For changes that may cause downtime but must happen, use the create_before_destroy attribute to create your new resource before destroying the old resource.

Update your security group rule to allow port 80 access instead of 8080.

resource "aws_security_group" "sg_web" {
  name = "sg_web"
  ingress {
-  from_port   = "8080"
+  from_port   = "80"
-  to_port   = "8080"
+  to_port   = "80"
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }
  ## ...
}

Update your EC2 instance to reflect this change by adding the create_before_destroy attribute and updating the VM so it runs on port 80.

resource "aws_instance" "example" {
  ami                    = data.aws_ami.ubuntu.id
  instance_type          = "t2.micro"
  vpc_security_group_ids = [aws_security_group.sg_web.id]
  user_data              = <<-EOF
              #!/bin/bash
              apt-get update
              apt-get install -y apache2
-             sed -i -e 's/80/8080/' /etc/apache2/ports.conf
              echo "Hello World" > /var/www/html/index.html
              systemctl restart apache2
              EOF
  tags = {
    Name          = "terraform-learn-state-ec2"
    Drift_example = "v1"
  }

  lifecycle {
-   prevent_destroy = true
+   create_before_destroy = true
  }
}

Run terraform apply and observe the changes that force a replacement. Without the create_before_destroy tag, Terraform would destroy the instance before recreating it, which may lead to downtime. Enter yes when prompted to accept your changes.

$ terraform apply
aws_security_group.sg_web: Refreshing state... [id=sg-0c9b526ba6f89d910]
aws_instance.example: Refreshing state... [id=i-099bb19ca402a6761]

Terraform used the selected providers to generate the following execution
plan. Resource actions are indicated with the following symbols:
  ~ update in-place
+/- create replacement and then destroy

Terraform will perform the following actions:

  # aws_instance.example must be replaced
+/- resource "aws_instance" "example" {
##...

  # aws_security_group.sg_web will be updated in-place
  ~ resource "aws_security_group" "sg_web" {
        id                     = "sg-0c9b526ba6f89d910"
   ##...

Plan: 1 to add, 1 to change, 1 to destroy.

Changes to Outputs:
  ~ instance_id = "i-099bb19ca402a6761" -> (known after apply)
  ~ public_ip   = "3.138.139.170" -> (known after apply)

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes
aws_security_group.sg_web: Modifying... [id=sg-0c9b526ba6f89d910]
aws_security_group.sg_web: Still modifying... [id=sg-0c9b526ba6f89d910, 10s elapsed]
aws_security_group.sg_web: Still modifying... [id=sg-0c9b526ba6f89d910, 20s elapsed]
aws_security_group.sg_web: Modifications complete after 22s [id=sg-0c9b526ba6f89d910]
aws_instance.example: Creating…
aws_instance.example: Creation complete after 1m14s [id=i-0b2fd8a0df19c215d]
aws_instance.example (940b3833): Destroying... [id=i-099bb19ca402a6761]
aws_instance.example: Destruction complete after 41s

Apply complete! Resources: 1 added, 1 changed, 1 destroyed.

Outputs:

instance_id = "i-0b2fd8a0df19c215d"
public_ip = "18.116.49.153"

Ignore changes

For changes outside the Terraform workflow that should not impact Terraform operations, use the ignore_changes argument.

Update the drift_example tag in the AWS CLI.

$ aws ec2 create-tags --resources $(terraform output -raw instance_id) --tags Key=drift_example,Value=v2

Add the ignore_changes attribute to your lifecycle block in the EC2 instance.

resource "aws_instance" "example" {
##...
  lifecycle {
    create_before_destroy = true
+   ignore_changes        = [tags]
  }
}

Run terraform apply. This apply will refresh your state file with v2 instead of overwriting your tag with v1 as written in your configuration.

$ terraform apply
aws_security_group.sg_web: Refreshing state... [id=sg-0c9b526ba6f89d910]
aws_instance.example: Refreshing state... [id=i-0b2fd8a0df19c215d]

Apply complete! Resources: 0 added, 0 changed, 0 destroyed.

Outputs:

instance_id = "i-0b2fd8a0df19c215d"
public_ip = "18.116.49.153"

Examine your instance in the state file to confirm that your drift_example tag is v2.

$ terraform state show aws_instance.example
##...
# aws_instance.example:
resource "aws_instance" "example" {
    tags                         = {
        "Name"          = "terraform-learn-state-ec2"
        "drift_example" = "v2"
    }
##...

Clean up your resources

When you are finished with this tutorial, destroy the resources you created. Enter yes when prompted to confirm your changes.

$ terraform destroy

aws_security_group.sg_web: Refreshing state... [id=sg-0c9b526ba6f89d910]
aws_instance.example: Refreshing state... [id=i-0b2fd8a0df19c215d]

Terraform used the selected providers to generate the following execution
plan. Resource actions are indicated with the following symbols:
  - destroy

##...
Plan: 0 to add, 0 to change, 2 to destroy.

Changes to Outputs:
  - instance_id = "i-0b2fd8a0df19c215d" -> null
  - public_ip   = "18.116.49.153" -> null

Do you really want to destroy all resources?
  Terraform will destroy all your managed infrastructure, as shown above.
  There is no undo. Only 'yes' will be accepted to confirm.

  Enter a value: yes
aws_instance.example: Destruction complete after 1m11s
aws_security_group.sg_web: Destroying... [id=sg-0c9b526ba6f89d910]
aws_security_group.sg_web: Still destroying... [id=sg-0c9b526ba6f89d910, 10s elapsed]
aws_security_group.sg_web: Destruction complete after 11s

Destroy complete! Resources: 2 destroyed.

Next steps

In this tutorial, you learned the different lifecycle management options you can use to prevent resource deletion. You also used lifecycle management to avoid downtime when Terraform recreates your infrastructure and to ignore changes to certain resource attributes.

For more information about Terraform lifecycle management and state drift, review the resources below:

  • Drift Management tutorial
  • Learn Terraform Import tutorial
  • Lifecycle management documentation
 Previous
 Next

On this page

  1. Manage Resource Lifecycle
  2. Prerequisites
  3. Create infrastructure
  4. Prevent resource deletion
  5. Create resources before they are destroyed
  6. Ignore changes
  7. Clean up your resources
  8. Next steps
Give Feedback(opens in new tab)
  • Certifications
  • System Status
  • Terms of Use
  • Security
  • Privacy
  • Trademark Policy
  • Trade Controls
  • Give Feedback(opens in new tab)