Input Validation - Terraform Tips & Tricks

coleheard

Cole Heard

Posted on March 10, 2024

Input Validation - Terraform Tips & Tricks

Terraform modules are a great way to follow D.R.Y. development principles. I’ve written about D.R.Y. in the past, but to sum it up briefly:

The DRY (“Don't Repeat Yourself”) principle follows the idea of every logic duplication being eliminated by abstraction. This means that during the development process we should avoid writing repetitive duplicated code as much as possible.

Writing a module for use by others on your team or the community at large can present some challenges.

If someone isn't familiar with my code - they may input a value that I did not account for.

Incorrect input can cause deployment failure or, even more frightening, deploy an incorrect configuration without being immediately apparent.

Ralph Wiggum - Danger

In this post, I’ll be covering some of the methods I’ve used to enforce input standards.

Table of Contents


Structured Types

You'll see the most basic form of type enforcement everywhere you look.

The variable below enforces the input standards with the type argument.

This variable will only accept a bool, true or false.



variable "validation_environment" {
  type        = bool
  description = "Set as true to enable validation environment."
  default     = false
}


Enter fullscreen mode Exit fullscreen mode

The input must be a number.



variable "maximum_sessions_allowed" {
  type        = number
  description = "The maximum number of concurrent sessions per host."
  default     = 3
}


Enter fullscreen mode Exit fullscreen mode

You can nest and combine these types using basic Terraform syntax. In the example below, Terraform must receive a list. Even if the list only contains a single string, it is a list of one string.



variable "included_location_ids" {
  type        = list(string)
  description = "A list of Named Location IDs that will this policy will apply against."
  default     = ["All"]
}


Enter fullscreen mode Exit fullscreen mode

In the next example, the type is defined as a map of objects. We've added a few other conditions as well - Each object must have 4 key-value pairs (KPV). Each key must match the defined name, "app_name" or "local_path". Each KPV value is also type defined: string, number, bool, etc.



variable "application_map" {
  type = map(object({
    app_name     = string
    local_path   = string
    aad_group    = string
    cmd_argument = string
  }))
  description = "A map of all applications and their metadata."
  default     = null
}


Enter fullscreen mode Exit fullscreen mode

Lets reconsider enforcing the cmd_argument KPV from the object.



variable "application_map" {
  type = map(object({
    app_name     = string
    local_path   = string
    aad_group    = string
  }))
  description = "A map of all applications and their metadata."
  default     = null
}


Enter fullscreen mode Exit fullscreen mode

This change did remove the enforcement of cmd_argument, but it also barred its presence completely.



variable "application_map" {
  type = map(object({
    app_name     = string
    local_path   = string
    aad_group    = string
    cmd_argument = optional(string)
  }))
  description = "A map of all applications and their metadata."
  default     = null
}


Enter fullscreen mode Exit fullscreen mode

The optional modifier allows cmd_argument to be included within the object, but it won't reject an object without it.


Validation Block

The condition argument succeeds if it evaluates to true. If the statement evaluates to false, Terraform cancels the operation and outputs the string value of the error_message argument.



variable "stars" {
  type        = number
  description = "Please rate your sanctification with my module. Select the number of stars, 1 through 5."
  default     = 5
  validation {
    condition = (
      var.stars >= 1 &&
      var.stars <= 5
    )
    error_message = "Please select a number of stars 1 through 5."
  }
}


Enter fullscreen mode Exit fullscreen mode

Going Further with Functions

We can expand the validation block use with Terraform functions.

In the example below, the anytrue function evaluates the input against each statement. If any of the three statements within evaluate to true, Terraform will proceed.



variable "primary_color" {
  type        = string
  description = "The primary color of your choice!"
  validation {
    condition = anytrue([
      lower(var.primary_color) == "blue",
      lower(var.primary_color) == "red",
      lower(var.primary_color) == "yellow"
    ])
    error_message = "The var.primary_color input is incorrect. Please select blue, red, or yellow."
  }
}


Enter fullscreen mode Exit fullscreen mode

Did you notice the lower function was used here as well? We don't care if the user inputs "BLUE" or "blue" do we? They're both valid primary colors.

Adding For Expressions

For Expressions can be used alongside functions to build more thorough validation rules.

  1. Anytrue evaluate every string as it loops each of the ingested list's values.

  2. The result of each loop is compiled into a new list of bools.

  3. Finally, the list of bools is evaluated against the alltrue function. If any value in the list is false, the condition will fail and trigger error_message.



variable "excluded_platforms" {
  type        = list(string)
  description = "The policy will enforce if the sign-in comes from the listed device platform(s)."
  default     = ["none"]
  validation {
    condition = alltrue([
      for i in var.excluded_platforms : anytrue([
        i == "none",
        i == "all",
        i == "android",
        i == "iOS",
        i == "linux",
        i == "macOS",
        i == "windows",
        i == "windowsPhone",
        i == "unknownFutureValue"
      ])
    ])
    error_message = "Invalid input for included_platforms. The list may only contain the following value(s): none, all, android, iOS, linux, macOS, windows, windowsPhone or unknownFutureValue."
  }
}


Enter fullscreen mode Exit fullscreen mode

Wrapping Up

Validation blocks and structured types are both excellent tools for input validation and consistency.

Mix and match both techniques for the best outcome.

Ralph Wiggum - Goodbye

💖 💪 🙅 🚩
coleheard
Cole Heard

Posted on March 10, 2024

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related