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 validations 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]
    }