DEV Community πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’»

Cover image for Terraform - Complex Variable Types
Marcel.L
Marcel.L

Posted on • Updated on

Terraform - Complex Variable Types

Terraform Variables

When creating a terraform configuration, you have to configure and declare Input Variables. Input variables serve as parameters for a Terraform module and resources, allowing aspects of the module to be customized without altering the module's own source code, and allowing modules to be shared between different configurations.

The Terraform language uses the following types for its values:

  • string: a sequence of Unicode characters representing some text, like "hello".
  • number: a numeric value. The number type can represent both whole numbers like 15 and fractional values like 6.283185.
  • bool: a boolean value, either true or false. bool values can be used in conditional logic.
  • list (or tuple): a sequence of values, like ["one", "two"]. Elements in a list or tuple are identified by consecutive whole numbers, starting with zero.
  • map (or object): a group of values identified by named labels, like {name = "Mabel", age = 52}.

Strings, numbers, and bools are sometimes called primitive types. Lists/tuples and maps/objects are sometimes called complex types, structural types, or collection types.

Using Primitive Variable Types

In the following example we create a basic Azure Resource Group and we declare each resource argument with it's own separate variable using Primitive types:

#main.tf
resource "azurerm_resource_group" "demo_rg" {
  count    = var.create_rg ? 1 : 0
  name     = var.name
  location = var.location
}
Enter fullscreen mode Exit fullscreen mode

Each variable is declared separately:

#variables.tf
variable "create_rg" {
    type = bool
    default = false
}

variable "name" {
    type = string
    default = "Default-RG-Name"
}

variable "location" {
    type = string
    default = "uksouth"
}
Enter fullscreen mode Exit fullscreen mode

As you can see from the above example each resource argument is declared using a primitive variable type.

Using Complex Variable Types

In the following example we create an Azure Resource Group and two storage accounts, but instead of declaring each variable individually using primitive types we will use Collections using complex types. We will create our Resource Group by using a single complex variable called rg_config and we will create our storage account/s using a single complex variable list of objects called storage_config.

As you can see from the following variable declaration, we are only declaring each resources values using a complex variable type of Object (Resource Group config) and List Object (List of Storage Account configs):

#// code/variables.tf#L1-L20
#Resource Group Config - Object
variable "rg_config" {
  type = object({
    create_rg = bool
    name      = string
    location  = string
  })
}

#Storage Account Config - List of Objects (Each object represents a storage config)
variable "storage_config" {
  type = list(object({
    name                      = string
    account_kind              = string
    account_tier              = string
    account_replication_type  = string
    access_tier               = string
    enable_https_traffic_only = bool
    min_tls_version           = string
    is_hns_enabled            = bool
  }))
}
Enter fullscreen mode Exit fullscreen mode

NOTE: Because we are using variable objects we can just reference and lookup each key of the relevant object passed in to obtain the corresponding configuration value e.g. var.config.key:

Example using COUNT

#// code/resources.tf#L6-L32
resource "azurerm_resource_group" "demo_rg" {
  count    = var.rg_config.create_rg ? 1 : 0
  name     = var.rg_config.name
  location = var.rg_config.location
  tags     = { Purpose = "Demo-RG", Automation = "true" }
}

## COUNT Example ##
resource "azurerm_storage_account" "sas" {
  count = length(var.storage_config)

  #Implicit dependency from previous resource
  resource_group_name = azurerm_resource_group.demo_rg[0].name
  location            = azurerm_resource_group.demo_rg[0].location

  #values from variable storage_config objects
  name                      = var.storage_config[count.index].name
  account_kind              = var.storage_config[count.index].account_kind
  account_tier              = var.storage_config[count.index].account_tier
  account_replication_type  = var.storage_config[count.index].account_replication_type
  access_tier               = var.storage_config[count.index].access_tier
  enable_https_traffic_only = var.storage_config[count.index].enable_https_traffic_only
  min_tls_version           = var.storage_config[count.index].min_tls_version
  is_hns_enabled            = var.storage_config[count.index].is_hns_enabled

  #Apply tags
  tags = { Purpose = "Demo-sa-${count.index + 1}", Automation = "true" }
}
Enter fullscreen mode Exit fullscreen mode

