Terraform - Selective configuration with 'lookup()'

pwd9000

Marcel.L

Posted on April 14, 2022

Terraform - Selective configuration with 'lookup()'

Overview

This tutorial uses examples from the following GitHub project: Azure Terraform Deployments.

In todays tutorial we will take a look at an interesting Terraform function called lookup().

The lookup() function can be used to lookup a particular value inside of a map, given its key and if the given key does not exist, the given default value is returned instead:



lookup(map, key, default)


Enter fullscreen mode Exit fullscreen mode

Example



$ lookup({a="hello", b="world"}, "a", "what?")
"hello"

$ lookup({a="hello", b="world"}, "b", "what?")
"world"

$ lookup({a="hello", b="world"}, "c", "what?")
"what?"


Enter fullscreen mode Exit fullscreen mode

So how can this be useful in Infrastructure as Code (IaC)?

It allows us to be more creative and granular with Terraform configurations by allowing us to create multiple configurations for different scenarios and be able to select what scenario or configuration we want to deploy. Let's take a look at a real world example of this.

Real world example

The example code used in the following section can also be found here: 05_lookup_demo.

Say for example we have to create Azure cloud resources for multiple sites of our organization. In the following example we will use Site A in UK South and Site B in UK West as two separate sites for our Org.

We start off by creating a list of sites in a variable for siteA and siteB:



## variables.tf ##

variable "site_names" {
  type        = list(string)
  default     = ["siteA", "siteB"]
  description = "Provide a list of all Contoso site names - Will be mapped to local var 'site_configs'"
}


Enter fullscreen mode Exit fullscreen mode

Next we create a locals variable called site_configs, a map configuration containing child maps for each of the sites we want to set certain criteria against:



## local.tf ##

locals {
  site_configs = {
    siteA = {
      resource_group_name = "Demo-Inf-SiteA-RG"
      location            = "UKSouth"
      allowed_ips         = ["8.8.8.8", "8.8.8.9"]
    },
    siteB = {
      resource_group_name = "Demo-Inf-SiteB-RG"
      location            = "UKWest"
      allowed_ips         = ["7.7.7.7", "7.7.7.8"]
    }
  }
}


Enter fullscreen mode Exit fullscreen mode

So for our first set of resources we will deploy azure resource groups for each of our sites:



## storage_resources.tf ##

resource "azurerm_resource_group" "RGS" {
  for_each = toset(var.site_names)
  name     = lookup(local.site_configs[each.value], "resource_group_name", null)
  location = lookup(local.site_configs[each.value], "location", null)
}


Enter fullscreen mode Exit fullscreen mode

Notice that we are using a for_each loop using the list we created earlier with our site names, siteA and siteB. The lookup() function is then used to lookup the corresponding key for each site config inside of our site_configs locals variable map, that corresponds to siteA and siteB.

As you can see each Azure resource group was created for each site in the locations we defined in our local variable for UK South and UK West:

image.png

Next we will create a few storage accounts for each of our sites. We have a variable called storage_config which is a list of objects where each object represents a storage account configuration. But notice that one of the keys of each storage config/object has a key called site_name.



## config-dev.tfvars ##

storage_config = [
  #V2 Storage - SiteA
  {
    name                      = "pwd9000v2sitea"
    account_kind              = "StorageV2"
    account_tier              = "Standard"
    account_replication_type  = "LRS"
    enable_https_traffic_only = true
    access_tier               = "Hot"
    is_hns_enabled            = false
    site_name                 = "siteA"
  },
  #ADLS2 Storage - SiteA
  {
    name                      = "pwd9000dfssitea"
    account_kind              = "BlockBlobStorage"
    account_tier              = "Premium"
    account_replication_type  = "ZRS"
    enable_https_traffic_only = true
    access_tier               = "Hot"
    is_hns_enabled            = true
    site_name                 = "siteA"
  },
  #V2 Storage - SiteB
  {
    name                      = "pwd9000v2siteb"
    account_kind              = "StorageV2"
    account_tier              = "Standard"
    account_replication_type  = "LRS"
    enable_https_traffic_only = false
    access_tier               = "Hot"
    is_hns_enabled            = false
    site_name                 = "siteB"
  }
]


Enter fullscreen mode Exit fullscreen mode

This site_name corresponds with the local variable maps key of each of the site_configs maps:



## local.tf ##

locals {
  site_configs = {
    siteA = {
      resource_group_name = "Demo-Inf-SiteA-RG"
      location            = "UKSouth"
      allowed_ips         = ["8.8.8.8", "8.8.8.9"]
    },
    siteB = {
      resource_group_name = "Demo-Inf-SiteB-RG"
      location            = "UKWest"
      allowed_ips         = ["7.7.7.7", "7.7.7.8"]
    }
  }
}


Enter fullscreen mode Exit fullscreen mode

Notice that when we are building out the storage accounts for each of the sites we can now lookup the network_rules to apply to each of our storage accounts that corresponds to the allowed IPs for that site using the lookup() function ip_rules = lookup(local.site_configs[each.value.site_name], "allowed_ips", null) as shown below:



resource "azurerm_storage_account" "SAS" {
  for_each = { for n in var.storage_config : n.name => n }

  #Implicit dependency from previous resource
  resource_group_name = azurerm_resource_group.RGS[each.value.site_name].name
  location            = azurerm_resource_group.RGS[each.value.site_name].location

  #values from variable storage_config objects
  name                      = "${lower(each.value.name)}${random_integer.sa_num.result}"
  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
  is_hns_enabled            = each.value.is_hns_enabled

  #Lookup allowed ips
  network_rules {
    default_action = "Deny"
    ip_rules       = lookup(local.site_configs[each.value.site_name], "allowed_ips", null)
  }
}

resource "random_integer" "sa_num" {
  min = 0001
  max = 9999
}


Enter fullscreen mode Exit fullscreen mode

As you can see Site A storage accounts are set with allowed IPs of allowed_ips = ["8.8.8.8", "8.8.8.9"].

image.png image.png

And Site B storage accounts are set with allowed IPs of allowed_ips = ["7.7.7.7", "7.7.7.8"]

image.png image.png

As you can see the Terraform lookup() function can be quite useful in cases where we have multiple sites or different configs and having the ability match and correlate different configurations for different scenarios.

I hope you have enjoyed this post and have learned something new. ❤️ Code samples used in this tutorial can also be found here: 05_lookup_demo.

Author

Like, share, follow me on: 🐙 GitHub | 🐧 X/Twitter | 👾 LinkedIn

💖 💪 🙅 🚩
pwd9000
Marcel.L

Posted on April 14, 2022

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related