Terraform
Checks
Note: Check blocks are only available in Terraform v1.5.0 and later.
The check
block can validate your infrastructure outside the usual resource lifecycle. Check blocks address a gap between post-apply and functional validation of infrastructure.
Hands-on: Try the Validate Infrastructure Using Checks tutorial.
Check blocks allow you to define custom conditions that execute on every Terraform plan or apply operation without affecting the overall status of an operation. Check blocks execute as the last step of a plan or apply after Terraform has planned or provisioned your infrastructure.
Syntax
You can declare a check
block with a local name, zero-to-one scoped data sources, and one-to-many assertions.
The following example loads the Terraform website and validates that it returns the expected status code 200
.
check "health_check" {
data "http" "terraform_io" {
url = "https://www.terraform.io"
}
assert {
condition = data.http.terraform_io.status_code == 200
error_message = "${data.http.terraform_io.url} returned an unhealthy status code"
}
}
Scoped data sources
You can use any data source from any provider as a scoped data source within a check
block.
A check
block can optionally contain a nested (a.k.a. scoped) data source. This data
block behaves like an external data source, except you can not reference it outside its enclosing check
block. Additionally, if a scoped data source's provider raises any errors, they are masked as warnings and do not prevent Terraform from continuing operation execution.
You can use a scoped data source to validate the status of a piece of infrastructure outside of the usual Terraform resource lifecycle. In the above example, if the terraform_io
data source fails to load, you receive a warning instead of a blocking error, which would occur if you declared this data source outside of a check
block.
Meta-Arguments
Scoped data sources support the depends_on
and provider
meta-arguments. Scoped data sources do not support the count
orfor_each
meta-arguments.
depends_on
The depends_on
meta-argument can be particularly powerful when used within scoped data sources.
The first time Terraform creates the initial plan for our previous example, the plan fails because Terraform has not applied its configuration yet. Meaning this test fails because Terraform must still create the resources to make this website exist. Therefore, the first time Terraform runs this check, it always throws a potentially distracting error message.
You can fix this by adding depends_on
to your scoped data source, ensuring it depends on an essential piece of your site's infrastructure, such as the load balancer. The check returns known after apply
until that crucial piece of your website is ready. This strategy avoids producing unnecessary warnings during setup, and the check executes during subsequent plans and applies.
One problem with this strategy is that if the resource your scoped data source depends_on
changes, the check block returns known after apply
until Terraform has updated that resource. Depending on your use case, this behavior could be acceptable or problematic.
We recommend implementing the depends_on
meta-argument if your scoped data source depends on the existence of another resource without referencing it directly.
Assertions
Check blocks validate your custom assertions using assert
blocks. Each check
block must have at least one, but potentially many, assert
blocks. Each assert
block has a condition
attribute and an error_message
attribute.
Unlike other custom conditions, assertions do not affect Terraform's execution of an operation. A failed assertion reports a warning without halting the ongoing operation. This contrasts with other custom conditions, such as a postcondition, where Terraform produces an error immediately, halting the operation and blocking the application or planning of future resources.
Condition arguments within assert
blocks can refer to scoped data sources within the enclosing check
block and any variables, resources, data sources, or module outputs within the current module.
Meta-Arguments
Check blocks do not currently support meta-arguments. We are still collecting feedback on this feature, so if your use case would benefit from check blocks supporting meta-arguments, please let us know.
Continuous validation in Terraform Cloud
Terraform Cloud can automatically validate whether checks in a workspace’s configuration continue to pass after Terraform provisions new infrastructure. See Continuous Validation for details.
Choosing Checks or other Custom Conditions
Check blocks offer the most flexible validation solution within Terraform. You can reference outputs, variables, resources, and data sources within check assertions. You can also use checks to model every alternate Custom Condition. However, that does not mean you should replace all your custom conditions with check blocks.
There are major behavioral differences between check block assertions and other custom conditions, the main one being that check blocks do not affect Terraform's execution of an operation. You can use this non-blocking behavior to decide the best type of validation for your use case.
Outputs and variables
Output postconditions and variable validations both make assertions around inputs and outputs.
This is one of the cases where you might want Terraform to block further execution.
For example, it is not helpful for Terraform to warn that an input variable is invalid after it applies an entire configuration with that input variable. In this case, a check block would warn of the invalid input variable without interrupting the operation. A validation block for the same input variable would alert you of the invalid variable and halt the plan or apply operation.
Resource Preconditions and Postconditions
The difference between preconditions and postconditions and check blocks is more nuanced.
Preconditions are unique amongst the custom conditions in that they execute before a resource change is applied or planned. Choosing Between Preconditions and Postconditions offers guidance on choosing between a precondition and a postcondition, and the same topics also apply to choosing between a precondition and a check block.
You can often use postconditions interchangeably with check blocks to validate resources and data sources.
For example, you can rewrite the above check
block example to use a postcondition instead. The below code uses a postcondition
block to validate that the Terraform website returns the expected status code of 200
.
data "http" "terraform_io" {
url = "https://www.terraform.io"
lifecycle {
postcondition {
condition = self.status_code == 200
error_message = "${self.url} returned an unhealthy status code"
}
}
}
Both the check
and postcondition
block examples validate that the Terraform website returns a 200
status code during a plan or an apply operation. The difference between the two blocks is how each handles failure.
If a postcondition
block fails, it blocks Terraform from executing the current operation. If a check
block fails, it does not block Terraform from executing an operation.
If the above example's postcondition fails, it is impossible to recover from. Terraform blocks any future plan or apply operations if your postcondition is unsatisfied during the planning stage. This problem occurs because the postcondition does not directly depend on Terraform configuration, but instead on the complex interactions between multiple resources.
We recommend using check blocks to validate the status of infrastructure as a whole. We only recommend using postconditions when you want a guarantee on a single resource based on that resource's configuration.