Example using FOR_EACH

resource "azurerm_resource_group" "demo_rg" {
  count    = var.rg_config.create_rg ? 1 : 0
  name     = var.rg_config.name
  location = var.rg_config.location
  tags     = { Purpose = "Demo-RG", Automation = "true" }
}

## FOR_EACH Example ##
resource "azurerm_storage_account" "sas" {
  for_each = { for each in var.storage_config : each.name => each )

  #Implicit dependency from previous resource
  resource_group_name = azurerm_resource_group.demo_rg[0].name
  location            = azurerm_resource_group.demo_rg[0].location

  #values from variable storage_config objects
  name                      = each.value.name
  account_kind              = each.value.account_kind
  account_tier              = each.value.account_tier
  account_replication_type  = each.value.account_replication_type
  access_tier               = each.value.access_tier
  enable_https_traffic_only = each.value.enable_https_traffic_only
  min_tls_version           = each.value.min_tls_version
  is_hns_enabled            = each.value.is_hns_enabled

  #Apply tags
  tags = { Purpose = "Demo-sa-${count.index + 1}", Automation = "true" }
}
Enter fullscreen mode Exit fullscreen mode

Because we are now using a list of objects as the variable for storage accounts, each storage account we want to create can be configured on our TFVARS file as an object inside its own block, and so we can simply add additional object blocks into our TFVARS to build one or many storage accounts, each with different configs:

#// code/common.auto.tfvars.tf#L1-L30
#Resource Group Config - Object Values
rg_config = {
  create_rg = true
  name      = "Demo-Terraform-RG"
  location  = "uksouth"
}

#Storage Account Configs - List of Objects Values
storage_config = [
  #Storage Account 1 (Object1): StorageV2
  {
    name                      = "pwd9000v2sa001"
    account_kind              = "StorageV2"
    account_tier              = "Standard"
    account_replication_type  = "LRS"
    min_tls_version           = "TLS1_2"
    enable_https_traffic_only = true
    access_tier               = "Cool"
    is_hns_enabled            = false
  },
  #Storage Account 2 (object2): Azure Data Lake Storage V2 (ADLS2)
  {
    name                      = "pwd9000adls2sa001"
    account_kind              = "BlockBlobStorage"
    account_tier              = "Premium"
    account_replication_type  = "ZRS"
    min_tls_version           = "TLS1_2"
    enable_https_traffic_only = false
    access_tier               = "Hot"
    is_hns_enabled            = true
  }
]
Enter fullscreen mode Exit fullscreen mode

As you can see from the last example, using complex variable types and making our configurations more object oriented can offer much greater flexibility and granularity in terraform deployments.

I hope you have enjoyed this post and have learned something new. You can also find the code samples used in this blog post on my GitHub page. ❀️

Author

Like, share, follow me on: πŸ™ GitHub | 🐧 Twitter | πŸ‘Ύ LinkedIn

Top comments (5)

Collapse
 
dversoza profile image
Daniel Versoza • Edited on

Amazing, man! You just saved me! Thank you so much!

Collapse
 
stefiix92 profile image
Michal Ε tefanec

Nice article :)

Collapse
 
hinodeya69 profile image
Hinodeya69

@pwd9000
Nice job.
But I have just some interrogation if you have multiple groups.
How can you manage this case because, the for loop in the storage resource only see the 8 attributs of the objects var.storage_config.

Thank mate for your feedback about this point ;)

Collapse
 
capdragon profile image
CaptDragon

Excellent Article!

Collapse
 
aliamin7 profile image
Ali

Great one !

CSS margins

Stop by this week's meme thread!