Last active
December 24, 2023 12:16
-
-
Save AlexAtkinson/11f2a22511b09b0c241d59e9ebfe66ec to your computer and use it in GitHub Desktop.
Terraform Variables - Ensure default values are changed, plus some other validations
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# A collection of variable validation examples for Terraform. | |
# | |
# Test with: | |
# tf plan -var="environment=Development" -var="aws_region=us-east-2" -var="rds_password=zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzAZaz98><" -var="subnet_public_a=10.222.16.0/20" -var="subnet_database_a=10.10.10.0/28" -var="application=testing" -var="orchestration=linktourl" -var="department=department_foo" -var="company=coolcorp" | |
variable "company" { | |
description = "The name of the company." | |
type = string | |
nullable = false | |
default = "CHANGEME" | |
validation { | |
condition = var.company != "CHANGEME" && lower(var.company) == var.company && !can(regex(" ", var.company)) | |
error_message = "Must be lowercase and not contain spaces." | |
} | |
} | |
variable "environment" { | |
description = "The execution environment: [DevOps|Development|QA|Staging|Production]" | |
type = string | |
default = "CHANGEME" | |
nullable = false | |
validation { | |
condition = var.environment != "CHANGEME" && contains(["DevOps", "Development", "QA", "Staging", "Production"], var.environment) && !can(regex(" ", var.environment)) | |
error_message = "The variable 'environment' must match the long form references as defined here: <link to docks>" | |
} | |
} | |
variable "department" { | |
description = "The department responsible for the resource. (lowercase)" | |
type = string | |
nullable = false | |
default = "CHANGEME" | |
validation { | |
condition = var.department != "CHANGEME" && lower(var.department) == var.department && !can(regex(" ", var.department)) | |
error_message = "Must be lowercase and not contain spaces." | |
} | |
} | |
variable "application" { | |
description = "The application this resouce is is supporting. (lowercase)" | |
type = string | |
nullable = false | |
default = "CHANGEME" | |
validation { | |
condition = var.application != "CHANGEME" && lower(var.application) == var.application && !can(regex(" ", var.application)) | |
error_message = "Must be lowercase and not contain spaces." | |
} | |
} | |
variable "orchestration" { | |
description = "The repository for this infrastructure (IaC)" | |
type = string | |
nullable = false | |
default = "CHANGEME" | |
validation { | |
condition = var.orchestration != "CHANGEME" && lower(var.orchestration) == var.orchestration && !can(regex(" ", var.orchestration)) | |
error_message = "Must be lowercase and not contain spaces." | |
} | |
} | |
variable "aws_region" { | |
description = "The region in which to execute." | |
type = string | |
nullable = false | |
default = "CHANGEME" | |
validation { | |
condition = ( | |
var.aws_region != "CHANGEME" && | |
contains(["us-west-1", "us-east-2", "eu-west-1", "eu-west-3"], var.aws_region) && | |
!can(regex(" ", var.aws_region)) | |
) | |
error_message = "The variable 'aws_region' must be one of the operating regions as defined here: <link to docs>" | |
} | |
} | |
# Between two numbers | |
variable "alb_tg_traffic_port" { | |
description = <<EOT | |
The target group protocol. | |
EOT | |
type = number | |
nullable = false | |
default = 443 | |
validation { | |
condition = ( | |
var.alb_tg_traffic_port >= 0 && | |
var.alb_tg_traffic_port <= 65535 | |
) | |
error_message = "Must be a valid port number." | |
} | |
} | |
# Matches one of a specified set of numbers. | |
variable "ecs_task_cpu_allocation" { | |
description = <<EOT | |
The cpu units for the task. | |
EOT | |
type = number | |
nullable = false | |
default = 256 | |
validation { | |
condition = ( | |
var.ecs_task_cpu_allocation == 256 || | |
var.ecs_task_cpu_allocation == 512 || | |
var.ecs_task_cpu_allocation == 1024 || | |
var.ecs_task_cpu_allocation == 2048 || | |
var.ecs_task_cpu_allocation == 4096 || | |
var.ecs_task_cpu_allocation == 8192 || | |
var.ecs_task_cpu_allocation == 16384 | |
) | |
error_message = "Must be a valid task size! See: https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_definition_parameters.html#task_size" | |
} | |
} | |
# Use modulo operator to see if number is divisable by to simplify some validations... | |
# This is valid for ECS memory allocation | |
variable "n" { | |
description = <<EOT | |
The cpu units for the task. | |
EOT | |
type = number | |
nullable = false | |
validation { | |
condition = ( | |
var.n == 512 || | |
var.n >= 1024 && try(var.n % 1024 == 0) && var.n <= 30720 | |
) | |
error_message = "Must be a valid task size!" | |
} | |
} | |
# Validate HTTP Response Codes | |
variable "alb_tg_healthcheck_matcher" { | |
description = <<EOT | |
The target group healthcheck valid response code(s). | |
May be a comma separated list. Ranges are valid. Eg: 200,201,400-499. | |
REFs: | |
- https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lb_target_group#health_check | |
- https://httpwg.org/specs/rfc9110.html#overview.of.status.codes | |
EOT | |
type = string | |
nullable = false | |
default = "200" | |
validation { | |
condition = ( | |
!can(regex("[[:space:]]", var.alb_tg_healthcheck_matcher)) && | |
!can(regex("[[:alpha:]]+", var.alb_tg_healthcheck_matcher)) | |
) | |
error_message = "Variable may only contain numbers and commas." | |
} | |
validation { | |
condition = ( | |
alltrue([ | |
for i in regexall("[[:digit:]]+", var.alb_tg_healthcheck_matcher) : | |
tonumber(i) >= 100 && | |
tonumber(i) <= 599 | |
]) | |
) | |
error_message = "HTTP Response codes must be between 100 and 599. See: https://httpwg.org/specs/rfc9110.html#overview.of.status.codes" | |
} | |
validation { | |
condition = ( | |
length(split(",", var.alb_tg_healthcheck_matcher)) == length(distinct(split(",", var.alb_tg_healthcheck_matcher))) | |
) | |
error_message = "Duplicate response codes detected. Entries must be distcint!" | |
} | |
} | |
# String as boolien validatidation. | |
# Useful for satisfying templating scenarios. | |
variable "bool" { | |
description = "Enter a bool." | |
type = string | |
nullable = false | |
default = "CHANGEME" | |
validation { | |
condition = ( | |
var.bool != "CHANGEME" && | |
var.bool == lower(var.bool) && | |
contains(["true", "false"], var.bool) && | |
!can(regex(" ", var.bool)) | |
) | |
error_message = "The variable 'bool' must be a valid bool when converted from a string." | |
} | |
} | |
# This is an outmoded approach. TF has solid bool validation built in now. | |
variable "real_bool" { | |
type = bool | |
description = "DANGER!!! SET TO FALSE ONLY." | |
default = false | |
validation { | |
condition = ( | |
can(regex("^([t][r][u][e]|[f][a][l][s][e])$",var.real_bool)) && | |
!can(regex(" ", var.real_bool)) | |
) | |
error_message = "Bool must be true or false." | |
} | |
} | |
# Compound Key Map lookup based on two or more variables/values. | |
# NOTE: locals do not support such map lookup mechanisms. | |
# WARN: everything assembled like this will have GLOBAL scope. | |
variable "compound_lookup_map" { | |
default = { | |
Development_us-east-2 = "dev_us-east-2_value" | |
Development_us-west-1 = "dev_us-west-1_value" | |
Production_us-east-2 = "prod_us-east-2_value" | |
Production_us-west-1 = "prod_us-west-1_value" | |
} | |
} | |
output compound_lookup_result { | |
value = var.compound_lookup_map["${var.environment}_${var.aws_region}"] | |
} | |
# NOTE: Terraform Regex DOES NOT support lookarounds, lookaheads, or quantifiers... AFAICT | |
variable "rds_password" { | |
description = "The rds password." | |
type = string | |
nullable = false | |
default = "CHANGEME" | |
validation { | |
condition = ( | |
length(var.rds_password) >= 18 && | |
length(var.rds_password) <= 64 | |
) | |
error_message = "RDS password must be between 18 and 64 characters long." | |
} | |
validation { | |
condition = length(regexall("[a-z]", var.rds_password)) >= 2 | |
error_message = "RDS password must contain at least 2 lower case letters!" | |
} | |
validation { | |
condition = length(regexall("[A-Z]", var.rds_password)) >= 2 | |
error_message = "RDS password must contain at least 2 upper case letters!" | |
} | |
validation { | |
condition = length(regexall("[0-9]", var.rds_password)) >= 2 | |
error_message = "RDS password must contain at least 2 digits!" | |
} | |
validation { | |
condition = length(regexall("[!#$%^&*()<>;?]", var.rds_password)) >= 2 | |
error_message = "RDS password must contain at least 2 of the following special characters: !#$%^&*()<>;?" | |
} | |
validation { | |
condition = length(regexall("[\\/@'\" ]", var.rds_password)) == 0 | |
error_message = "RDS password must NOT contain spaces or any of the following special characters: /@'\"" | |
} | |
validation { | |
condition = var.rds_password != "CHANGEME" | |
error_message = "RDS password must not be \"CHANGEME\"!" | |
} | |
validation { | |
condition = !can(regex(" ", var.rds_password)) | |
error_message = "RDS password must not contain spaces!" | |
} | |
} | |
variable "subnet_public_a" { | |
description = "The CIDR for public subnet in AZ a" | |
type = string | |
nullable = false | |
validation { | |
condition = can(cidrhost(var.subnet_public_a, 4094)) | |
error_message = "Must be valid /20 IPv4 CIDR with a maximum of 4094 hosts (eg: 10.10.0.0/20)." | |
} | |
} | |
variable "subnet_database_a" { | |
description = "The CIDR for database subnet in AZ a" | |
type = string | |
nullable = false | |
validation { | |
condition = can(cidrhost(var.subnet_database_a, 14)) | |
error_message = "Must be valid /28 IPv4 CIDR with a maximum of 14 hosts (eg: 10.10.110.0/28)." | |
} | |
} | |
# Just to make this example runable | |
locals { | |
terraform-git-repo = "terraform-aws-infra" | |
} | |
provider "aws" { | |
region = var.aws_region | |
default_tags { | |
tags = { | |
Environment = var.environment | |
Application = var.application | |
Department = var.department | |
Orchestration = var.orchestration | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment