Search code examples
terraformterraform-provider-vsphere

Terraform using dynamic variable values based on user input


terraform gurus,

Please advice how to use different pre-configured variables (preferably maps) based on user-provided variables.

For example, user executes terraform apply -var="dc=dc1" and terraform uses vsphere variables (server, credentials, vm names, etc) specified for dc1.

Currently I tried this code:

# Variable for dc provided by user
variable "vsphere" {
  type        = string
  description = "VSphere DC: dc1 or dc2"

  validation {
    condition     = var.dc == "dc1" || var.dc == "dc2"
    error_message = "The dc value must be dc1 or dc2"
  }
}

# Provide dc details based on user input
variable "vsphere_data" {
  type = map(string)

  default = {
    user       = "terraform"
    password   = ""
    server     = var.vsphere_dc == "dc1" ? "vcenter" : "vcenter2"
    host_ip    = var.vsphere_dc == "dc1" ? "192.168.1.1" : "192.168.2.1"
    datacenter = var.vsphere_dc == "dc1" ? "DC1" : "DC2"
    datastore  = var.vsphere_dc == "dc1" ? "dc1-ssd" : "dc2-ssd"
    dvs        = var.vsphere_dc == "dc1" ? "dc1-dvs" : "dc2-dvs"
  }
}

# Provider
provider "vsphere" {
  user                 = var.vsphere_data.user
  password             = var.vsphere_data.password
  vsphere_server       = var.vsphere_data.server
}

But when running terraform validate I experience this error:

│ Error: Variables not allowed
│
│   on main.tf line 22, in variable "vsphere_data":
│   22:     datacenter = var.vsphere_dc == "dc1" ? "DC1" : "DC2"
│
│ Variables may not be used here.

Please advice how to better implement such case and why do I experience this error? Is locals or removing ternary operation will be a solution to the scenario above?


Solution

  • Use locals instead of variables.

    # Variable for dc provided by user
    variable "vsphere" {
      type        = string
      description = "VSphere DC: dc1 or dc2"
    
      validation {
        condition     = var.vsphere == "dc1" || var.vsphere == "dc2"
        error_message = "The dc value must be dc1 or dc2."
      }
    }
    
    # Provide dc details based on user input
    locals {
      vsphere_data = {
        user       = "terraform"
        password   = ""
        server     = var.vsphere == "dc1" ? "vcenter" : "vcenter2"
        host_ip    = var.vsphere == "dc1" ? "192.168.1.1" : "192.168.2.1"
        datacenter = var.vsphere == "dc1" ? "DC1" : "DC2"
        datastore  = var.vsphere == "dc1" ? "dc1-ssd" : "dc2-ssd"
        dvs        = var.vsphere == "dc1" ? "dc1-dvs" : "dc2-dvs"
      }
    }
    
    provider "vsphere" {
      user                 = local.vsphere_data.user
      password             = local.vsphere_data.password
      vsphere_server       = local.vsphere_data.server
    }
    

    You can run a plan for this code with:

    terraform plan -var="vsphere=dc1"
    

    There are a few additional problems with your initial code:

    variable "vsphere" {
      type        = string
      description = "VSphere DC: dc1 or dc2"  
    
      validation {
        condition     = var.dc == "dc1" || var.dc == "dc2" # # THIS IS WRONG!!!
        error_message = "The dc value must be dc1 or dc2"
      }
    }
    

    For validation of a variable, you can not reference another variable. The validation should reference only itself. Correctly would be:

    variable "vsphere" {
      type        = string
      description = "VSphere DC: dc1 or dc2"
    
      validation {
        condition     = var.vsphere == "dc1" || var.vsphere == "dc2"
        error_message = "The dc value must be dc1 or dc2."
      }
    }
    

    Please note, you don't need the var.dc variable.

    Moreover, you don't have this variable var.vsphere_dc. I assume this is a typo or something, otherwise it makes no sense. The is the local value we compute.