mirror of
https://github.com/opentofu/opentofu.git
synced 2025-02-25 18:45:20 -06:00
Merge pull request #30772 from hashicorp/laura-update-pre-post-conditions
[WIP] Preconditions and Postconditions Content Updates
This commit is contained in:
commit
d3e660d912
@ -271,6 +271,10 @@
|
|||||||
"title": "Dynamic Blocks",
|
"title": "Dynamic Blocks",
|
||||||
"path": "expressions/dynamic-blocks"
|
"path": "expressions/dynamic-blocks"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"title": "Custom Condition Checks",
|
||||||
|
"path": "expressions/custom-conditions"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"title": "Type Constraints",
|
"title": "Type Constraints",
|
||||||
"path": "expressions/type-constraints"
|
"path": "expressions/type-constraints"
|
||||||
@ -278,10 +282,6 @@
|
|||||||
{
|
{
|
||||||
"title": "Version Constraints",
|
"title": "Version Constraints",
|
||||||
"path": "expressions/version-constraints"
|
"path": "expressions/version-constraints"
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Pre and Postconditions",
|
|
||||||
"path": "expressions/preconditions-postconditions"
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -122,6 +122,29 @@ referencing the managed resource values through a `local` value.
|
|||||||
|
|
||||||
~> **NOTE:** **In Terraform 0.12 and earlier**, due to the data resource behavior of deferring the read until the apply phase when depending on values that are not yet known, using `depends_on` with `data` resources will force the read to always be deferred to the apply phase, and therefore a configuration that uses `depends_on` with a `data` resource can never converge. Due to this behavior, we do not recommend using `depends_on` with data resources.
|
~> **NOTE:** **In Terraform 0.12 and earlier**, due to the data resource behavior of deferring the read until the apply phase when depending on values that are not yet known, using `depends_on` with `data` resources will force the read to always be deferred to the apply phase, and therefore a configuration that uses `depends_on` with a `data` resource can never converge. Due to this behavior, we do not recommend using `depends_on` with data resources.
|
||||||
|
|
||||||
|
## Custom Condition Checks
|
||||||
|
|
||||||
|
You can use `precondition` and `postcondition` blocks to specify assumptions and guarantees about how the data source operates. The following examples creates a postcondition that checks whether the AMI has the correct tags.
|
||||||
|
|
||||||
|
``` hcl
|
||||||
|
data "aws_ami" "example" {
|
||||||
|
id = var.aws_ami_id
|
||||||
|
|
||||||
|
lifecycle {
|
||||||
|
# The AMI ID must refer to an existing AMI that has the tag "nomad-server".
|
||||||
|
postcondition {
|
||||||
|
condition = self.tags["Component"] == "nomad-server"
|
||||||
|
error_message = "tags[\"Component\"] must be \"nomad-server\"."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Custom conditions can help 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.
|
||||||
|
|
||||||
|
Refer to [Custom Condition Checks](/language/expressions/custom-conditions#preconditions-and-postconditions) for more details.
|
||||||
|
|
||||||
|
|
||||||
## Multiple Resource Instances
|
## Multiple Resource Instances
|
||||||
|
|
||||||
Data resources support [`count`](/language/meta-arguments/count)
|
Data resources support [`count`](/language/meta-arguments/count)
|
||||||
|
@ -39,6 +39,14 @@ The condition can be any expression that resolves to a boolean value. This will
|
|||||||
usually be an expression that uses the equality, comparison, or logical
|
usually be an expression that uses the equality, comparison, or logical
|
||||||
operators.
|
operators.
|
||||||
|
|
||||||
|
### Custom Condition Checks
|
||||||
|
|
||||||
|
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 help 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.
|
||||||
|
|
||||||
|
Refer to [Custom Condition Checks](/language/expressions/custom-conditions#input-variable-validation) for details.
|
||||||
|
|
||||||
## Result Types
|
## Result Types
|
||||||
|
|
||||||
The two result values may be of any type, but they must both
|
The two result values may be of any type, but they must both
|
||||||
|
357
website/docs/language/expressions/custom-conditions.mdx
Normal file
357
website/docs/language/expressions/custom-conditions.mdx
Normal file
@ -0,0 +1,357 @@
|
|||||||
|
---
|
||||||
|
page_title: Custom Condition Checks - Configuration Language
|
||||||
|
description: >-
|
||||||
|
Check custom requirements for variables, outputs, data sources, and resources and provide better error messages in context.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Custom Condition Checks
|
||||||
|
|
||||||
|
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 help 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 [validation conditions](#input-variable-validation) for input variables
|
||||||
|
- Creating [preconditions and postconditions](#preconditions-and-postconditions) for resources, data sources, and outputs
|
||||||
|
- Writing effective [condition expressions](#condition-expressions) and [error messages](#error-messages)
|
||||||
|
- When Terraform [evaluates custom conditions](#conditions-checked-only-during-apply) during the plan and apply cycle
|
||||||
|
|
||||||
|
|
||||||
|
## Input Variable Validation
|
||||||
|
|
||||||
|
-> **Note:** Input variable validation is available in Terraform CLI v0.13.0 and later.
|
||||||
|
|
||||||
|
Add one or more `validation` blocks within the `variable` block to specify custom conditions. Each validation requires a [`condition` argument](#condition-expressions), 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`, Terraform produces an [error message](#error-messages) that includes the result of the `error_message` expression. If you declare multiple validations, Terraform returns error messages for all failed conditions.
|
||||||
|
|
||||||
|
The following example checks whether the AMI ID has valid syntax.
|
||||||
|
|
||||||
|
```hcl
|
||||||
|
variable "image_id" {
|
||||||
|
type = string
|
||||||
|
description = "The id of the machine image (AMI) to use for the server."
|
||||||
|
|
||||||
|
validation {
|
||||||
|
condition = length(var.image_id) > 4 && substr(var.image_id, 0, 4) == "ami-"
|
||||||
|
error_message = "The image_id value must be a valid AMI id, starting with \"ami-\"."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
If the failure of an expression determines the validation decision, use the [`can` function](/language/functions/can) as demonstrated in the following example.
|
||||||
|
|
||||||
|
```hcl
|
||||||
|
variable "image_id" {
|
||||||
|
type = string
|
||||||
|
description = "The id of the machine image (AMI) to use for the server."
|
||||||
|
|
||||||
|
validation {
|
||||||
|
# regex(...) fails if it cannot find a match
|
||||||
|
condition = can(regex("^ami-", var.image_id))
|
||||||
|
error_message = "The image_id value must be a valid AMI id, starting with \"ami-\"."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Preconditions and Postconditions
|
||||||
|
|
||||||
|
-> **Note:** Preconditions and postconditions are available in Terraform CLI v1.2.0 and later.
|
||||||
|
|
||||||
|
Use `precondition` and `postcondition` blocks to create custom rules for resources, data sources, and outputs.
|
||||||
|
|
||||||
|
Terraform checks a precondition _before_ evaluating the object it is associated with and checks a postcondition _after_ evaluating the object. Terraform 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](#conditions-checked-only-during-apply) for more details.
|
||||||
|
|
||||||
|
### Usage
|
||||||
|
|
||||||
|
Each precondition and postcondition requires a [`condition` argument](#condition-expressions). 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](#self-object) to refer to attributes of each instance of the resource where they are configured.
|
||||||
|
|
||||||
|
If the condition evaluates to `false`, Terraform will produce an [error message](#error-messages) that includes the result of the `error_message` expression. If you declare multiple preconditions or postconditions, Terraform 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.
|
||||||
|
|
||||||
|
``` hcl
|
||||||
|
data "aws_ami" "example" {
|
||||||
|
id = var.aws_ami_id
|
||||||
|
|
||||||
|
lifecycle {
|
||||||
|
# The AMI ID must refer to an existing AMI that has the tag "nomad-server".
|
||||||
|
postcondition {
|
||||||
|
condition = self.tags["Component"] == "nomad-server"
|
||||||
|
error_message = "tags[\"Component\"] must be \"nomad-server\"."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Resources and Data Sources
|
||||||
|
|
||||||
|
The `lifecycle` block inside a `resource` or `data` block can include both `precondition` and `postcondition` blocks.
|
||||||
|
|
||||||
|
- Terraform evaluates `precondition` blocks after evaluating existing `count` and `for_each` arguments. This lets Terraform evaluate the precondition separately for each instance and then make `each.key`, `count.index`, etc. available to those conditions. Terraform also evaluates preconditions before evaluating the resource's configuration arguments. Preconditions can take precedence over argument evaluation errors.
|
||||||
|
- Terraform 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.
|
||||||
|
|
||||||
|
#### 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 Terraform from saving an invalid new output value in the state. You can also use them to preserve the output value from the previous apply, if applicable.
|
||||||
|
|
||||||
|
Terraform 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 private DNS hostname.** In Amazon Web Services, EC2 instances are assigned private 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 Terraform produce an error immediately, before any other components rely on the new EC2 instance.
|
||||||
|
|
||||||
|
```hcl
|
||||||
|
|
||||||
|
resource "aws_instance" "example" {
|
||||||
|
instance_type = "t2.micro"
|
||||||
|
ami = "ami-abc123"
|
||||||
|
|
||||||
|
lifecycle {
|
||||||
|
# The AMI ID must refer to an AMI that contains an operating system
|
||||||
|
# for the `x86_64` architecture.
|
||||||
|
precondition {
|
||||||
|
condition = data.aws_ami.example.architecture == "x86_64"
|
||||||
|
error_message = "The selected AMI must be for the x86_64 architecture."
|
||||||
|
}
|
||||||
|
|
||||||
|
# The EC2 instance must be allocated a private DNS hostname.
|
||||||
|
postcondition {
|
||||||
|
condition = self.private_dns != ""
|
||||||
|
error_message = "EC2 instance must be in a VPC that has private DNS hostnames enabled."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data "aws_ebs_volume" "example" {
|
||||||
|
# Use data resources that refer to other resources to
|
||||||
|
# load extra data that isn't directly exported by a resource.
|
||||||
|
#
|
||||||
|
# Read the details about the root storage volume for the EC2 instance
|
||||||
|
# declared by aws_instance.example, using the exported ID.
|
||||||
|
|
||||||
|
filter {
|
||||||
|
name = "volume-id"
|
||||||
|
values = [aws_instance.example.root_block_device.volume_id]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
output "api_base_url" {
|
||||||
|
value = "https://${aws_instance.example.private_dns}:8433/"
|
||||||
|
|
||||||
|
# The EC2 instance will have an encrypted root volume.
|
||||||
|
precondition {
|
||||||
|
condition = data.aws_ebs_volume.example.encrypted
|
||||||
|
error_message = "The server's root volume is not encrypted."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 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? Terraform 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.
|
||||||
|
|
||||||
|
|
||||||
|
## Condition Expressions
|
||||||
|
|
||||||
|
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 Terraform'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.
|
||||||
|
|
||||||
|
```hcl
|
||||||
|
condition = var.name != "" && lower(var.name) == var.name
|
||||||
|
```
|
||||||
|
|
||||||
|
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](/language/expressions/operators) for details.
|
||||||
|
|
||||||
|
### `contains` Function
|
||||||
|
|
||||||
|
Use the [`contains` function](/language/functions/contains) to test whether a given value is one of a set of predefined valid values.
|
||||||
|
|
||||||
|
```hcl
|
||||||
|
condition = contains(["STAGE", "PROD"], var.environment)
|
||||||
|
```
|
||||||
|
|
||||||
|
### `length` Function
|
||||||
|
|
||||||
|
Use the [`length` function](/language/functions/length) to test a collection's length and require a non-empty list or map.
|
||||||
|
|
||||||
|
```hcl
|
||||||
|
condition = length(var.items) != 0
|
||||||
|
```
|
||||||
|
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](/language/expressions/for) in conjunction with the functions `alltrue` and `anytrue` to test whether a condition holds for all or for any elements of a collection.
|
||||||
|
|
||||||
|
```hcl
|
||||||
|
condition = alltrue([
|
||||||
|
for v in var.instances : contains(["t2.micro", "m3.medium"], v.type)
|
||||||
|
])
|
||||||
|
```
|
||||||
|
|
||||||
|
### `can` Function
|
||||||
|
|
||||||
|
Use the [`can` function](/language/functions/can) 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.
|
||||||
|
|
||||||
|
```hcl
|
||||||
|
condition = can(regex("^[a-z]+$", var.name)
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also use `can` with the type conversion functions to test whether a value is convertible to a type or type constraint.
|
||||||
|
|
||||||
|
```hcl
|
||||||
|
# This remote output value must have a value that can
|
||||||
|
# be used as a string, which includes strings themselves
|
||||||
|
# but also allows numbers and boolean values.
|
||||||
|
condition = can(tostring(data.terraform_remote_state.example.outputs["name"]))
|
||||||
|
```
|
||||||
|
|
||||||
|
```hcl
|
||||||
|
# This remote output value must be convertible to a list
|
||||||
|
# type of with element type.
|
||||||
|
condition = can(tolist(data.terraform_remote_state.example.outputs["items"]))
|
||||||
|
```
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
```hcl
|
||||||
|
# var.example must have an attribute named "foo"
|
||||||
|
condition = can(var.example.foo) ```
|
||||||
|
|
||||||
|
```hcl
|
||||||
|
# var.example must be a sequence with at least one element
|
||||||
|
condition = can(var.example[0])
|
||||||
|
# (although it would typically be clearer to write this as a
|
||||||
|
# test like length(var.example) > 0 to better represent the
|
||||||
|
# intent of the condition.)
|
||||||
|
```
|
||||||
|
|
||||||
|
### `self` Object
|
||||||
|
|
||||||
|
Use the `self` object in postcondition blocks to refer to attributes of the instance under evaluation.
|
||||||
|
|
||||||
|
```hcl
|
||||||
|
resource "aws_instance" "example" {
|
||||||
|
instance_type = "t2.micro"
|
||||||
|
ami = "ami-abc123"
|
||||||
|
|
||||||
|
lifecycle {
|
||||||
|
postcondition {
|
||||||
|
condition = self.instance_state == "running"
|
||||||
|
error_message = "EC2 instance must be running."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### `each` and `count` Objects
|
||||||
|
|
||||||
|
In blocks where [`for_each`](/language/meta-arguments/for_each) or [`count`](/language/meta-arguments/count) are set, use `each` and `count` objects to refer to other resources that are expanded in a chain.
|
||||||
|
|
||||||
|
```hcl
|
||||||
|
variable "vpc_cidrs" {
|
||||||
|
type = set(string)
|
||||||
|
}
|
||||||
|
|
||||||
|
data "aws_vpc" "example" {
|
||||||
|
for_each = var.vpc_cidrs
|
||||||
|
|
||||||
|
filter {
|
||||||
|
name = "cidr"
|
||||||
|
values = [each.key]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_internet_gateway" "example" {
|
||||||
|
for_each = aws_vpc.example
|
||||||
|
vpc_id = each.value.id
|
||||||
|
|
||||||
|
lifecycle {
|
||||||
|
precondition {
|
||||||
|
condition = aws_vpc.example[each.key].state == "available"
|
||||||
|
error_message = "VPC ${each.key} must be available."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Error Messages
|
||||||
|
|
||||||
|
Input variable validations, preconditions, and postconditions all must include the `error_message` argument. This contains the text that Terraform will include as part of error messages when it detects an unmet condition.
|
||||||
|
|
||||||
|
```
|
||||||
|
Error: Resource postcondition failed
|
||||||
|
|
||||||
|
with data.aws_ami.example,
|
||||||
|
on ec2.tf line 19, in data "aws_ami" "example":
|
||||||
|
72: condition = self.tags["Component"] == "nomad-server"
|
||||||
|
|----------------
|
||||||
|
| self.tags["Component"] is "consul-server"
|
||||||
|
|
||||||
|
The selected AMI must be tagged with the Component value "nomad-server".
|
||||||
|
```
|
||||||
|
|
||||||
|
The `error_message` argument can be any expression that evaluates to a string.
|
||||||
|
This includes literal strings, heredocs, and template expressions. 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 Terraform's own error messages. Terraform 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
|
||||||
|
|
||||||
|
Terraform evaluates custom conditions as early as possible.
|
||||||
|
|
||||||
|
Input variable validations can only refer to the variable value, so Terraform always evaluates them immediately. When Terraform evaluates preconditions and postconditions depends on whether the value(s) associated with the condition are known before or after applying the configuration.
|
||||||
|
|
||||||
|
- **Known before apply:** Terraform checks the condition during the planning phase. For example, Terraform can know the value of an image ID during planning as long as it is not generated from another resource.
|
||||||
|
- **Known after apply:** Terraform delays checking that condition until the apply phase. For example, AWS only assigns the root volume ID when it starts an EC2 instance, so Terraform cannot know this value until apply.
|
||||||
|
|
||||||
|
During the apply phase, a failed _precondition_
|
||||||
|
will prevent Terraform from implementing planned actions for the associated resource. However, a failed _postcondition_ will halt processing after Terraform has already implemented these actions. The failed postcondition prevents any further downstream actions that rely on the resource, but does not undo the actions Terraform has already taken.
|
||||||
|
|
||||||
|
Terraform typically has less information during the initial creation of a
|
||||||
|
full configuration than when applying subsequent changes. Therefore, Terraform may check conditions during apply for initial creation and then check them during planning for subsequent updates.
|
||||||
|
|
@ -1,395 +0,0 @@
|
|||||||
---
|
|
||||||
page_title: Preconditions and Postconditions - Configuration Language
|
|
||||||
---
|
|
||||||
|
|
||||||
# Preconditions and Postconditions
|
|
||||||
|
|
||||||
Terraform providers can automatically detect and report problems related to
|
|
||||||
the remote system they are interacting with, but they typically do so using
|
|
||||||
language that describes implementation details of the target system, which
|
|
||||||
can sometimes make it hard to find the root cause of the problem in your
|
|
||||||
Terraform configuration.
|
|
||||||
|
|
||||||
Preconditions and postconditions allow you to optionally describe the
|
|
||||||
assumptions you are making as a module author, so that Terraform can detect
|
|
||||||
situations where those assumptions don't hold and potentially return an
|
|
||||||
error earlier or an error with better context about where the problem
|
|
||||||
originated.
|
|
||||||
|
|
||||||
Preconditions and postconditions both follow a similar structure, and differ
|
|
||||||
only in when Terraform evaluates them: Terraform checks a precondition prior
|
|
||||||
to evaluating the object it is associated with, and a postcondition _after_
|
|
||||||
evaluating the object. That means that preconditions are useful for stating
|
|
||||||
assumptions about data from elsewhere that the resource configuration relies
|
|
||||||
on, while postconditions are more useful for stating assumptions about the
|
|
||||||
result of the resource itself.
|
|
||||||
|
|
||||||
The following example shows some different possible uses of preconditions and
|
|
||||||
postconditions.
|
|
||||||
|
|
||||||
```hcl
|
|
||||||
variable "aws_ami_id" {
|
|
||||||
type = string
|
|
||||||
|
|
||||||
# Input variable validation can check that the AMI ID is syntactically valid.
|
|
||||||
validation {
|
|
||||||
condition = can(regex("^ami-", var.aws_ami_id))
|
|
||||||
error_message = "The AMI ID must have the prefix \"ami-\"."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
data "aws_ami" "example" {
|
|
||||||
id = var.aws_ami_id
|
|
||||||
|
|
||||||
lifecycle {
|
|
||||||
# A data resource with a postcondition can ensure that the selected AMI
|
|
||||||
# meets this module's expectations, by reacting to the dynamically-loaded
|
|
||||||
# AMI attributes.
|
|
||||||
postcondition {
|
|
||||||
condition = self.tags["Component"] == "nomad-server"
|
|
||||||
error_message = "The selected AMI must be tagged with the Component value \"nomad-server\"."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
resource "aws_instance" "example" {
|
|
||||||
instance_type = "t2.micro"
|
|
||||||
ami = "ami-abc123"
|
|
||||||
|
|
||||||
lifecycle {
|
|
||||||
# A resource with a precondition can ensure that the selected AMI
|
|
||||||
# is set up correctly to work with the instance configuration.
|
|
||||||
precondition {
|
|
||||||
condition = data.aws_ami.example.architecture == "x86_64"
|
|
||||||
error_message = "The selected AMI must be for the x86_64 architecture."
|
|
||||||
}
|
|
||||||
|
|
||||||
# A resource with a postcondition can react to server-decided values
|
|
||||||
# during the apply step and halt work immediately if the result doesn't
|
|
||||||
# meet expectations.
|
|
||||||
postcondition {
|
|
||||||
condition = self.private_dns != ""
|
|
||||||
error_message = "EC2 instance must be in a VPC that has private DNS hostnames enabled."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
data "aws_ebs_volume" "example" {
|
|
||||||
# We can use data resources that refer to other resources in order to
|
|
||||||
# load extra data that isn't directly exported by a resource.
|
|
||||||
#
|
|
||||||
# This example reads the details about the root storage volume for
|
|
||||||
# the EC2 instance declared by aws_instance.example, using the exported ID.
|
|
||||||
|
|
||||||
filter {
|
|
||||||
name = "volume-id"
|
|
||||||
values = [aws_instance.example.root_block_device.volume_id]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
output "api_base_url" {
|
|
||||||
value = "https://${aws_instance.example.private_dns}:8433/"
|
|
||||||
|
|
||||||
# An output value with a precondition can check the object that the
|
|
||||||
# output value is describing to make sure it meets expectations before
|
|
||||||
# any caller of this module can use it.
|
|
||||||
precondition {
|
|
||||||
condition = data.aws_ebs_volume.example.encrypted
|
|
||||||
error_message = "The server's root volume is not encrypted."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
The input variable validation rule, preconditions, and postconditions in the
|
|
||||||
above example declare explicitly some assumptions and guarantees that the
|
|
||||||
module developer is making in the design of this module:
|
|
||||||
|
|
||||||
* The caller of the module must provide a syntactically-valid AMI ID in the
|
|
||||||
`aws_ami_id` input variable.
|
|
||||||
|
|
||||||
This would detect if the caller accidentally assigned an AMI name to the
|
|
||||||
argument, instead of an AMI ID.
|
|
||||||
|
|
||||||
* The AMI ID must refer to an AMI that exists and that has been tagged as
|
|
||||||
being intended for the component "nomad-server".
|
|
||||||
|
|
||||||
This would detect if the caller accidentally provided an AMI intended for
|
|
||||||
some other system component, which might otherwise be detected only after
|
|
||||||
booting the EC2 instance and noticing that the expected network service
|
|
||||||
isn't running. Terraform can therefore detect that problem earlier and
|
|
||||||
return a more actionable error message for it.
|
|
||||||
|
|
||||||
* The AMI ID must refer to an AMI which contains an operating system for the
|
|
||||||
`x86_64` architecture.
|
|
||||||
|
|
||||||
This would detect if the caller accidentally built an AMI for a different
|
|
||||||
architecture, which might therefore not be able to run the software this
|
|
||||||
virtual machine is intended to host.
|
|
||||||
|
|
||||||
* The EC2 instance must be allocated a private DNS hostname.
|
|
||||||
|
|
||||||
In AWS, EC2 instances are assigned private DNS hostnames only if they
|
|
||||||
belong to a virtual network configured in a certain way. This would
|
|
||||||
detect if the selected virtual network is not configured correctly,
|
|
||||||
giving explicit feedback to prompt the user to debug the network settings.
|
|
||||||
|
|
||||||
* The EC2 instance will have an encrypted root volume.
|
|
||||||
|
|
||||||
This 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. Therefore Terraform can draw attention to the
|
|
||||||
problem immediately, before any other components rely on the
|
|
||||||
insecurely-configured component.
|
|
||||||
|
|
||||||
Writing explicit preconditions and postconditions is always optional, but it
|
|
||||||
can be helpful to users and future maintainers of a Terraform module by
|
|
||||||
capturing assumptions that might otherwise be only implied, and by allowing
|
|
||||||
Terraform to check those assumptions and halt more quickly if they don't
|
|
||||||
hold in practice for a particular set of input variables.
|
|
||||||
|
|
||||||
## Precondition and Postcondition Locations
|
|
||||||
|
|
||||||
Terraform supports preconditions and postconditions in a number of different
|
|
||||||
locations in a module:
|
|
||||||
|
|
||||||
* The `lifecycle` block inside a `resource` or `data` block can include both
|
|
||||||
`precondition` and `postcondition` blocks associated with the containing
|
|
||||||
resource.
|
|
||||||
|
|
||||||
Terraform evaluates resource preconditions before evaluating the resource's
|
|
||||||
configuration arguments. Resource preconditions can take precedence over
|
|
||||||
argument evaluation errors.
|
|
||||||
|
|
||||||
Terraform evaluates resource postconditions after planning and after
|
|
||||||
applying changes to a managed resource, or after reading from a data
|
|
||||||
resource. Resource postcondition failures will therefore prevent applying
|
|
||||||
changes to other resources that depend on the failing resource.
|
|
||||||
|
|
||||||
* An `output` block declaring an output value can include a `precondition`
|
|
||||||
block.
|
|
||||||
|
|
||||||
Terraform evaluates output value preconditions before evaluating the
|
|
||||||
`value` expression to finalize the result. Output value preconditions
|
|
||||||
can take precedence over potential errors in the `value` expression.
|
|
||||||
|
|
||||||
Output value preconditions can be particularly useful in a root module,
|
|
||||||
to prevent saving an invalid new output value in the state and to preserve
|
|
||||||
the value from the previous apply, if any.
|
|
||||||
|
|
||||||
Output value preconditions can serve a symmetrical purpose to input
|
|
||||||
variable `validation` blocks: whereas input variable validation checks
|
|
||||||
assumptions the module makes about its inputs, output value preconditions
|
|
||||||
check guarantees that the module makes about its outputs.
|
|
||||||
|
|
||||||
## Condition Expressions
|
|
||||||
|
|
||||||
`precondition` and `postcondition` blocks both require an argument named
|
|
||||||
`condition`, whose value is a boolean expression which should return `true`
|
|
||||||
if the intended assumption holds or `false` if it does not.
|
|
||||||
|
|
||||||
Preconditions and postconditions can both refer to any other objects in the
|
|
||||||
same module, as long as the references don't create any cyclic dependencies.
|
|
||||||
|
|
||||||
Resource postconditions can additionally refer to attributes of each instance
|
|
||||||
of the resource where they are configured, using the special symbol `self`.
|
|
||||||
For example, `self.private_dns` refers to the `private_dns` attribute of
|
|
||||||
each instance of the containing resource.
|
|
||||||
|
|
||||||
Condition expressions are otherwise just normal Terraform expressions, and
|
|
||||||
so you can use any of Terraform's built-in functions or language operators
|
|
||||||
as long as the expression is valid and returns a boolean result.
|
|
||||||
|
|
||||||
### Common Condition Expression Features
|
|
||||||
|
|
||||||
Because condition expressions must produce boolean results, they can often
|
|
||||||
use built-in functions and language features that are less common elsewhere
|
|
||||||
in the Terraform language. The following language features are particularly
|
|
||||||
useful when writing condition expressions:
|
|
||||||
|
|
||||||
* You can use the built-in function `contains` to test whether a given
|
|
||||||
value is one of a set of predefined valid values:
|
|
||||||
|
|
||||||
```hcl
|
|
||||||
condition = contains(["STAGE", "PROD"], var.environment)
|
|
||||||
```
|
|
||||||
|
|
||||||
* You can use the boolean operators `&&` (AND), `||` (OR), and `!` (NOT) to
|
|
||||||
combine multiple simpler conditions together:
|
|
||||||
|
|
||||||
```hcl
|
|
||||||
condition = var.name != "" && lower(var.name) == var.name
|
|
||||||
```
|
|
||||||
|
|
||||||
* You can require a non-empty list or map by testing the collection's length:
|
|
||||||
|
|
||||||
```hcl
|
|
||||||
condition = length(var.items) != 0
|
|
||||||
```
|
|
||||||
|
|
||||||
This is a better approach than directly comparing with another collection
|
|
||||||
using `==` or `!=`, because the comparison operators can only return `true`
|
|
||||||
if both operands have exactly the same type, which is often ambiguous
|
|
||||||
for empty collections.
|
|
||||||
|
|
||||||
* You can use `for` expressions which produce lists of boolean results
|
|
||||||
themselves in conjunction with the functions `alltrue` and `anytrue` to
|
|
||||||
test whether a condition holds for all or for any elements of a collection:
|
|
||||||
|
|
||||||
```hcl
|
|
||||||
condition = alltrue([
|
|
||||||
for v in var.instances : contains(["t2.micro", "m3.medium"], v.type)
|
|
||||||
])
|
|
||||||
```
|
|
||||||
|
|
||||||
* You can 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:
|
|
||||||
|
|
||||||
```hcl
|
|
||||||
condition = can(regex("^[a-z]+$", var.name)
|
|
||||||
```
|
|
||||||
|
|
||||||
You can also use `can` with the type conversion functions to test whether
|
|
||||||
a value is convertible to a type or type constraint:
|
|
||||||
|
|
||||||
```hcl
|
|
||||||
# This remote output value must have a value that can
|
|
||||||
# be used as a string, which includes strings themselves
|
|
||||||
# but also allows numbers and boolean values.
|
|
||||||
condition = can(tostring(data.terraform_remote_state.example.outputs["name"]))
|
|
||||||
```
|
|
||||||
|
|
||||||
```hcl
|
|
||||||
# This remote output value must be convertible to a list
|
|
||||||
# type of with element type.
|
|
||||||
condition = can(tolist(data.terraform_remote_state.example.outputs["items"]))
|
|
||||||
```
|
|
||||||
|
|
||||||
You can also use `can` with attribute access or index operators to
|
|
||||||
concisely test whether a collection or structural value has a particular
|
|
||||||
element or index:
|
|
||||||
|
|
||||||
```hcl
|
|
||||||
# var.example must have an attribute named "foo"
|
|
||||||
condition = can(var.example.foo)
|
|
||||||
```
|
|
||||||
|
|
||||||
```hcl
|
|
||||||
# var.example must be a sequence with at least one element
|
|
||||||
condition = can(var.example[0])
|
|
||||||
# (although it would typically be clearer to write this as a
|
|
||||||
# test like length(var.example) > 0 to better represent the
|
|
||||||
# intent of the condition.)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Early Evaluation
|
|
||||||
|
|
||||||
Terraform will evaluate conditions as early as possible.
|
|
||||||
|
|
||||||
If the condition expression depends on a resource attribute that won't be known
|
|
||||||
until the apply phase then Terraform will delay checking the condition until
|
|
||||||
the apply phase, but Terraform can check all other expressions during the
|
|
||||||
planning phase, and therefore block applying a plan that would violate the
|
|
||||||
conditions.
|
|
||||||
|
|
||||||
In the earlier example on this page, Terraform would typically be able to
|
|
||||||
detect invalid AMI tags during the planning phase, as long as `var.aws_ami_id`
|
|
||||||
is not itself derived from another resource. However, Terraform will not
|
|
||||||
detect a non-encrypted root volume until the EC2 instance was already created
|
|
||||||
during the apply step, because that condition depends on the root volume's
|
|
||||||
assigned ID, which AWS decides only when the EC2 instance is actually started.
|
|
||||||
|
|
||||||
For conditions which Terraform must defer to the apply phase, a _precondition_
|
|
||||||
will prevent taking whatever action was planned for a related resource, whereas
|
|
||||||
a _postcondition_ will merely halt processing after that action was already
|
|
||||||
taken, preventing any downstream actions that rely on it but not undoing the
|
|
||||||
action.
|
|
||||||
|
|
||||||
Terraform typically has less information during the initial creation of a
|
|
||||||
full configuration than when applying subsequent changes to that configuration.
|
|
||||||
Conditions checked only during apply during initial creation may therefore
|
|
||||||
be checked during planning on subsequent updates, detecting problems sooner
|
|
||||||
in that case.
|
|
||||||
|
|
||||||
## Error Messages
|
|
||||||
|
|
||||||
Each `precondition` or `postcondition` block must include an argument
|
|
||||||
`error_message`, which provides some custom error sentences that Terraform
|
|
||||||
will include as part of error messages when it detects an unmet condition.
|
|
||||||
|
|
||||||
```
|
|
||||||
Error: Resource postcondition failed
|
|
||||||
|
|
||||||
with data.aws_ami.example,
|
|
||||||
on ec2.tf line 19, in data "aws_ami" "example":
|
|
||||||
72: condition = self.tags["Component"] == "nomad-server"
|
|
||||||
|----------------
|
|
||||||
| self.tags["Component"] is "consul-server"
|
|
||||||
|
|
||||||
The selected AMI must be tagged with the Component value "nomad-server".
|
|
||||||
```
|
|
||||||
|
|
||||||
The `error_message` argument can be any expression which evaluates to a string.
|
|
||||||
This includes literal strings, heredocs, and template expressions. Multi-line
|
|
||||||
error messages are supported, and lines with leading whitespace will not be
|
|
||||||
word wrapped.
|
|
||||||
|
|
||||||
Error message should typically be written as one or more full sentences in a
|
|
||||||
style similar to Terraform's own error messages. Terraform will show the given
|
|
||||||
message alongside the name of the resource that detected the problem and any
|
|
||||||
outside values used as part of the condition expression.
|
|
||||||
|
|
||||||
## Preconditions or Postconditions?
|
|
||||||
|
|
||||||
Because preconditions can refer to the result attributes of other resources
|
|
||||||
in the same module, it's typically true that a particular check could be
|
|
||||||
implemented either as 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 for a particular situation, consider
|
|
||||||
whether the check is representing either an assumption or a guarantee:
|
|
||||||
|
|
||||||
* An _assumption_ is a condition that must be true in order for the
|
|
||||||
configuration of a particular resource to be usable. In the earlier
|
|
||||||
example on this page, the `aws_instance` configuration had the _assumption_
|
|
||||||
that the given AMI will always be for the `x86_64` CPU architecture.
|
|
||||||
|
|
||||||
Assumptions should typically be written as preconditions, so that future
|
|
||||||
maintainers can find them close to the other expressions that rely on
|
|
||||||
that condition, and thus know more about what different variations that
|
|
||||||
resource is intended to allow.
|
|
||||||
|
|
||||||
* A _guarantee_ is a characteristic or behavior of an object that the rest of
|
|
||||||
the configuration ought to be able to rely on. In the earlier example on
|
|
||||||
this page, the `aws_instance` configuration had the _guarantee_ that the
|
|
||||||
EC2 instance will be running in a network that assigns it a private DNS
|
|
||||||
record.
|
|
||||||
|
|
||||||
Guarantees should typically be written as postconditions, so that
|
|
||||||
future maintainers can find them close to the resource configuration that
|
|
||||||
is responsible for implementing those guarantees and more easily see
|
|
||||||
which behaviors are important to preserve when changing the configuration.
|
|
||||||
|
|
||||||
In practice though, the distinction between these two is subjective: is the
|
|
||||||
AMI being tagged as Component `"nomad-server"` a guarantee about the AMI or
|
|
||||||
an assumption made by the EC2 instance? To decide, it might help to consider
|
|
||||||
which resource or output value would be most helpful to report in a resulting
|
|
||||||
error message, because Terraform will always report errors in the location
|
|
||||||
where the condition was declared.
|
|
||||||
|
|
||||||
The decision between the two may also be a matter of convenience. If a
|
|
||||||
particular resource has many dependencies that _all_ make an assumption about
|
|
||||||
that resource then it can be pragmatic to declare that just once as a
|
|
||||||
post-condition of the resource, rather than many times as preconditions on
|
|
||||||
each of the dependencies.
|
|
||||||
|
|
||||||
It may sometimes be helpful to declare the same or similar conditions as both
|
|
||||||
preconditions _and_ postconditions, particularly if the postcondition is
|
|
||||||
in a different module than the precondition, so that they can verify one
|
|
||||||
another as the two modules evolve independently.
|
|
@ -110,6 +110,30 @@ The following arguments can be used within a `lifecycle` block:
|
|||||||
Only attributes defined by the resource type can be ignored.
|
Only attributes defined by the resource type can be ignored.
|
||||||
`ignore_changes` cannot be applied to itself or to any other meta-arguments.
|
`ignore_changes` cannot be applied to itself or to any other meta-arguments.
|
||||||
|
|
||||||
|
## Custom Condition Checks
|
||||||
|
|
||||||
|
You can add `precondition` and `postcondition` blocks with a `lifecycle` block to specify assumptions and guarantees about how resources and data sources operate. The following examples creates a precondition that checks whether the AMI is properly configured.
|
||||||
|
|
||||||
|
```hcl
|
||||||
|
resource "aws_instance" "example" {
|
||||||
|
instance_type = "t2.micro"
|
||||||
|
ami = "ami-abc123"
|
||||||
|
|
||||||
|
lifecycle {
|
||||||
|
# The AMI ID must refer to an AMI that contains an operating system
|
||||||
|
# for the `x86_64` architecture.
|
||||||
|
precondition {
|
||||||
|
condition = data.aws_ami.example.architecture == "x86_64"
|
||||||
|
error_message = "The selected AMI must be for the x86_64 architecture."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Custom conditions can help 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.
|
||||||
|
|
||||||
|
Refer to [Custom Conditions](/language/expressions/custom-conditions#preconditions-and-postconditions) for more details.
|
||||||
|
|
||||||
## Literal Values Only
|
## Literal Values Only
|
||||||
|
|
||||||
The `lifecycle` settings all affect how Terraform constructs and traverses
|
The `lifecycle` settings all affect how Terraform constructs and traverses
|
||||||
|
@ -186,6 +186,29 @@ be given inline as a single resource, but we can also compose together multiple
|
|||||||
modules as described elsewhere on this page in situations where the
|
modules as described elsewhere on this page in situations where the
|
||||||
dependencies themselves are complicated enough to benefit from abstractions.
|
dependencies themselves are complicated enough to benefit from abstractions.
|
||||||
|
|
||||||
|
## Assumptions and Guarantees
|
||||||
|
|
||||||
|
Every module has implicit assumptions and guarantees that define what data it expects and what data it produces for consumers.
|
||||||
|
|
||||||
|
- **Assumption:** 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.
|
||||||
|
- **Guarantee:** 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 [custom conditions](/language/expressions/custom-conditions) help capture and test for assumptions and guarantees. This helps future maintainers understand the configuration design and intent. Custom conditions also return useful information about errors earlier and in context, helping consumers more easily diagnose issues in their configurations.
|
||||||
|
|
||||||
|
The following examples creates a precondition that checks whether the EC2 instance has an encrypted root volume.
|
||||||
|
|
||||||
|
```hcl
|
||||||
|
output "api_base_url" {
|
||||||
|
value = "https://${aws_instance.example.private_dns}:8433/"
|
||||||
|
|
||||||
|
# The EC2 instance must have an encrypted root volume.
|
||||||
|
precondition {
|
||||||
|
condition = data.aws_ebs_volume.example.encrypted
|
||||||
|
error_message = "The server's root volume is not encrypted."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## Multi-cloud Abstractions
|
## Multi-cloud Abstractions
|
||||||
|
|
||||||
Terraform itself intentionally does not attempt to abstract over similar
|
Terraform itself intentionally does not attempt to abstract over similar
|
||||||
|
@ -131,6 +131,30 @@ The following meta-arguments are documented on separate pages:
|
|||||||
- [`lifecycle`, for lifecycle customizations](/language/meta-arguments/lifecycle)
|
- [`lifecycle`, for lifecycle customizations](/language/meta-arguments/lifecycle)
|
||||||
- [`provisioner`, for taking extra actions after resource creation](/language/resources/provisioners/syntax)
|
- [`provisioner`, for taking extra actions after resource creation](/language/resources/provisioners/syntax)
|
||||||
|
|
||||||
|
## Custom Condition Checks
|
||||||
|
|
||||||
|
You can use `precondition` and `postcondition` blocks to specify assumptions and guarantees about how the resource operates. The following examples creates a precondition that checks whether the AMI is properly configured.
|
||||||
|
|
||||||
|
```hcl
|
||||||
|
resource "aws_instance" "example" {
|
||||||
|
instance_type = "t2.micro"
|
||||||
|
ami = "ami-abc123"
|
||||||
|
|
||||||
|
lifecycle {
|
||||||
|
# The AMI ID must refer to an AMI that contains an operating system
|
||||||
|
# for the `x86_64` architecture.
|
||||||
|
precondition {
|
||||||
|
condition = data.aws_ami.example.architecture == "x86_64"
|
||||||
|
error_message = "The selected AMI must be for the x86_64 architecture."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Custom conditions can help 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.
|
||||||
|
|
||||||
|
Refer to [Custom Condition Checks](/language/expressions/custom-conditions#preconditions-and-postconditions) for more details.
|
||||||
|
|
||||||
## Operation Timeouts
|
## Operation Timeouts
|
||||||
|
|
||||||
Some resource types provide a special `timeouts` nested block argument that
|
Some resource types provide a special `timeouts` nested block argument that
|
||||||
|
@ -62,6 +62,27 @@ In a parent module, outputs of child modules are available in expressions as
|
|||||||
`web_server` declared an output named `instance_ip_addr`, you could access that
|
`web_server` declared an output named `instance_ip_addr`, you could access that
|
||||||
value as `module.web_server.instance_ip_addr`.
|
value as `module.web_server.instance_ip_addr`.
|
||||||
|
|
||||||
|
|
||||||
|
## Custom Condition Checks
|
||||||
|
|
||||||
|
You can use `precondition` blocks to specify guarantees about output data. The following examples creates a precondition that checks whether the EC2 instance has an encrypted root volume.
|
||||||
|
|
||||||
|
```hcl
|
||||||
|
output "api_base_url" {
|
||||||
|
value = "https://${aws_instance.example.private_dns}:8433/"
|
||||||
|
|
||||||
|
# The EC2 instance must have an encrypted root volume.
|
||||||
|
precondition {
|
||||||
|
condition = data.aws_ebs_volume.example.encrypted
|
||||||
|
error_message = "The server's root volume is not encrypted."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Custom conditions can help 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.
|
||||||
|
|
||||||
|
Refer to [Custom Condition Checks](/language/expressions/custom-conditions#preconditions-and-postconditions) for more details.
|
||||||
|
|
||||||
## Optional Arguments
|
## Optional Arguments
|
||||||
|
|
||||||
`output` blocks can optionally include `description`, `sensitive`, and `depends_on` arguments, which are described in the following sections.
|
`output` blocks can optionally include `description`, `sensitive`, and `depends_on` arguments, which are described in the following sections.
|
||||||
|
@ -160,9 +160,7 @@ commentary for module maintainers, use comments.
|
|||||||
|
|
||||||
-> This feature was introduced in Terraform CLI v0.13.0.
|
-> This feature was introduced in Terraform CLI v0.13.0.
|
||||||
|
|
||||||
In addition to Type Constraints as described above, a module author can specify
|
You can specify custom validation rules for a particular variable by adding a `validation` block within the corresponding `variable` block. The example below checks whether the AMI ID has the correct syntax.
|
||||||
arbitrary custom validation rules for a particular variable using a `validation`
|
|
||||||
block nested within the corresponding `variable` block:
|
|
||||||
|
|
||||||
```hcl
|
```hcl
|
||||||
variable "image_id" {
|
variable "image_id" {
|
||||||
@ -175,38 +173,7 @@ variable "image_id" {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
Refer to [Custom Condition Checks](/language/expressions/custom-conditions#input-variable-validation) for more details.
|
||||||
The `condition` argument is 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 variable that the condition applies to,
|
|
||||||
and _must not_ produce errors.
|
|
||||||
|
|
||||||
If the failure of an expression is the basis of the validation decision, use
|
|
||||||
[the `can` function](/language/functions/can) to detect such errors. For example:
|
|
||||||
|
|
||||||
```hcl
|
|
||||||
variable "image_id" {
|
|
||||||
type = string
|
|
||||||
description = "The id of the machine image (AMI) to use for the server."
|
|
||||||
|
|
||||||
validation {
|
|
||||||
# regex(...) fails if it cannot find a match
|
|
||||||
condition = can(regex("^ami-", var.image_id))
|
|
||||||
error_message = "The image_id value must be a valid AMI id, starting with \"ami-\"."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
If `condition` evaluates to `false`, Terraform will produce an error message
|
|
||||||
that includes the result of the `error_message` expression. The error message
|
|
||||||
should be at least one full sentence explaining the constraint that failed,
|
|
||||||
using a sentence structure similar to the above examples.
|
|
||||||
|
|
||||||
Error messages can be literal strings, heredocs, or template expressions. The
|
|
||||||
only valid reference in an error message is the variable under validation.
|
|
||||||
|
|
||||||
Multiple `validation` blocks can be declared in which case error messages
|
|
||||||
will be returned for _all_ failed conditions.
|
|
||||||
|
|
||||||
### Suppressing Values in CLI Output
|
### Suppressing Values in CLI Output
|
||||||
|
|
||||||
@ -319,7 +286,7 @@ may assign the value `null` to the variable.
|
|||||||
```
|
```
|
||||||
variable "example" {
|
variable "example" {
|
||||||
type = string
|
type = string
|
||||||
nullable = false
|
nullable = false
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user