Terraform 13 - for_each - Test Drive

3 minute read

"Buy Me A Coffee"


🚀 Terraform 0.13: The for_each Feature

Terraform 0.13 is the latest trend in the Terraform IaC world, and one of its most anticipated features is the new for_each capability.

I have all my Azure infrastructure currently deployed using Terraform 12. Now that version 13 is available (which, judging by the number, might seem unlucky—but 2020 has already set the bar for “unlucky”!), it’s time to explore the new features. 😄


Why Is for_each Great for Modules?

The for_each feature allows you to define multiple instances in a single block by using a map, leveraging modules to reduce the amount of code significantly.

I decided to start modifying my infrastructure to take advantage of these new features, beginning with for_each. You can refer to the Terraform 13 Changelog for more information about these changes.

Previously, I had a resource group module that I called n times—once for each resource group I needed (in my case, nine). It looked like this:

module "rg-dmz" {
  source             = "../modules/resource_group"
  name_suffix        = "dmz"
  location           = var.location
  full_env_code      = local.full_env_code
  create             = true
  enable_delete_lock = false
}

module "rg-app" {
  source             = "../modules/resource_group"
  name_suffix        = "app"
  location           = var.location
  full_env_code      = local.full_env_code
  create             = true
  enable_delete_lock = false
}
# ... repeated n times for each RG

Modifications Required

To make my code more efficient and dynamic, I updated my deployment to leverage for_each. This allows me to define multiple instances in one block using a map, reducing code duplication. More information about using for_each.

module "rgs" {
  for_each = {
    rg-dmz        = "dmz"
    rg-app        = "app"
    rg-services   = "services"
    rg-management = "management"
    rg-data       = "data"
    rg-network    = "network"
    rg-packer     = "packer"
  }

  source             = "../modules/resource_group"
  name_suffix        = each.value
  location           = var.location
  full_env_code      = local.full_env_code
  create             = true
  enable_delete_lock = false
}

State Resource Reorganization

After making these changes and running a plan, I encountered a new challenge: the new resources did not match the information in my state file. 🏃‍♂️ Some inspection was required, and I started doing the needful (thanks, BT, for that phrase). However, I had issues moving the state and received the following error:

terraform state mv module.rg-packer.azurerm_resource_group.rg  module.rgs["rg-packer"].azurerm_resource_group.rg                                     

Error: Index value required

  on  line 1:
  (source code not available)

Index brackets must contain either a literal number or a literal string.

Releasing state lock. This may take a few moments...

After some investigation (a.k.a. Googling), I found in the Terraform documentation how to terraform state mv using for_each. Depending on your terminal, you may need different syntax for the state movement. In my case, I was using PowerShell:

terraform state mv 'module.rg-packer.azurerm_resource_group.rg[0]' 'module.rgs_mel[\"rg-packer\"].azurerm_resource_group.rg[0]'

And voilà! 💃 State movement successful. I repeated this for the rest of my resources.

State Movement Success


Calling the Module Outputs

After completing my state migration and running a plan to ensure everything was correct, I needed to update all statements where I consumed module outputs. For example:

resource "azurerm_app_service_plan" "various" {
  name                = "azure-functions-cero-service-plan"
  location            = var.location
  resource_group_name = module.rgs["rg-management"].name # using the for_each module output
  kind                = "FunctionApp"
  reserved            = true

  sku {
    tier = "Dynamic"
    size = "Y1"
  }
}

The “No Changes Expected” Outcome 👼

After all these changes, the expected outcome was to have no planned changes, as we were only redesigning the code. My Terraform Cloud workspace applied the changes successfully, and the result was 🧙‍♂️

Terraform Cloud Apply


Summary

Terraform v13 offers excellent new features that many long-time users have been waiting for. Migrating existing infrastructure to v13 is not always straightforward, but it ultimately makes code management easier. I will continue updating my code to leverage all v13 features and share my progress. I hope this helps someone—Hasta la vista!

🚴‍♂️ If you enjoyed this blog, you can empower me with some caffeine to continue working on new content. 🚴‍♂️

"Buy Me A Coffee"

Comments