- Expressions
- Custom Conditions
Custom Conditions
You can create conditions that produce custom error messages for several types of objects in a configuration. For example, you can add a condition to an input variable that checks whether incoming image IDs are formatted properly. Custom conditions can capture assumptions, helping future maintainers understand the configuration design and intent. They also return useful information about errors earlier and in context, helping consumers more easily diagnose issues in their configurations.
This page explains the following:
- Creating checks with assertions to verify your infrastructure as a whole
- Creating validation conditions for input variables
- Creating preconditions and postconditions for resources, data sources, and outputs
- Writing effective condition expressions and error messages
- When OpenTofu evaluates custom conditions during the plan and apply cycle
Selecting a Custom Condition for your use case
OpenTofu's different custom conditions are best suited to various situations. Use the following broad guidelines to select the best custom condition for your use case:
- Check blocks with assertions validate your infrastructure as a whole. Additionally, check blocks do not prevent or block the overall execution of OpenTofu operations.
- Validation conditions or output postconditions can ensure your configuration's inputs and outputs meet specific requirements.
- Resource preconditions and postconditions can validate that OpenTofu produces your configuration with predictable results.
For more information on when to use certain custom conditions, see Choosing Between Preconditions and Postconditions and Choosing Checks or Other Custom Conditions.
Input Variable Validation
Add one or more validation
blocks within the variable
block to specify custom conditions. Each validation requires a condition
argument, an expression that must use the value of the variable to return true
if the value is valid, or false
if it is invalid. The expression can refer only to the containing variable and must not produce errors.
If the condition evaluates to false
, OpenTofu produces an error message that includes the result of the error_message
expression. If you declare multiple validations, OpenTofu returns error messages for all failed conditions.
The following example checks whether the AMI ID has valid syntax.
If the failure of an expression determines the validation decision, use the can
function as demonstrated in the following example.
Preconditions and Postconditions
Use precondition
and postcondition
blocks to create custom rules for resources, data sources, and outputs.
OpenTofu checks a precondition before evaluating the object it is associated with and checks a postcondition after evaluating the object. OpenTofu evaluates custom conditions as early as possible, but must defer conditions that depend on unknown values until the apply phase. Refer to Conditions Checked Only During Apply for more details.
Usage
Each precondition and postcondition requires a condition
argument. This is an expression that must return true
if the conditition is fufilled or false
if it is invalid. The expression can refer to any other objects in the same module, as long as the references do not create cyclic dependencies. Resource postconditions can also use the self
object to refer to attributes of each instance of the resource where they are configured.
If the condition evaluates to false
, OpenTofu will produce an error message that includes the result of the error_message
expression. If you declare multiple preconditions or postconditions, OpenTofu returns error messages for all failed conditions.
The following example uses a postcondition to detect if the caller accidentally provided an AMI intended for the wrong system component.
Resources and Data Sources
The lifecycle
block inside a resource
or data
block can include both precondition
and postcondition
blocks.
- OpenTofu evaluates
precondition
blocks after evaluating existingcount
andfor_each
arguments. This lets OpenTofu evaluate the precondition separately for each instance and then makeeach.key
,count.index
, etc. available to those conditions. OpenTofu also evaluates preconditions before evaluating the resource's configuration arguments. Preconditions can take precedence over argument evaluation errors. - OpenTofu evaluates
postcondition
blocks after planning and applying changes to a managed resource, or after reading from a data source. Postcondition failures prevent changes to other resources that depend on the failing resource.
In most cases, we do not recommend including both a data
block and a resource
block that both represent the same object in the same configuration. Doing so can prevent OpenTofu from understanding that the data
block result can be affected by changes in the resource
block. However, when you need to check a result of a resource
block that the resource itself does not directly export, you can use a data
block to check that object safely as long as you place the check as a direct postcondition
of the data
block. This tells OpenTofu that the data
block is serving as a check of an object defined elsewhere, allowing OpenTofu to perform actions in the correct order.
Outputs
An output
block can include a precondition
block.
Preconditions can serve a symmetrical purpose to input variable validation
blocks. Whereas input variable validation checks assumptions the module makes about its inputs, preconditions check guarantees that the module makes about its outputs. You can use preconditions to prevent OpenTofu from saving an invalid new output value in the state. You can also use them to preserve a valid output value from the previous apply, if applicable.
OpenTofu evaluates output value preconditions before evaluating the value
expression to finalize the result. Preconditions can take precedence over potential errors in the value
expression.
Examples
The following example shows use cases for preconditions and postconditions. The preconditions and postconditions declare the following assumptions and guarantees.
-
The AMI ID must refer to an AMI that contains an operating system for the
x86_64
architecture. The precondition would detect if the caller accidentally built an AMI for a different architecture, which may not be able to run the software this virtual machine is intended to host. -
The EC2 instance must be allocated a public DNS hostname. In Amazon Web Services, EC2 instances are assigned public DNS hostnames only if they belong to a virtual network configured in a certain way. The postcondition would detect if the selected virtual network is not configured correctly, prompting the user to debug the network settings.
-
The EC2 instance will have an encrypted root volume. The precondition ensures that the root volume is encrypted, even though the software running in this EC2 instance would probably still operate as expected on an unencrypted volume. This lets OpenTofu produce an error immediately, before any other components rely on the new EC2 instance.
Choosing Between Preconditions and Postconditions
You can often implement a validation check as either a postcondition of the resource producing the data or as a precondition of a resource or output value using the data. To decide which is most appropriate, consider whether the check is representing either an assumption or a guarantee.
Use Preconditions for Assumptions
An assumption is a condition that must be true in order for the configuration of a particular resource to be usable. For example, an aws_instance
configuration can have the assumption that the given AMI will always be configured for the x86_64
CPU architecture.
We recommend using preconditions for assumptions, so that future maintainers can find them close to the other expressions that rely on that condition. This lets them understand more about what that resource is intended to allow.
Use Postconditions for Guarantees
A guarantee is a characteristic or behavior of an object that the rest of the configuration should be able to rely on. For example, an aws_instance
configuration can have the guarantee that an EC2 instance will be running in a network that assigns it a private DNS record.
We recommend using postconditions for guarantees, so that future maintainers can find them close to the resource configuration that is responsible for implementing those guarantees. This lets them more easily determine which behaviors they should preserve when changing the configuration.
Additional Decision Factors
You should also consider the following questions when creating preconditions and postconditions.
- Which resource or output value would be most helpful to report in the error message? OpenTofu will always report errors in the location where the condition was declared.
- Which approach is more convenient? If a particular resource has many dependencies that all make an assumption about that resource, it can be pragmatic to declare that once as a post-condition of the resource, rather than declaring it many times as preconditions on each of the dependencies.
- Is it helpful to declare the same or similar conditions as both preconditions and postconditions? This can be useful if the postcondition is in a different module than the precondition because it lets the modules verify one another as they evolve independently.
Checks with Assertions
Check blocks can validate your infrastructure outside the usual resource lifecycle. You can add custom conditions via assert
blocks, which execute at the end of the plan and apply stages and produce warnings to notify you of problems within your infrastructure.
You can add one or more assert
blocks within a check
block to verify custom conditions. Each assertion requires a condition
argument, a boolean expression that should return true
if the intended assumption or guarantee is fulfilled or false
if it does not. Your condition
expression can refer to any resource, data source, or variable available to the surrounding check
block.
The following example uses a check block with an assertion to verify the OpenTofu website is healthy.
If the condition evaluates to false
, OpenTofu produces an error message that includes the result of the error_message
expression. If you declare multiple assertions, OpenTofu returns error messages for all failed conditions.
Continuous Validation in a cloud backend
A cloud backend can automatically check whether the checks in a workspace’s configuration continue to pass after OpenTofu provisions the infrastructure. For example, you can write a check
to continuously monitor the validity of an API gateway certificate. Continuous validation alerts you when the condition fails, so you can update the certificate and avoid errors the next time you want to update your infrastructure.
Condition Expressions
Check assertions, input variable validation, preconditions, and postconditions all require a condition
argument. This is a boolean expression that should return true
if the intended assumption or guarantee is fulfilled or false
if it does not.
You can use any of OpenTofu's built-in functions or language operators in a condition as long as the expression is valid and returns a boolean result. The following language features are particularly useful when writing condition expressions.
Logical Operators
Use the logical operators &&
(AND), ||
(OR), and !
(NOT) to combine multiple conditions together.
You can also use arithmetic operators (e.g. a + b
), equality operators (eg., a == b
) and comparison operators (e.g., a < b
). Refer to Arithmetic and Logical Operators for details.
contains
Function
Use the contains
function to test whether a given value is one of a set of predefined valid values.
length
Function
Use the length
function to test a collection's length and require a non-empty list or map.
This is a better approach than directly comparing with another collection using ==
or !=
. This is because the comparison operators can only return true
if both operands have exactly the same type, which is often ambiguous for empty collections.
for
Expressions
Use for
expressions in conjunction with the functions alltrue
and anytrue
to test whether a condition holds for all or for any elements of a collection.
can
Function
Use the can
function to concisely use the validity of an expression as a condition. It returns true
if its given expression evaluates successfully and false
if it returns any error, so you can use various other functions that typically return errors as a part of your condition expressions.
For example, you can use can
with regex
to test if a string matches a particular pattern because regex
returns an error when given a non-matching string.
You can also use can
with the type conversion functions to test whether a value is convertible to a type or type constraint.
You can also use can
with attribute access or index operators to test whether a collection or structural value has a particular element or index.
self
Object
Use the self
object in postcondition blocks to refer to attributes of the instance under evaluation.
each
and count
Objects
In blocks where for_each
or count
are set, use each
and count
objects to refer to other resources that are expanded in a chain.
Error Messages
Input variable validations, preconditions, and postconditions all must include the error_message
argument. This contains the text that OpenTofu will include as part of error messages when it detects an unmet condition.
The error_message
argument can be any expression that evaluates to a string.
This includes literal strings, heredocs, and template expressions. You can use the format
function to convert items of null
, list
, or map
types into a formatted string. Multi-line
error messages are supported, and lines with leading whitespace will not be
word wrapped.
We recommend writing error messages as one or more full sentences in a style similar to OpenTofu's own error messages. OpenTofu will show the message alongside the name of the resource that detected the problem and any external values included in the condition expression.
Conditions Checked Only During Apply
OpenTofu evaluates custom conditions as early as possible.
Input variable validations can only refer to the variable value, so OpenTofu always evaluates them immediately. Check assertions, preconditions, and postconditions depend on OpenTofu evaluating whether the value(s) associated with the condition are known before or after applying the configuration.
- Known before apply: OpenTofu checks the condition during the planning phase. For example, OpenTofu can know the value of an image ID during planning as long as it is not generated from another resource.
- Known after apply: OpenTofu delays checking that condition until the apply phase. For example, AWS only assigns the root volume ID when it starts an EC2 instance, so OpenTofu cannot know this value until apply.
During the apply phase, a failed precondition will prevent OpenTofu from implementing planned actions for the associated resource. However, a failed postcondition will halt processing after OpenTofu has already implemented these actions. The failed postcondition prevents any further downstream actions that rely on the resource, but does not undo the actions OpenTofu has already taken.
OpenTofu typically has less information during the initial creation of a full configuration than when applying subsequent changes. Therefore, OpenTofu may check conditions during apply for initial creation and then check them during planning for subsequent updates.