Introducing terraform input validation
A few years ago CloudFormation was a large part of my day. While Terraform slowly began to creep into my stacks, with its daring support for other providers, one of the features I always missed was an equivalent to CloudFormations AWS specific parameter types. These provided a great way to ensure you were using the type of value you thought you were, enforcing that something was actually a subnet ID for example, and now with Terraform 0.13 you can finally reproduce some of that functionality.
The addition of Terraforms validation
blocks enables you to check the
values provided to a variable
and ensure they match a pattern or pass
a check. This provides a simple but powerful way to ensure your supplied
data fits its intended usage. No more vpc_ids being using in place of
subnet_ids. Early failure and exit on receiving invalid YAML! There is a lot of
flexibility here. To continue the example above, validating a vpc_id, we only need to add the block, condition and error_message to our variable declaration.
## cat main.tf
variable "vpc_id" {
type = string
description = "VPC ID to deploy to"
validation {
# alternative regex approach
# condition = can(regex("^vpc-", var.vpc_id))
condition = length(var.vpc_id) > 4 && substr(var.vpc_id, 0, 4) == "vpc-"
error_message = "The vpc_id must be a valid VPC ID of the form 'vpc-'."
}
}
With a valid vpc_id we see no issues when running a plan:
./terraform-0.13-beta plan -var vpc_id=vpc-1234f3d34f
No changes. Infrastructure is up-to-date.
But when we try to pass an invalid value we’re stopped in our tracks. Before we get halfway through a build and have the API reject us.
./terraform-0.13-beta plan -var vpc_id=foo
Error: Invalid value for variable
on main.tf line 1:
1: variable "vpc_id" {
The vpc_id must be a valid VPC ID of the form 'vpc-'.
This was checked by the validation rule at main.tf:6,3-13.
The current version of terraforms custom validation rules are not an exact alternative to AWS specific parameter types. Terraforms validation is based on checking the values themselves. Initially you’ll mostly be checking ‘do they match a pattern and look like I expect?’ The AWS types actually check that a resource of that name exists in the account, not just that the string is formatted correctly. This provides a run time layer of validation that Terraform cannot currently match.
variable_validation
is not yet enabled by default in a non-beta
version of terraform (it will begin with 0.13) so there isn’t yet a huge
amount of code (188 hits on GitHub search) written using it. When used
to validate AWS resources there are two main approaches being taken,
exact string matching and regular expressions.
# explicit string matching
condition = length(var.vpc_id) > 4 && substr(var.vpc_id, 0, 4) == "vpc-"
# regular expression equivalent
condition = can(regex("^vpc-", var.vpc_id))
I’m personally more a fan of the regex approach as it’s less syntax heavy (not something you can often say about the regex option) and will adopt that style. Beyond basic string matching I found some other useful validation examples on GitHub.
# is something valid yaml?
condition = var.custom_vars != "" ? can(yamldecode(var.custom_vars)) : true
# does the value comply with an allowed list
condition = contains(["Dev", "Test", "Prod"], var.environment)
Writing your own validation
s has a niggle in that error_message
is
picky about the format it accepts:
Validation error message must be at least one full English sentence starting
with an uppercase letter and ending with a period or question mark.
If this all seems interesting but you’re still using terraform 0.12 you
can opt in to this feature by adding the experiment
keyword.
terraform {
experiments = [variable_validation]
}