Terraform: Variable validation with samples
Tags: terraform, linting
Published: 2022-03-04 18:57:38

---
title: Terraform: Variable validation with samples
published: true
description: How to use Terraform validation blocks and some common uses
tags: #terraform #linting
//cover_image: https://direct_url_to_image.jpg
---

Terraform allows you to validate variable input in using [validation blocks](https://www.terraform.io/language/values/variables#custom-validation-rules) using custom `condition` and yielding a custom `error_message`. Below are some examples:

_**Note:** Please share your common validation rules you've written and I'll update here_

**Update**: Please check out this awesome project by @bschaatsbergen that provides built in functions for assertions: https://github.com/bschaatsbergen/terraform-provider-assert

## Strings

### String may not contain character

Scenario: String may not contain a `/`.

```hcl
variable "string_may_not_contain" {
  type = string
  default = "test"

  validation {
    error_message = "Value cannot contain a \"/\"."
    condition = !can(regex("/", var.string_may_not_contain))
  }
}
```

### String with valid options

Scenario: Here we have a string and we only allow to values "approved" or "disapproved". I show 2 examples of the same check using different methods:

```hcl
variable "string_only_valid_options" {
  type = string
  default = "approved"

  # using regex
  validation {
    condition     = can(regex("^(approved|disapproved)$", var.string_only_valid_options))
    error_message = "Invalid input, options: \"approved\", \"disapproved\"."
  }

  # using contains()
  validation {
    condition     = contains(["approved", "disapproved"], var.string_only_valid_options)
    error_message = "Invalid input, options: \"approved\", \"disapproved\"."
  }
}
```

### Valid AWS Region Name

Scenario: string must be like AWS region

```hcl
variable "string_like_aws_region" {
  type = string
  default = "us-east-1"

  validation {
    condition     = can(regex("[a-z][a-z]-[a-z]+-[1-9]", var.string_like_aws_region))
    error_message = "Must be valid AWS Region names."
  }
```

### Valid IAM Role Name

Scenario: Your string must be a valid IAM role name

```hcl
variable "string_valid_iam_role_name" {
    type = string
    default = "MyCoolRole"
    # arn example: "arn:aws:iam::123456789012:role/MyCoolRole"

    validation {
      condition     = can(regex("^[a-zA-Z][a-zA-Z\\-\\_0-9]{1,64}$", var.string_valid_iam_role_name))
      error_message = "IAM role name must start with letter, only contain letters, numbers, dashes, or underscores and must be between 1 and 64 characters."
    }
}
```

### Valid IPv4 CIDR

Scenario: Your string input needs to look like a IPv4 CIDR. Thank you @entscheidungsproblem for [reporting](https://dev.to/entscheidungsproblem/comment/21moa) and providing a fix for `/32`.
```hcl
variable "string_like_valid_ipv4_cidr" {
  type    = string
  default = "10.0.0.0/16"

  validation {
    condition     = can(cidrhost(var.string_like_valid_ipv4_cidr, 0))
    error_message = "Must be valid IPv4 CIDR."
  }
}
```

### Semantic Version

```hcl
variable "semv1" {
  default = "10.57.123"

  validation {
    error_message = "Must be valid semantic version."
    condition     = can(regex("^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$", var.semv1))
  }
}
```

## Maps

### Map with optional conflicting keys

Scenario: You have a map variable and 2 keys conflict, in this case, you can only set either `cidr` or `netmask`.

```hcl
variable "only_one_optional_key" {
    type = object({
        name = optional(string)
        cidrs = optional(list(string))
        netmask = optional(number)
    })

    default = {
        cidr = "10.0.0.0/16"
        name = "test"
    }

    validation {
        error_message = "Can only specify either \"cidrs\", or \"netmask\"."
        condition = length(setintersection(keys(var.only_one_optional_key), ["cidrs", "netmask"])) == 1
    }
}
```

### Numbers

### Number within a range

Scenario: number must be between 1-16. 
Thanks: @tlindsay42

```hcl
variable "num_in_range" {
  type        = number
  default     = 1

  validation {
    condition     = var.num_in_range >= 1 && var.num_in_range <= 16 && floor(var.num_in_range) == var.num_in_range
    error_message = "Accepted values: 1-16."
  }
}
```

If you liked this post, please like. If you think it would be helpful in the future as a reference, please bookmark!