0

I'm trying to figure out how to use what I think might require a nested loop in Terraform.

I have a module that creates Azure storage accounts from a list of storage accounts.

I would like to extend this module to be able to include a list of storage containers inside the definition.

The variable is a map object for the storage_account that contains a sub map object for containers as shown below:

variables.tf

variable "location" {
  type        = string
  description = "Location for storage account resource"
}

variable "resource_group_name" {
  type        = string
  description = "Resource group the resource will fall under"
}

variable "storage_account" {
  type = map(object({
    name                          = string
    access_tier                   = string
    account_kind                  = string
    account_replication_type      = string
    account_tier                  = string
    containers = optional(map(object({
      name = string
      container_access_type = optional(string, "private")
    })))
    network_rules = list(object({
      default_action             = string
      bypass                     = optional(set(string))
      ip_rules                   = optional(set(string))
      virtual_network_subnet_ids = optional(set(string))
    }))
  }))
}

The module code looks like this:

main.tf

resource "azurerm_storage_account" "storage" {
  for_each = var.storage_account

  name                          = each.value.name
  resource_group_name           = var.resource_group_name
  location                      = var.location
  account_tier                  = each.value.account_tier
  account_replication_type      = each.value.account_replication_type
  account_kind                  = each.value.account_kind
  access_tier                   = each.value.access_tier
  public_network_access_enabled = true
  tags                          = var.tags

}

resource "azurerm_storage_container" "storage"{
  for_each = {
    for key, value in var.storage_account : key => value
  }

      name                 = each.value.name
      storage_account_name = azurerm_storage_account.storage[each.key].name
}

I call/use the module using something like this:

main.tf

module "storage_account" {
  source = "./modules/azurerm_storage_account"

  resource_group_name = "rg-temp"
  location            = "uksouth"

  storage_account = {
    "bigrandomthingy01" = {
      name                          = "bigrandomthingy01"
      account_kind                  = "StorageV2"
      account_tier                  = "Standard"
      account_replication_type      = "LRS"
      access_tier                   = "Hot"
      network_rules                 = []
      containers = {
        "container-01" = {
          name = "randomaa"
          container_access_type = "private"
        },
        "container-02" = {
          name = "randomab"
          container_access_type = "private"
        }
      }
    },
    "bigrandomthingy02" = {
      name                          = "bigrandomthingy02"
      account_kind                  = "StorageV2"
      account_tier                  = "Standard"
      account_replication_type      = "LRS"
      access_tier                   = "Hot"
      network_rules                 = []
      containers = {
        "container-01" = {
          name = "random01"
          container_access_type = "private"
        },
        "container-02" = {
          name = "random02"
          container_access_type = "private"
        }
      }
    }
  }
}

The code loops over each storage account and creates it no problem.

But it then also creates a single storage_container in the account that has the same name as the storage account.

I know my loop code is way off, but I'm not sure what I need to do to loop over the list of containers to create in each storage account?

Is this something that would need me to use flatten? Would this be an 'inner and outer' loop?

I've never really nested a loop before in TF, it's very confusing.

1

1 Answer 1

1

You can use the code below to create two storage accounts, each containing two containers with specified names.

Here is my directory structure

├── main.tf
├── modules
│   └── azurerm_storage_account
│       ├── main.tf
│       └── variables.tf

main.tf

provider "azurerm" {
  features {}
  subscription_id = "8332bf56-aa7c-xxxxxxx-d7e60e5f09a9"
}

resource "azurerm_resource_group" "example" {
  name     = "storage-rg"
  location = "East US"
}

module "storage_accounts" {
  source                = "./modules/azurerm_storage_account"
  resource_group_name   = azurerm_resource_group.example.name
  location              = azurerm_resource_group.example.location
  storage_accounts = {
    "venaktstoragedemo" = ["venkatcontainer1", "venkatcontainer2"],
    "thejastoragedemo"  = ["thejacontainer1", "thejacontainer2"]
  }
}

modules/azurerm_storage_account/main.tf

resource "azurerm_storage_account" "this" {
  for_each                = var.storage_accounts
  name                    = each.key
  resource_group_name     = var.resource_group_name
  location                = var.location
  account_tier           = "Standard"
  account_replication_type = "LRS"
}

locals {
  storage_containers = flatten([
    for storage_account, containers in var.storage_accounts : [
      for container in containers : {
        name                 = container
        storage_account_name = storage_account
      }
    ]
  ])
}

resource "azurerm_storage_container" "this" {
  for_each               = { for container in local.storage_containers : container.name => container }

  name                   = each.value.name
  storage_account_name   = each.value.storage_account_name
  container_access_type  = "private"
}

modules/storage_with_containers/variables.tf

variable "resource_group_name" {
  description = "The name of the resource group."
  type        = string
}

variable "location" {
  description = "The Azure location where the storage accounts will be created."
  type        = string
}

variable "storage_accounts" {
  description = "A map of storage accounts with their associated container names."
  type        = map(list(string))
}

terrafrom apply

enter image description here

After running the Terraform code, two storage accounts and two containers in each account were created successfully.

enter image description here

Sign up to request clarification or add additional context in comments.

4 Comments

Hi @venkat-v. Thank you for this, but it's not correct for how I am trying to achieve this. I am trying to make a single call to the module and pass a list of storage account with storage containers. Your solution is two module calls, one for each storage account. I know I will need to use a "for_each = var.storage_account....." to loop of the storage accounts, but I cannot figure out how to loop over the containers for each while I am inside the storage loop. Many thanks.
Hi @Scott, I have updated the solution to use a single module instead of two separate modules. Kindly check if it's helpful for your requirement.
Hi @Scott, I hope this helps to resolve your issue.
Hi @venkat-v. Again thank you for the update. And yes your code does work fine for the scenario you have defined. But I cannot change the structure/definition of my variable for storage_account, so I cannot use your solution for my situation. Are you saying that the way my variable has been defined will never work for what I am trying to do? Many thanks.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.