In Terraform, the can
function is used to determine whether a given expression or operation can be evaluated without causing an error. It returns true
if the expression can be evaluated without errors, and false
otherwise. This can be particularly useful for handling optional or dynamic attributes that may not always be present.
In below example I am doing variable validation of provided value. I want to make sure whenever user add value to variable it should be either Allow or Deny. If the provided value is different than Allow or Deny, it should give some meaningful error message mentioned in description, and user can change the values as per requirement accordingly. In the can it validates if given value is true for correct and false is for incorrect. Based on that it will either halt the processing showing error message or procced.
Below is the example of variable validation.
variable "rule_action" { type = string default = "Deny" validation { condition = can(regex("^(Allow|Deny)$", var.rule_action)) error_message = "Valid options are Allow and Deny." } description = "Specify the default rule_action of Deny, when no other rule match valid option are Deny or Allow" } output "rule_action" { value = var.rule_action }
As Terraform provide tons of function. Variable validation can be done using single function, combination of multiple functions or with no function, Below are the some of the examples I added for how validation can be done with functions.
var.infrastructure.network.vnet_name != "" #"The 'vnet_name' must not be empty." length(var.infrastructure.vm) > 0 #"At least one VM must be specified." length(var.username) > 5 && length(var.username) < 20 #"The username must be between 6 and 20 characters long." var.username != "" && regex("^[a-zA-Z0-9_]+$", var.username) #"The username must contain only alphanumeric characters and underscores." can(var.config["environment"]) && (var.config["environment"] == "dev" || var.config["environment"] == "prod") #"The 'environment' key must be set to either 'dev' or 'prod'." can(var.config["region"]) && regex("^[a-z]{2}-[a-z]+-[0-9]$", var.config["region"]) #"The 'region' key must be set to a valid region format (e.g., 'us-west-1')." alltrue([for vm in var.infrastructure.vm : length(vm.name) > 0]) #"Each VM spec must have a non-empty name." alltrue([for vm in var.vm_specs : vm.cpu > 0 && vm.cpu <= 64]) #"Each VM spec must have a CPU count between 1 and 64." alltrue([for vm in var.infrastructure.vm : vm.size == "Standard_B1s" || vm.size == "Standard_B2s"]) try(length(var.optional_value) > 0, true) #"The optional_value cannot be an empty string." length(distinct(var.unique_names)) == length(var.unique_names) #"All names in the list must be unique." alltrue([for env in var.environments : contains(var.allowed_values, env)]) #"All environments must be one of the following: ${join(", ", var.allowed_values)}"
Below is the output after running plan of variable validation. When I provide correct value it proceed well. But for incorrect value it shows proper error message what is missing.
PS D:\Projects\Terraform\\Variable_Validation> terraform plan Changes to Outputs: + rule_action = "Deny" You can apply this plan to save these new output values to the Terraform state, without changing any real infrastructure. ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── Note: You didn't use the -out option to save this plan, so Terraform can't guarantee to take exactly these actions if you run "terraform apply" now. PS D:\Projects\Terraform\\Variable_Validation> PS D:\Projects\Terraform\\Variable_Validation> PS D:\Projects\Terraform\\Variable_Validation> terraform plan Planning failed. Terraform encountered an error while generating this plan. ╷ │ Error: Invalid value for variable │ │ on Terraform_Variable_Validation_Example.tf line 1: │ 1: variable "rule_action" { │ ├──────────────── │ │ var.rule_action is "Deny1" │ │ Valid options are Allow and Deny. │ │ This was checked by the validation rule at Terraform_Variable_Validation_Example.tf:5,5-15.
Useful Articles
Terraform create Azure Virtual Machines from map of objects
Terraform refactoring moved block example
Terraform create Azure Virtual Machines from map of objects
Hashicorp Terraform map and object inside module and variable example
Terraform one module deploy null or multiple resources based on input
Terraform A reference to a resource type must be followed by at least one attribute access, specifying the resource name
Terraform fore_each for loop filter with if condition example
Terraform remote-exec provisioner with ssh connection in null_resource
Terraform count vs for_each for examples with map of objects
Terraform one module deploy null or multiple resources based on input (nested for loop) Example of Terraform functions flatten() and coalesce()
Terraform Azure Create Private Endpoint to existing Storage Account with Custom Private DNS zone record link
Creating a Private Endpoint for Azure Storage Account with required sub services using Terraform Example Terraform functions of lookup() and lower()
Using element function with count meta argument example Terraform Azure subnets Example Terraform functions of element(), count() and sum()
Terraform create Azure Virtual Network subnets from map of object and show name in the header Header name change in the output
Creating a Private Endpoint for Azure Storage Account with Terraform example 2 Example of for_each with toset() function
Creating a Private Endpoint for Azure Storage Account with Terraform example 3