Validate modules with custom conditions
Terraform lets you define custom conditions in your module configuration to validate resources, data sources, and outputs. When planning and applying changes to your infrastructure, Terraform evaluates these condition blocks and reports an error if a condition fails. Terraform supports preconditions, which it evaluates before it provisions the enclosing block, and postconditions, which it evaluates afterward.
In this tutorial, you will provision resources using a local module that represents an application deployment, including a load balancer and EC2 instances. While this module includes variable validation, it is still possible for users of the module to misconfigure the application. You will add condition blocks to the module to ensure that users configure the VPC and EC2 instances correctly.
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 standard Terraform workflow. If you are new to Terraform, complete the Get Started tutorials first.
For this tutorial, you will need:
- Terraform 1.2+ installed locally.
- An AWS account with credentials configured for Terraform.
Note
Some of the infrastructure in this tutorial does not qualify for the AWS free tier. Destroy the infrastructure at the end of the guide to avoid unnecessary charges. We are not responsible for any charges that you incur.
Clone the example repository
Clone the example repository for this tutorial, which contains Terraform configuration that uses a local module to deploy an application hosted on AWS.
Change into the repository directory.
Review example configuration
The example configuration defines a VPC to host your application, selects an AWS AMI using a data source, and uses a local module to deploy EC2 instances and a load balancer into the VPC.
Open main.tf to review the initial configuration. The module.app
block in
main.tf
configures the example-app-deployment
module with several arguments.
The module configuration in modules/example-app-deployment/main.tf
defines the
infrastructure that will host your example application, which consists of a load
balancer, security groups, and EC2 instances. This local module uses public
modules from the Terraform registry to provision your security groups and load
balancer.
The EC2 instance configuration references input variables passed from the root module to set the number of instances, the instance type, AMI ID, and private subnets to provision the instances in.
The variables.tf
file defines several input variables for your module,
including the VPC ID and subnets to deploy the application in.
Plan changes
Terraform configuration can be syntactically valid and deployable, but still not satisfy other constraints such as application-specific requirements. When you maintain a module, you can use custom conditions in your configuration to enforce these requirements.
In the root module, rename the terraform.tfvars.example
file to
terraform.tfvars
, so that Terraform will detect the file with end-user-configured variables in it.
This file sets values for three of the variables used by the example configuration. Terraform can deploy your infrastructure with these values, but they do not meet the hypothetical requirements of the example application, which needs an EC2 instance that supports EBS optimization, and a VPC that has DNS support enabled.
If you were deploying a real application with these requirements, the application would fail on the configured infrastructure with little indication of what might be wrong. A developer familiar with the application requirements would have to diagnose the issues once the application was already deployed, and would have trace the cause to these misconfigured variables.
In this tutorial, you will add conditions to the module to ensure that:
- Each private subnet has the same number of EC2 instances.
- The EC2 instance type supports EBS optimization.
- The VPC has DNS support enabled.
By adding these conditions, you will ensure that users cannot deploy the application on infrastructure that does not meet the application's requirements.
Initialize the configuration.
Before you add conditions to the example module, execute a plan to review the resources that Terraform will deploy.
The plan reports that Terraform is ready to apply your configuration. Before you do so, add conditions to your module to ensure that your users configure the application correctly.
Add preconditions
Terraform allows you to add preconditions and postconditions to the lifecycle of resource, data source, or output blocks. Terraform evaluates preconditions before the enclosing block, validating that your configuration is compliant before it applies it. Terraform evaluates post conditions after the enclosing block, letting you confirm that the results of applied changes are compliant before it applies the rest of your configuration.
Update modules/example-app-deployment/main.tf
to include a data source that looks up the instance type. Add two preconditions to the the aws_instance.app
block for
your EC2 instances to check the number of instances per subnet, and instance type.
The first precondition verifies that each private subnet contains the same
number of instances. It does so by dividing the number of instances by the
number of subnets, and checking that the remainder is 0
. This condition
ensures that application traffic is spread evenly across the subnets used by
your application.
The second precondition verifies that the chosen EC2 instance type supports EBS
optimization. In order to do so, it accesses the instance type's
ebs_optimized_support
attribute from the data source.
Trigger a condition failure
Attempt to plan this configuration, and Terraform will report that the preconditions failed.
Note
The configuration uses the count
meta-argument to create a number
of EC2 instances equal to the value of the aws_instance_count
variable,
currently set to 3
. Terraform reports errors for both preconditions for each
instance.
Terraform reports errors whenever a condition fails, and will not continue to plan or apply your configuration. You must resolve the errors before you can successfully deploy this configuration.
Plan configuration with correct values
Earlier in this tutorial, you set the number of instances and the instance type
in terraform.tfvars
. Update these to values that are compatible with the
conditions you added to the example module.
First, update the instance type to one that supports EBS optimization
(t3.micro
). Second, update the aws_instance_count
variable to deploy four
instances, so that the number of instances is evenly divisible by the number of
private subnets.
Plan this configuration again, and verify that it satisfies the preconditions.
Both of the preconditions checks now pass, and Terraform is ready to apply your configuration. Before you do so, add a postcondition to the example module.
Add a postcondition
The root configuration in this project creates a VPC using a public module. The
example-app-deployment
module expects DNS support to be enabled on the VPC.
Add a data source to the application module that looks up the created VPC by its ID and uses a
postcondition to verify that DNS support is enabled.
Add the following data source to modules/example-app-deployment/main.tf
.
The postcondition refers to the data source using the self
value. Terraform
will not create the VPC until you apply the example configuration, so it cannot
validate this condition until after it has begun provisioning your
infrastructure. When you run terraform apply
, Terraform will start applying
the configuration, and will create the VPC before it reads its attributes from
the data source. After it does so, it will evaluate the postcondition and report
an error if it fails.
Apply configuration
Apply your configuration now. Respond to the confirmation prompt with a yes
,
and Terraform will begin applying your changes, and then report an error when
the postcondition on your data source fails.
Resolve this error by enabling DNS support on your VPC. Update the value for the
enable_dns
variable in terraform.tfvars
.
Apply the configuration again. Respond to the confirmation prompt with a yes
.
After updating your VPC, Terraform read the new value for enable_dns_support
from the aws_vpc.app
data source inside the example-app-deployment
module
and evaluated the postcondition. Since DNS support is now enabled, the
postcondition succeeded.
Destroy infrastructure
Destroy the infrastructure you created in this tutorial. Respond to the
confirmation prompt with a yes
.
If you used HCP Terraform for this tutorial, after destroying your resources,
delete the learn-terraform-conditions
workspace from your HCP Terraform
organization.
Next steps
In this tutorial you learned about the behavior and benefits of preconditions and postconditions. Conditions allow module authors to write configuration that is easier for other people to use successfully, by validating multiple conditions either before or after resource provisioning.
For more information on topics covered in this tutorial, check out the following resources:
Read the Terraform custom conditions documentation.
Follow the Customize Terraform Configuration with Variables tutorial to learn how to create Terraform variables and how to validate the values of individual variables.
Complete the Reuse Configuration with Modules to learn how to create and publish custom Terraform modules.
Learn how to configure run tasks in HCP Terraform.