Terraform
lifecycle reference
Terraform performs the following operations when you apply a configuration:
- Creates resources defined in the configuration that are not associated with a real infrastructure object in the state.
- Destroys resources that exist in the state but not in the configuration.
- Updates resources in-place whose arguments have changed.
- Destroys and re-create resources whose arguments have changed but that Terraform cannot update in-place because of remote API limitations.
The lifecycle block accepts a rule that customizes how Terraform performs the lifecycle stages for each resource.
Usage
All lifecycle settings affect how Terraform constructs and traverses
the dependency graph. As a result, only literal values can be used because
the processing happens too early for arbitrary expression evaluation.
Support for each lifecycle rule varies across Terraform configuration blocks. Refer to the reference documentation for the Terraform block you are adding to your configuration for details.
Depending on the block you are configuring, you may be able to use one or more of the following rules.
create_before_destroy
By default, when Terraform must change a resource argument that cannot be updated in-place due to remote API limitations, Terraform destroys the existing object and then create a new replacement object with the new configured arguments. Use the create_before_destroy rule to instruct Terraform to create a replacement resource before destroying the current resource.
This is an opt-in behavior because many remote object types have unique name requirements or other constraints that must be accommodated for both a new and an old object to exist concurrently. Some resource types offer special options to append a random suffix onto each object name to avoid collisions, for example. Terraform CLI cannot automatically activate such features, so you must understand the constraints for each resource type before using create_before_destroy with it.
create_before_destroy and resource dependencies
Terraform propagates and applies create_before_destroy behavior to all resource dependencies. For example:
create_before_destroyis enabled on resourceAbut not on resourceB.- Because resource
Ais dependent on resourceB, Terraform enablescreate_before_destroyfor resourceBimplicitly by default and stores it to the state file.
As a result, you cannot override create_before_destroy
to false on resource B because that would imply dependency cycles in the graph.
When the resource contains a provisioner that runs during the destroy operation, setting create_before_destroy to true also prevents the provisioner from running.
You can use create_before_destroy in resource blocks.
prevent_destroy
When prevent_destroy is set to true, Terraform rejects plans that would destroy the infrastructure object associated with the resource and returns an error. The argument must be present in the configuration. This rule doesn't prevent Terraform from destroying a resource if you remove its configuration. Refer to Remove a resource from state for instructions on how to remove a resource from state without destroying the actual resource.
Use this rule as protection against accidentally replacing objects that may be costly to reproduce, such as database instances. Enabling prevent_destory, however, makes certain configuration changes impossible to apply
and prevents the terraform destroy command from operating once such objects are created. Use prevent_destroy sparingly.
You can use prevent_destroy in in resource blocks.
ignore_changes
By default, Terraform detects any difference in the current settings of a real infrastructure object and plans to update the remote object to match configuration. Use the ignore_changes argument when a resource is created with references to data that may change in the future, but should not affect the resource after its creation.
In some rare cases, a remote object's settings are modified by processes outside of Terraform, which Terraform attempts to resolve on the next run. To let Terraform share management responsibilities of a single object with a separate process, the ignore_changes meta-argument specifies resource attributes that Terraform should ignore when planning updates to the associated remote object.
Terraform considers the arguments corresponding to the given attribute names when planning a create operation, but are ignored when planning an
update operation. The arguments are the relative address of the attributes in the resource. You can reference map and list elements using index notation,
such as tags["Name"] and list[0].
In the following example, Terraform ignores changes to tags so that a management agent can update them based on a rule set managed elsewhere:
resource "aws_instance" "example" {
# ...
lifecycle {
ignore_changes = [
tags
]
}
}
Instead of a list of items, you can use the all keyword to instruct
Terraform to ignore all attributes. As a result, Terraform can
create and destroy the remote object but will never propose updates to it.
Terraform only ignores attributes defined by the resource type. You can't apply
ignore_changes to itself or to any other meta-arguments.
You can use ignore_changes in resource blocks.
replace_triggered_by
Terraform replaces the resource when any of the referenced resources or specified attributes change. Supply a list of expressions that reference managed resources, instances, or instance attributes.
When used in a resource that uses count or for_each, you can use count.index or each.key in the expression to reference specific instances of other resources that are configured with the same count or collection.
References trigger replacement in the following conditions:
- If the reference is to a resource with multiple instances, a plan to update or replace any instance triggers a replacement.
- If the reference is to a single resource instance, a plan to update or replace that instance triggers a replacement.
- If the reference is to a single attribute of a resource instance, any change to the attribute value triggers a replacement.
You can only reference managed resources in replace_triggered_by
expressions. This lets you modify these expressions without forcing
replacement. In the following example, Terraform replaces aws_appautoscaling_target each time this instance of aws_ecs_service is replaced:
resource "aws_appautoscaling_target" "ecs_target" {
# ...
lifecycle {
replace_triggered_by = [
aws_ecs_service.svc.id
]
}
}
replace_triggered_by allows only resource addresses because the decision is based on the planned actions for all of the given resources. Plain values, such as local values or input variables, do not have planned actions of their own, but you can treat them with a resource-like lifecycle by using them with the terraform_data resource.
You can use replace_triggered_by in resource blocks.
precondition
Specifies a condition that Terraform evaluates before creating the resource. The following arguments in the precondition block are required:
| Argument | Description | Data type |
|---|---|---|
condition | Expression that must return true for Terraform to proceed with an operation. You can refer to any other object in the same configuration scope unless the reference creates a cyclic dependency. | Expression that can include references, strings, and operators. |
error_message | Message that Terraform prints to the console if the condition returns false. | String |
Terraform evaluates precondition blocks before evaluating the resource's configuration arguments. The precondition can take precedence over argument evaluation errors.
Terraform evaluates precondition blocks after evaluating count and for_each meta-arguments. As a result, Terraform can evaluate the precondition separately for each instance and makes the each.key and count.index objects available in the conditions.
You can include a precondition and postcondition block in the same resource. Do not add precondition blocks to a resource block and a data block that represent the same object in the same configuration. Doing so may cause Terraform to ignore changes to the data block that result from changes in the resource block.
Refer to Validate your configuration for information about adding validations to your Terraform configuration.
You can use precondition in the following Terraform configuration blocks:
postcondition
Specifies a condition that Terraform evaluates after creating the resource. The following arguments in the precondition block are required:
| Argument | Description | Data type |
|---|---|---|
condition | Expression that must return true for Terraform to perform operations on downstream resources. You can refer to any other object in the same configuration scope unless the reference creates a cyclic dependency. | Expression that can include references, strings, and operators. |
error_message | Message that Terraform prints to the console if the condition returns false. | String |
Terraform evaluates postcondition blocks after planning and applying changes to the data source. Postcondition failures prevent changes to other resources that depend on the failing resource.
You can include a postcondition and precondition block in the same resource. Do not add postcondition blocks to a resource block and a data block that represent the same object in the same configuration. Doing so may cause Terraform to ignore changes to the data block that result from changes in the resource block.
Refer to Validate your configuration for information about adding validations to your Terraform configuration.
You can use precondition in the following Terraform configuration blocks:
destroy
Set to false to remove a resource from state without destroying the actual infrastructure resource. You can only use this rule in removed block.
Supported constructs
Support for each lifecycle rule varies across Terraform configuration blocks. Refer to the reference documentation for the Terraform block you are adding to your configuration for details.
Example use cases
The following use cases describe common patterns for the lifecycle argument.
Ignore attribute changes
In the following example, Terraform ignores changes to the resource's tags:
resource "aws_instance" "example" {
# ...
lifecycle {
ignore_changes = [tags]
}
}
Specify triggers that replace resources
In the following example, Terraform replaces aws_appautoscaling_target each time this instance of aws_ecs_service is replaced:
resource "aws_appautoscaling_target" "ecs_target" {
# ...
lifecycle {
replace_triggered_by = [
aws_ecs_service.svc.id
]
}
}
Validate the AMI for creating instances
In the following example, the precondition block ensures that the AMI ID retrieved from the data block includes x86_64 as its architecture attribute. The postcondition block specifies that the EC2 instance must be allocated a public DNS hostname. When either condition is not met, Terraform returns the error_message for the failed condition:
data "aws_ami" "example" {
most_recent= true
owners = ["amazon"]
filter {
name = "image-id"
values = ["ami-*"]
}
}
resource "aws_instance" "example" {
instance_type = "t3.micro"
ami = data.aws_ami.example.id
lifecycle {
precondition {
condition = data.aws_ami.example.architecture == "x86_64"
error_message = "The selected AMI must be for the x86_64 architecture."
}
postcondition {
condition = self.public_dns != ""
error_message = "EC2 instance must be in a VPC that has public DNS hostnames enabled."
}
}
}
Validate that a root storage volume is encrypted
In the following example, the data block retrieves the root storage volume connected to the aws_instance.example EC2 instance using the volume_id attribute. When a data resource verifies the result of a managed resource declared in the same configuration, you must define the check in a postcondition block in the resource so that Terraform waits for changes to the managed resource to complete before reading the data resource.
data "aws_ebs_volume" "example" {
filter {
name = "volume-id"
values = [aws_instance.example.root_block_device[0].volume_id]
}
lifecycle {
# The EC2 instance will have an encrypted root volume.
postcondition {
condition = self.encrypted
error_message = "The server's root volume is not encrypted."
}
}
}
data "aws_ami" "example" {
most_recent= true
owners = ["amazon"]
filter {
name = "image-id"
values = ["ami-*"]
}
}
resource "aws_instance" "example" {
instance_type = "t3.micro"
ami = data.aws_ami.example.id
}
output "api_base_url" {
value = "https://${aws_instance.example.private_dns}:8433/"
}
The validation fails in this example, so Terraform prints the following message to the console:
│ Error: Resource postcondition failed
│
│ on main.tf line 31, in data "aws_ebs_volume" "example":
│ 31: condition = self.encrypted
│ ├────────────────
│ │ self.encrypted is false
│
│ The server's root volume is not encrypted.
Validate ephemeral resources
In the following example, the aws_ssm_parameter ephemeral resource has a precondition to ensure that compliance mode is enabled to secure production secrets, and a postcondition to ensure the generated password meets password requirements:
variable "environment" {
description = "Deployment environment"
type = string
}
variable "compliance_mode" {
description = "Enable compliance requirements for production"
type = bool
default = false
}
ephemeral "aws_ssm_parameter" "database_password" {
name = "/secrets/${var.environment}/database/password"
lifecycle {
precondition {
condition = var.environment != "prod" || var.compliance_mode == true
error_message = "Enable compliance mode to assess production secrets."
}
postcondition {
condition = can(regex("^[A-Za-z0-9!@#$%^&*()_+=-]{16,}$", self.value))
error_message = "Password from external source must meet security requirements."
}
}
}