Preconditions and Postconditions
Terraform preconditions and postconditions allow you to define custom validation rules. Such rules ensure that certain conditions are met before and after the creation or modification of resources, data sources, and outputs. This provides a way of enforcing custom validation logic and returning informative error messages to your users.
The basic syntax for a condition block is:
<condition> {
condition = <expression>
error_message = <expression>
}
Let's break down this syntax:
<condition>
: Specify precondition or postcondition to define when the condition is need to checked.
condition
: A boolean expression that checks whether or not a variable value meets the desired criteria. This must evaluate to either true or false.
error_message
: A string expression that is displayed as an error message if the condition is not met.
Example 1: Precondition
Here is an example of a precondition that could be used to validate if the instance_type of an EC2 instance is set to a valid value:
resource "aws_instance" "example_ec2_instance" { ami = "ami-0123456789" instance_type = "t2.micro" lifecycle { precondition { condition = contains(["t2.micro", "t2.small", "t2.medium"],aws_instance.example_ec2_instance.instance_type) error_message = "Invalid instance type. Must be one of t2.micro, t2.small, or t2.medium" } } }
Here, the precondition will check whether the instance_type of the example_ec2_instance resource is one of t2.micro, t2.small, or t2.medium. If this is not the case, then the given error message is displayed.
Example 2: Postcondition
Here is a typical example of a postcondition to verify the status of an EC2 instance running upon creation:
resource "aws_instance" "example_ec2_instance" { ami = "ami-0123456789" instance_type = "t2.micro" lifecycle { postcondition { condition = self.status == "running" error_message = "Instance is not running after creation" } } }
In the above example, this postcondition will make sure the status of the resource example_ec2_instance is running when created. If this condition does not happen, then an error message will be shown.
Arguments
1. condition
The condition argument must be a Boolean expression whose result is either true or false, and it can refer to any other objects of the same module while it must not introduce
circular dependencies
, which means dependencies pointing back to each other. For post-conditions you can also use the self
object if you want to access the attributes from the resource that is being created or modified. So you will be able to check the resource properties after its creation or modification.
2. error_message
error_message is a string expression that describes an error message which will be shown when this condition is not met. If a condition fails, the result of the error_message expression will be part of the resulting error message from Terraform. This allows you to provide a custom error message that can explain to the user why it failed and make it easier to understand and fix.
How do they work?
- Precondition: Terraform will run the precondition block before making the associated object (resource, data source, or output). If the precondition is not met, Terraform will not create or modify that object.
- Postcondition: Terraform will execute the post condition block after creating or updating the associated object. It thus allows you to check if the object has been created or modified according to your expectations based on a given condition.
When are they checked?
When the condition is not dependent on unknown values, Terraform will validate custom conditions as early as possible. That is, if your condition within Terraform does not depend on any unknown or dynamically generated values, Terraform will check it at the planning stage of the deployment.
On the other hand, if a condition does depend on
unknown values
, Terraform delays the checking of the condition until the apply phase. This will be because these unknowns such as resource IDs, resource attributes or outputs, are only known after the resources have been created or updated.
For instance, consider the following example of a postcondition that we define on a null_resource resource, specifically to check its id. The problem is that this null_resource resource hasn't been built yet, and therefore we don't know in advance what its id will be.
resource "null_resource" "postcondition" {
provisioner "local-exec" {
command = "echo 'Postcondition'"
}
lifecycle {
postcondition {
condition = self.id == 123
error_message = "Running while apply phase"
}
}
}
Terraform will perform the following actions:
# null_resource.postcondition will be created
+ resource "null_resource" "postcondition" {
+ id = (known after apply)
}
Plan: 1 to add, 0 to change, 0 to destroy.
Enter a value: yes
null_resource.postcondition: Creating...
null_resource.postcondition: Provisioning with 'local-exec'...
null_resource.postcondition: Executing: ["/bin/sh" "-c" "echo 'postcondition'"]
null_resource.postcondition: postcondition
null_resource.postcondition: Creation complete after 0s [id=4425172414869377883]
| Error: Resource postcondition failed
| self.id is "123"
| Running while apply phase
The first apply of this Terraform configuration, terraform is going to provision the null_resource with the local-exec provisioner and the postcondition is going to be evaluated during apply since the id attribute is unknown at plan time.
If we run apply the configuration again, Terraform will refresh the state of the null_resource and check the postcondition in the plan phase since the id attribute is known. This is because Terraform reference the existing state of the null_resource to check the condition.
Terraform will perform the following actions:
null_resource.default: Refreshing state... [id=6572268843874077948]
| Error: Resource postcondition failed
| self.id is "123"
| Running while apply phase
In summary, custom conditions are evaluated as early as possible during the plan phase, Terraform postpones the evaluation to the apply phase if the condition depends on the values that will be unknown until after the resources have been provisioned or updated.
Precondition in output block
An output block in Terraform can have a precondition block, but not a postcondition block. The precondition block is similar in concept to input validation blocks. While input validation checked assumptions about the inputs, preconditions check guarantees on the outputs. Preconditions can also catch Terraform from saving an invalid output value in the statefile. And they can preserve a valid output value from the previous apply.
local {
output = -10
}
output "availability_zone" {
value = local.output
description = "An example output value"
precondition {
condition = local.output > 0
error_message = "The example output value must be greater than 0."
}
}
When you run this Terraform configuration, Terraform will execute the output block and validate the precondition. Since the local.output is set to "-10", it does not meet the condition of greater than 0 and therefore Terraform will return the following error:
Terraform will perform the following actions:
| Error: Module output value precondition failed
| local.output is -10
| The example output value must be greater than 0.
NOTE: Postcondition doesn't supported in output block.
Precondition and Postcondition in Resources and Data Sources
In Terraform, resources and data sources can support preconditions or postconditions. However, this needs to be defined inside the resource and data source's lifecycle block
The Terraform precondition blocks are evaluated after existing count and for_each arguments have been evaluated. That way Terraform can evaluate the precondition separately for each instance, and the each.key, count.index and other related variables are available within the precondition block. For example,
resource "null_resource" "postcondition" {
for_each = toset(["first-instance","second-instance","third-instance"])
provisioner "local-exec" {
command = "echo '${each.value}'"
}
lifecycle {
precondition {
condition = each.value == "first-instance"
error_message = "Example error"
}
}
}
Terraform will perform the following actions: | Resource precondition failed | each.value is "second-instance" | Example error | Resource precondition failed | each.value is "third-instance" | Example error
In the above example, the precondition block will be evaluated after the evaluation of for_each expression. If the value of the current instance is not "first-instance", then Terraform provides the message "Example error" and will abort the plan or apply.
Related Pages
- Variables - Input Values and Output Values
Feedback
Was this page helpful?