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
(ortuple
): a sequence of values, like["one", "two"]
. Elements in a list or tuple are identified by consecutive whole numbers, starting with zero. -
map
(orobject
): 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
}
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"
}
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
}))
}
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" }
}
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" }
}
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
}
]
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 | 🐧 X/Twitter | 👾 LinkedIn
Top comments (6)
Amazing, man! You just saved me! Thank you so much!
@pwd9000, I just got here again, and your article saved me once more. Thanks!
Nice article :)
@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 ;)
Excellent Article!
Great one !