6

Is there an efficient way to apply validation logic to variables used in a terraform run? Specifically I want to check the length and casing of some variables. The variables are a combination of ones declared in tfvars files, in variables.tf files, and collected during runtime by terraform.

Thanks.

Bob
  • 1,484
  • 5
  • 22
  • 44
  • 2
    This open issue looks promising: https://github.com/hashicorp/terraform/issues/2847 but so far no work has been done on it – ThomasVdBerge Apr 28 '18 at 20:36

3 Answers3

3

Custom Validation Rules

Results

Failure case

provider aws {
     profile="default"
}
terraform {
  experiments = [variable_validation]
}

## Custom Validation Rules
variable "test" {
  type        = string
  description = "Example to test the case and length of the variable"
  default = "TEsT"

  validation {
    condition     = length(var.test) > 4 && upper(var.test) == var.test
    error_message = "Validation condition of the test variable did not meet."
  }
}

Execution

$ terraform plan

Warning: Experimental feature "variable_validation" is active

  on main.tf line 5, in terraform:
   5:   experiments = [variable_validation]

Experimental features are subject to breaking changes in future minor or patch
releases, based on feedback.

If you have feedback on the design of this feature, please open a GitHub issue
to discuss it.


Error: Invalid value for variable   # <---------------------------

  on main.tf line 9:
   9: variable "test" {

Validation condition of the test variable did not meet.

This was checked by the validation rule at main.tf:14,3-13.

Pass case

terraform {
  experiments = [variable_validation]
}

## Custom Validation Rules
variable "test" {
  type        = string
  description = "Example to test the case and length of the variable"
  default = "TESTED"

  validation {
    condition     = length(var.test) > 4 && upper(var.test) == var.test
    error_message = "Validation condition of the test variable did not meet."
  }
}

Execution

$ terraform plan
Refreshing Terraform state in-memory prior to plan...
The refreshed state will be used to calculate this plan, but will not be
persisted to local or remote state storage.


------------------------------------------------------------------------

No changes. Infrastructure is up-to-date.

Others

Alternatively, use null_resource local-exec to implement logic in shell script, or use external provider to send the variable to an external program to validate?

mon
  • 18,789
  • 22
  • 112
  • 205
1

This isn't something you can currently do directly with Terraform but I find it easier to just mangle the input variables to the required format if necessary.

As an example the aws_lb_target_group resource takes a protocol parameter that currently requires it to be uppercased instead of automatically upper casing things and suppressing the diff like the aws_lb_listener resource does for the protocol (or even the protocol in the health_check block).

To solve this I just use the upper function when creating the resource:

variable "protocol" {
  default = "http"
}

resource "aws_vpc" "main" {
  cidr_block = "10.0.0.0/16"
}

resource "aws_lb_target_group" "test" {
  name     = "tf-example-lb-tg"
  port     = 80
  protocol = "${upper(var.protocol)}"
  vpc_id   = "${aws_vpc.main.id}"
}

As for checking length I just substring things to make them the right length. I currently do this for ALBs as the name has a max length of 32 and I have Gitlab CI create review environments for some services that get a name based on the slug of the Git branch name so have little control over the length that is used.

variable "environment" {}
variable "service_name" {}

variable "internal" {
  default = true
}

resource "aws_lb" "load_balancer" {
  name            = "${substr(var.environment, 0, min(length(var.environment), 27 - length(var.service_name)))}-${var.service_name}-${var.internal ? "int" : "ext"}"
  internal        = "${var.internal}"
  security_groups = ["${aws_security_group.load_balancer.id}"]
  subnets         = ["${data.aws_subnet_ids.selected.ids}"]
}

With the above then any combination of length of environment or service name will lead to the environment/service name pair being trimmed to 27 characters at most which leaves room for the extra characters that I want to specify.

ydaetskcoR
  • 53,225
  • 8
  • 158
  • 177
  • 1
    Thanks, good stuff. Could certainly combine some of those methods into a count in a little custom resource...raise an error if the condition fails (i.e. count >0). I'm betting that a future terraform release provides a more concise and well documented way to do this. – Bob May 05 '18 at 11:10
0

Got inspired by this conversation and found the following already existing provider: https://github.com/craigmonson/terraform-provider-validate

Konstantin Grigorov
  • 1,356
  • 12
  • 20