Terraform
Use checks to validate infrastructure
Terraform checks let you define assertions to validate as part of your infrastructure management workflow. Unlike variable validation or custom conditions, check
blocks are decoupled from the lifecycle of a specific resource or data source. Checks let you take advantage of Terraform's abstraction of the differences between different provider APIs. Because Terraform standardizes how you interact with all provider APIs, you can use the familiar Terraform language features and syntax to define conditions to validate in your Terraform runs. Checks also let you verify your assumptions about your infrastructure on an ongoing basis instead of just at the time of provisioning.
In this tutorial, you will define and use checks to check the response code for a service and validate that your TLS certificate is still valid. You will define conditions that reference existing resources and integrate data sources to query your systems. If you complete this tutorial using HCP Terraform, you will also enable health assessments and use continuous validation to verify the results of your checks.
Tip
Health assessments are available in HCP Terraform Plus Edition. Refer to HCP Terraform pricing for details.
Prerequisites
You can complete this tutorial using the same workflow with either Terraform Community Edition or HCP Terraform. HCP Terraform is a platform that you can use to manage and execute your Terraform projects. It includes features like remote state and execution, structured plan output, workspace resource summaries, and more.
Select the HCP Terraform tab to complete this tutorial using HCP Terraform.
This tutorial assumes that you are familiar with the Terraform and HCP Terraform workflows. If you are new to Terraform, complete the Get Started tutorials first. If you are new to HCP Terraform, complete the HCP Terraform Get Started tutorials first.
In order to complete this tutorial, you will need the following:
- Terraform v1.5+ installed locally.
- An AWS account.
- An HCP Terraform account with HCP Terraform locally authenticated.
- An HCP Terraform organization with the Plus edition.
- An HCP Terraform variable set configured with your AWS credentials.
Clone example configuration
Clone the example repository for this tutorial. It contains Terraform configuration for a load balancer, self-signed TLS certificate, target group, security group, and an autoscaling group that runs Teramino, a Terraform-skinned Tetris application.
$ git clone https://github.com/hashicorp-education/learn-terraform-checks
Change to the repository directory.
$ cd learn-terraform-checks
Define a check
Checks can validate any condition that you can define with Terraform configuration. A check can validate an attribute of your infrastructure, or the functionality of the resource itself. Rather than writing custom scripts to test assertions about your infrastructure, you can use Terraform language features to validate your infrastructure health.
Open your main.tf
file and add a check block that verifies the status of your TLS certificate to the file. Save the file.
main.tf
check "certificate" {
assert {
condition = aws_acm_certificate.cert.status == "ERRORED"
error_message = "Certificate status is ${aws_acm_certificate.cert.status}"
}
}
A check
block consists of one or more assert
statements. The assertions contain the condition to verify and the error message to display if the assertion fails. This check verifies the TLS certificate's status. To demonstrate how Terraform handles a failed check, the condition asserts that the status is ERRORED
, which will fail.
Create infrastructure and validate checks
Open your terraform.tf
file and uncomment the cloud
block. Replace the
organization
name with your own HCP Terraform organization.
terraform.tf
terraform {
cloud {
organization = "organization-name"
workspaces {
name = "learn-terraform-checks"
}
}
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 4.4.0"
}
}
required_version = ">= 1.5"
}
Initialize your configuration. Terraform will automatically create the learn-terraform-checks
workspace in your HCP Terraform organization.
$ terraform init
Initializing HCP Terraform...
Initializing provider plugins...
- Reusing previous version of hashicorp/aws from the dependency lock file
- Installing hashicorp/aws v4.4.0...
- Installed hashicorp/aws v4.4.0 (signed by HashiCorp)
HCP Terraform has been successfully initialized!
You may now begin working with HCP Terraform. Try running "terraform plan" to
see any changes that are required for your infrastructure.
If you ever set or change modules or Terraform Settings, run "terraform init"
again to reinitialize your working directory.
Note: This tutorial assumes that you are using a tutorial-specific HCP Terraform organization with a global variable set of your AWS credentials. Review the Create a Credential Variable Set for detailed guidance. If you are using a scoped variable set, assign it to your new workspace now.
Now, create your infrastructure. Terraform will evaluate any checks included in your configuration as the last step of the operation, and the configuration will fail the check because your certificate status is actually ISSUED
.
$ terraform apply
data.aws_ami.amazon-linux: Reading...
data.aws_availability_zones.available: Reading...
data.aws_ami.amazon-linux: Read complete after 0s [id=ami-01974536802026e4f]
data.aws_availability_zones.available: Read complete after 1s [id=us-east-2]
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
##...
Plan: 21 to add, 0 to change, 0 to destroy.
Changes to Outputs:
+ application_endpoint = (known after apply)
+ asg_name = "terramino"
+ lb_endpoint = (known after apply)
╷
│ Warning: Check block assertion known after apply
│
│ on main.tf line 169, in check "certificate":
│ 169: condition = aws_acm_certificate.cert.status == "ERRORED"
│ ├────────────────
│ │ aws_acm_certificate.cert.status is a string
│
│ The condition could not be evaluated at this time, a result will be known when this plan is applied.
╵
Do you want to perform these actions?
Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.
Enter a value: yes
##...
╷
│ Warning: Check block assertion failed
│
│ on main.tf line 169, in check "certificate":
│ 169: condition = aws_acm_certificate.cert.status == "ERRORED"
│ ├────────────────
│ │ aws_acm_certificate.cert.status is "ISSUED"
│
│ Certificate status is ISSUED
╵
Apply complete! Resources: 21 added, 0 changed, 0 destroyed.
Outputs:
application_endpoint = "https://terramino-1659500809.us-east-2.elb.amazonaws.com/index.php"
lb_endpoint = "https://terramino-1659500809.us-east-2.elb.amazonaws.com"
Notice that after Terraform generated the execution plan for the run, it displayed the check, but noted that the result would only be known after the apply. At the end of the apply, Terraform determined the certificate status and showed that the check failed.
Open main.tf
again. Find the aws_launch_configuration.terramino
resource and update the instance_type
to t2.small
. This time, Terraform will know at the time of the plan that your configuration's check fails, but will still let you proceed with the instance resize.
main.tf
resource "aws_launch_configuration" "terramino" {
name_prefix = "terramino-"
image_id = data.aws_ami.amazon-linux.id
instance_type = "t2.small"
user_data = file("user-data.sh")
security_groups = [aws_security_group.terramino_instance.id]
lifecycle {
create_before_destroy = true
}
}
Run terraform apply
. This time, Terraform evaluates the check and notifies that it failed, but still lets you change your infrastructure.
$ terraform apply
##...
Plan: 1 to add, 1 to change, 1 to destroy.
╷
│ Warning: Check block assertion failed
│
│ on main.tf line 169, in check "certificate":
│ 169: condition = aws_acm_certificate.cert.status == "ERRORED"
│ ├────────────────
│ │ aws_acm_certificate.cert.status is "ISSUED"
│
│ Certificate status is ISSUED
╵
Do you want to perform these actions?
Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.
Enter a value: yes
Terraform evaluates checks after generating the plan. Unlike custom conditions or variable validation errors, failed checks do not block applies. Terraform will notify if there are any failures or issues to address, letting you decide whether to proceed with the operation.
Unlike other configuration validation mechanisms, checks are decoupled from other components and resources in your configuration. These differ from variable validation, which lets you ensure the inputs to your configuration satisfy your requirements, and custom conditions, which let you define conditions as part of your resource definitions. These conditions are tied to the specific resource lifecycle, rather than your configuration as a whole.
Enable health assessments
HCP Terraform health assessments run daily to verify the status of your infrastructure. These assessments include drift detection, which verifies that your infrastructure settings still match your intended configuration, and continuous validation, which verifies that any checks defined in your configuration still pass.
You can enable assessments on a specific workspace, or on all workspaces within your organization. Enable assessments on the workspace for this tutorial. First, navigate to your learn-terraform-checks
workspace. In the Health section, select Settings. Then, select Enable and click Save settings.
Health assessments run every 12 hours once enabled for a workspace, but you can also manually trigger an assessment to confirm the status of your workspace. Navigate to your workspace's Health page, then click Start health assessment.
After a few moments, HCP Terraform displays the failed check result.
You can configure workspace notifications to alert your team with assessment results. Automated health assessments proactively identify issues in your infrastructure configuration, rather than having to wait for a manual operation to validate your checks. By resolving issues promptly, you restore predictability in your infrastructure management workflow, since your team does not have to decide how to reconcile issues as part of other operations. Automated assessments help you identify and remediate issues early, avoiding adding operational complexity to Terraform runs.
Use a data source within a check
You can reference data sources in check block assertions. Terraform queries the data source when it evaluates your configuration's checks, at the end of each Terraform operation. This lets you access the most up-to-date data about your environment when a workspace manages many resources and takes longer to complete Terraform operations.
Add the following check to main.tf
.
main.tf
check "response" {
data "http" "terramino" {
url = "https://${aws_lb.terramino.dns_name}"
insecure = true
}
assert {
condition = data.http.terramino.status_code == 200
error_message = "Terramino response is ${data.http.terramino.status_code}"
}
}
This check defines a data source that captures the response of a GET request to your Terramino service and asserts that the status code is 200
.
You can reference any data sources or resource attributes in your configuration to define check conditions, but you cannot access data sources defined within check blocks in the rest of your configuration. The data source namespace is scoped within the check block, and evaluates at the time of the check. If needed, you can specify the depends_on
meta-argument for a data source within the check to enforce an evaluation order.
Next, update the condition on your original check so that it verifies that the certificate status is ISSUED
and your check passes.
main.tf
check "certificate" {
assert {
condition = aws_acm_certificate.cert.status == "ISSUED"
error_message = "Certificate status is ${aws_acm_certificate.cert.status}"
}
}
Run terraform init
to download the http
provider.
$ terraform init
##...
Initializing provider plugins...
- Reusing previous version of hashicorp/aws from the dependency lock file
- Reusing previous version of hashicorp/tls from the dependency lock file
- Finding latest version of hashicorp/http...
- Using previously-installed hashicorp/aws v4.4.0
- Using previously-installed hashicorp/tls v4.0.4
- Installing hashicorp/http v3.3.0...
- Installed hashicorp/http v3.3.0 (signed by HashiCorp)
Terraform has made some changes to the provider dependency selections recorded
in the .terraform.lock.hcl file. Review those changes and commit them to your
version control system if they represent changes you intended to make.
HCP Terraform has been successfully initialized!
You may now begin working with HCP Terraform. Try running "terraform plan" to
see any changes that are required for your infrastructure.
If you ever set or change modules or Terraform Settings, run "terraform init"
again to reinitialize your working directory.
Run terraform apply
. Terraform will query the data source and evaluate your new check.
$ terraform apply
##...
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the
following symbols:
<= read (data resources)
Terraform will perform the following actions:
# data.http.terramino will be read during apply
# (config will be reloaded to verify a check block)
<= data "http" "terramino" {
+ body = <<-EOT
Hello World
EOT
+ id = "https://terramino-1659500809.us-east-2.elb.amazonaws.com"
+ insecure = true
+ response_body = <<-EOT
Hello World
EOT
+ response_headers = {
+ "Accept-Ranges" = "bytes"
+ "Content-Length" = "12"
+ "Content-Type" = "text/html; charset=UTF-8"
+ "Date" = "Thu, 01 Jun 2023 10:59:54 GMT"
+ "Etag" = "\"c-5fd0eb17984b7\""
+ "Last-Modified" = "Thu, 01 Jun 2023 10:13:52 GMT"
+ "Server" = "Apache/2.4.56 (Amazon) PHP/7.2.34"
}
+ status_code = 200
+ url = "https://terramino-1659500809.us-east-2.elb.amazonaws.com"
}
Plan: 0 to add, 0 to change, 0 to destroy.
Do you want to perform these actions?
Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.
Enter a value: yes
##...
data.http.terramino: Reading...
data.http.terramino: Read complete after 1s [id=https://terramino-1659500809.us-east-2.elb.amazonaws.com]
Apply complete! Resources: 0 added, 0 changed, 0 destroyed.
Outputs:
application_endpoint = "https://terramino-1659500809.us-east-2.elb.amazonaws.com/index.php"
lb_endpoint = "https://terramino-1659500809.us-east-2.elb.amazonaws.com"
As expected, both of your checks passed. If the checks in your configuration pass, Terraform will record it in your state file, but will not display output for passing checks as part of your run.
Navigate to your learn-terraform-checks
workspace in HCP Terraform, then go to Health > Continuous validation.
As part of the last run, HCP Terraform detected your new check and that both of your checks now pass.
Then, under your workspace's States, open the latest state version and find the check_results
field. Terraform recorded that both of your checks passed in the state file.
terraform.tfstate
"check_results": [
{
"object_kind": "check",
"config_addr": "check.certificate",
"status": "pass",
"objects": [
{
"object_addr": "check.certificate",
"status": "pass"
}
]
},
{
"object_kind": "check",
"config_addr": "check.response",
"status": "pass",
"objects": [
{
"object_addr": "check.response",
"status": "pass"
}
]
}
]
Clean up infrastructure
Destroy the resources you created to avoid incurring additional costs.
$ terraform destroy
##...
Plan: 0 to add, 0 to change, 21 to destroy.
Changes to Outputs:
- application_endpoint = "https://terramino-1659500809.us-east-2.elb.amazonaws.com/index.php" -> null
- lb_endpoint = "https://terramino-1659500809.us-east-2.elb.amazonaws.com" -> null
Do you really want to destroy all resources?
Terraform will destroy all your managed infrastructure, as shown above.
There is no undo. Only 'yes' will be accepted to confirm.
Enter a value: yes
##...
Destroy complete! Resources: 21 destroyed.
If you used HCP Terraform for this tutorial, after destroying your resources, delete the learn-terraform-checks
workspace from your HCP Terraform organization.
Next steps
In this tutorial, you used check blocks to define assertions for Terraform to evaluate as part of your infrastructure management operations. You learned about referencing data sources within check blocks, their limited scoping, and the difference between check blocks and other configuration validation mechanisms.
Checks and configuration-level validation such as custom conditions and variable validation create predictability in your infrastructure operations by helping you verify your assumptions.
Review the following resources to learn more about how to ensure conformity in our infrastructure using Terraform and HCP Terraform.
- Review the checks documentation
- Review check examples for the AWS, Azure, and GCP providers
- Learn how to use custom conditions to validate module inputs and usage
- Learn how to write Sentinel policies to evaluate your infrastructure changes
- Configure OPA policies in HCP Terraform to ensure compliances at an organization level